From fd44934b987fde083bf4d654eaccffeb1e46678d Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Thu, 17 Oct 2019 13:12:29 +0200 Subject: [PATCH] Reformatting Signed-off-by: Mario Danic --- .../nextcloud/talk/activities/BaseActivity.kt | 179 +- .../talk/activities/MagicCallActivity.kt | 86 +- .../nextcloud/talk/activities/MainActivity.kt | 207 +- .../talk/adapters/items/AdvancedUserItem.java | 232 +- .../talk/adapters/items/AppItem.java | 149 +- .../talk/adapters/items/CallItem.java | 245 +- .../talk/adapters/items/ConversationItem.java | 400 +- .../adapters/items/GenericTextHeaderItem.java | 104 +- .../items/MentionAutocompleteItem.java | 191 +- .../talk/adapters/items/MenuItem.java | 120 +- .../adapters/items/NotificationSoundItem.java | 134 +- .../talk/adapters/items/ProgressItem.java | 164 +- .../talk/adapters/items/UserItem.java | 482 +- .../MagicIncomingTextMessageViewHolder.java | 256 +- .../MagicOutcomingTextMessageViewHolder.java | 164 +- .../MagicPreviewMessageViewHolder.java | 240 +- .../MagicSystemMessageViewHolder.java | 72 +- .../MagicUnreadNoticeMessageViewHolder.java | 35 +- .../java/com/nextcloud/talk/api/NcApi.java | 405 +- .../application/NextcloudTalkApplication.kt | 262 +- .../MentionAutocompleteCallback.java | 72 +- .../adapters/items/BrowserFileItem.java | 257 +- .../controllers/BrowserController.java | 491 +- .../interfaces/ListingInterface.java | 2 +- .../filebrowser/models/BrowserFile.java | 105 +- .../filebrowser/models/DavResponse.java | 4 +- .../models/properties/NCEncrypted.java | 61 +- .../models/properties/NCPreview.java | 61 +- .../models/properties/OCFavorite.java | 61 +- .../filebrowser/models/properties/OCId.java | 61 +- .../filebrowser/models/properties/OCSize.java | 61 +- .../filebrowser/operations/DavListing.java | 54 +- .../operations/ListingAbstractClass.java | 29 +- .../filebrowser/webdav/DavUtils.java | 130 +- .../webdav/ReadFilesystemOperation.java | 109 +- .../AccountVerificationController.java | 807 ++-- .../talk/controllers/CallController.java | 4238 +++++++++-------- .../CallNotificationController.java | 744 +-- .../talk/controllers/ContactsController.java | 1766 +++---- .../controllers/ConversationInfoController.kt | 1244 ++--- .../ConversationsListController.java | 1046 ++-- .../talk/controllers/LockedController.java | 220 +- .../RingtoneSelectionController.java | 432 +- .../ServerSelectionController.java | 566 +-- .../talk/controllers/SettingsController.java | 1207 ++--- .../controllers/SwitchAccountController.java | 322 +- .../controllers/WebViewLoginController.java | 708 +-- .../controllers/base/ButterKnifeController.kt | 36 +- .../base/providers/ActionBarProvider.java | 2 +- .../bottomsheet/EntryMenuController.java | 485 +- .../bottomsheet/OperationsMenuController.java | 1215 ++--- .../items/BasicListItemWithImage.kt | 15 +- .../items/ListIconDialogAdapter.kt | 177 +- .../bottomsheet/items/MagicBottomSheets.kt | 72 +- .../talk/dagger/modules/BusModule.java | 10 +- .../talk/dagger/modules/ContextModule.java | 16 +- .../talk/dagger/modules/DatabaseModule.java | 39 +- .../talk/dagger/modules/RestModule.java | 449 +- .../talk/events/BottomSheetLockEvent.java | 43 +- .../talk/events/CertificateEvent.java | 40 +- .../nextcloud/talk/events/EventStatus.java | 23 +- .../talk/events/MediaStreamEvent.java | 17 +- .../talk/events/MoreMenuClickEvent.java | 8 +- .../nextcloud/talk/events/NetworkEvent.java | 14 +- .../talk/events/PeerConnectionEvent.java | 33 +- .../events/SessionDescriptionSendEvent.java | 31 +- .../talk/events/UserMentionClickEvent.java | 2 +- .../events/WebSocketCommunicationEvent.java | 6 +- .../talk/interfaces/ClosedInterface.kt | 4 +- .../interfaces/ConversationMenuInterface.kt | 5 +- .../talk/interfaces/SelectionInterface.kt | 4 +- .../talk/jobs/AccountRemovalWorker.java | 353 +- .../jobs/AddParticipantsToConversation.java | 86 +- .../talk/jobs/CapabilitiesWorker.java | 190 +- .../talk/jobs/DeleteConversationWorker.java | 122 +- .../talk/jobs/LeaveConversationWorker.java | 122 +- .../talk/jobs/PushRegistrationWorker.java | 24 +- .../talk/jobs/ShareOperationWorker.java | 113 +- .../talk/jobs/SignalingSettingsWorker.java | 172 +- .../talk/jobs/WebsocketConnectionsWorker.java | 72 +- .../talk/models/ExternalSignalingServer.java | 8 +- .../nextcloud/talk/models/ImportAccount.java | 18 +- .../com/nextcloud/talk/models/LoginData.java | 6 +- .../nextcloud/talk/models/RetrofitBucket.java | 4 +- .../talk/models/RingtoneSettings.java | 10 +- .../talk/models/SignatureVerification.java | 5 +- .../models/database/ArbitraryStorage.java | 10 +- .../nextcloud/talk/models/database/User.java | 143 +- .../json/autocomplete/AutocompleteOCS.java | 4 +- .../autocomplete/AutocompleteOverall.java | 4 +- .../json/autocomplete/AutocompleteUser.java | 12 +- .../json/capabilities/Capabilities.java | 16 +- .../json/capabilities/CapabilitiesList.java | 4 +- .../json/capabilities/CapabilitiesOCS.java | 4 +- .../capabilities/CapabilitiesOverall.java | 4 +- .../capabilities/NotificationsCapability.java | 4 +- .../json/capabilities/SpreedCapability.java | 8 +- .../json/capabilities/ThemingCapability.java | 40 +- .../talk/models/json/chat/ChatMessage.java | 442 +- .../talk/models/json/chat/ChatOCS.java | 4 +- .../talk/models/json/chat/ChatOverall.java | 4 +- .../talk/models/json/chat/ChatUtils.java | 29 +- .../json/conversations/Conversation.java | 236 +- .../models/json/conversations/RoomOCS.java | 4 +- .../json/conversations/RoomOverall.java | 4 +- .../models/json/conversations/RoomsOCS.java | 4 +- .../json/conversations/RoomsOverall.java | 4 +- .../converters/EnumLobbyStateConverter.java | 40 +- .../EnumNotificationLevelConverter.java | 60 +- .../EnumParticipantTypeConverter.java | 72 +- .../EnumReadOnlyConversationConverter.java | 44 +- .../converters/EnumRoomTypeConverter.java | 60 +- .../EnumSystemMessageTypeConverter.kt | 98 +- .../LoganSquareJodaTimeConverter.java | 39 +- .../converters/ObjectParcelConverter.java | 16 +- .../json/converters/UriTypeConverter.java | 28 +- .../talk/models/json/generic/GenericMeta.java | 12 +- .../talk/models/json/generic/GenericOCS.java | 4 +- .../models/json/generic/GenericOverall.java | 4 +- .../talk/models/json/generic/Status.java | 28 +- .../talk/models/json/mention/Mention.java | 14 +- .../talk/models/json/mention/MentionOCS.java | 4 +- .../models/json/mention/MentionOverall.java | 4 +- .../json/notifications/Notification.java | 60 +- .../notifications/NotificationAction.java | 16 +- .../json/notifications/NotificationOCS.java | 4 +- .../notifications/NotificationRichObject.java | 12 +- .../json/notifications/NotificationsOCS.java | 4 +- .../notifications/NotificationsOverall.java | 4 +- .../json/participants/AddParticipantOCS.java | 10 +- .../participants/AddParticipantOverall.java | 4 +- .../models/json/participants/Participant.java | 148 +- .../json/participants/ParticipantsOCS.java | 4 +- .../participants/ParticipantsOverall.java | 4 +- .../json/push/DecryptedPushMessage.java | 40 +- .../json/push/PushConfigurationState.java | 20 +- .../models/json/push/PushRegistration.java | 13 +- .../models/json/push/PushRegistrationOCS.java | 4 +- .../json/push/PushRegistrationOverall.java | 4 +- .../models/json/sharees/ExactSharees.java | 4 +- .../talk/models/json/sharees/Sharee.java | 12 +- .../talk/models/json/sharees/ShareesOCS.java | 4 +- .../models/json/sharees/ShareesOverall.java | 4 +- .../talk/models/json/sharees/SharesData.java | 9 +- .../talk/models/json/sharees/Value.java | 4 +- .../json/signaling/DataChannelMessage.java | 20 +- .../signaling/DataChannelMessageNick.java | 20 +- .../models/json/signaling/NCIceCandidate.java | 12 +- .../json/signaling/NCMessagePayload.java | 20 +- .../json/signaling/NCMessageWrapper.java | 14 +- .../json/signaling/NCSignalingMessage.java | 28 +- .../talk/models/json/signaling/Signaling.java | 10 +- .../models/json/signaling/SignalingOCS.java | 4 +- .../json/signaling/SignalingOverall.java | 4 +- .../json/signaling/settings/IceServer.java | 16 +- .../json/signaling/settings/Settings.java | 16 +- .../settings/SignalingSettingsOcs.java | 4 +- .../settings/SignalingSettingsOverall.java | 4 +- .../json/userprofile/UserProfileData.java | 12 +- .../json/userprofile/UserProfileOCS.java | 4 +- .../json/userprofile/UserProfileOverall.java | 4 +- .../json/websocket/ActorWebSocketMessage.java | 12 +- .../AuthParametersWebSocketMessage.java | 8 +- .../json/websocket/AuthWebSocketMessage.java | 8 +- .../json/websocket/BaseWebSocketMessage.java | 4 +- .../json/websocket/ByeWebSocketMessage.java | 4 +- .../CallOverallWebSocketMessage.java | 4 +- .../json/websocket/CallWebSocketMessage.java | 12 +- .../ErrorOverallWebSocketMessage.java | 4 +- .../json/websocket/ErrorWebSocketMessage.java | 8 +- .../EventOverallWebSocketMessage.java | 8 +- .../HelloOverallWebSocketMessage.java | 4 +- .../HelloResponseOverallWebSocketMessage.java | 4 +- .../HelloResponseWebSocketMessage.java | 21 +- .../json/websocket/HelloWebSocketMessage.java | 12 +- .../JoinedRoomOverallWebSocketMessage.java | 4 +- .../RequestOfferOverallWebSocketMessage.java | 4 +- .../RequestOfferSignalingMessage.java | 8 +- .../RoomOverallWebSocketMessage.java | 4 +- .../RoomPropertiesWebSocketMessage.java | 8 +- .../json/websocket/RoomWebSocketMessage.java | 12 +- ...HelloResponseFeaturesWebSocketMessage.java | 4 +- ...SignalingDataWebSocketMessageForOffer.java | 8 +- .../ConversationsListViewModel.kt | 1 - .../MentionAutocompletePresenter.java | 208 +- .../talk/receivers/PackageReplacedReceiver.kt | 90 +- .../com/nextcloud/talk/utils/AccountUtils.kt | 206 +- .../com/nextcloud/talk/utils/ApiUtils.java | 455 +- .../com/nextcloud/talk/utils/DateUtils.kt | 26 +- .../com/nextcloud/talk/utils/DeviceUtils.java | 112 +- .../nextcloud/talk/utils/DisplayUtils.java | 613 +-- .../nextcloud/talk/utils/DoNotDisturbUtils.kt | 116 +- .../com/nextcloud/talk/utils/DrawableUtils.kt | 278 +- .../talk/utils/EmojiTextInputEditText.java | 72 +- .../utils/FABAwareScrollingViewBehavior.java | 74 +- .../nextcloud/talk/utils/KeyboardUtils.java | 94 +- .../com/nextcloud/talk/utils/LoggingUtils.kt | 71 +- .../nextcloud/talk/utils/MagicCharPolicy.java | 124 +- .../java/com/nextcloud/talk/utils/MagicMap.kt | 10 +- .../nextcloud/talk/utils/NotificationUtils.kt | 262 +- .../utils/OkHttpNetworkFetcherWithCache.java | 19 +- .../com/nextcloud/talk/utils/PushUtils.java | 648 +-- .../nextcloud/talk/utils/SecurityUtils.java | 145 +- .../com/nextcloud/talk/utils/ShareUtils.java | 94 +- .../nextcloud/talk/utils/TextMatchers.java | 95 +- .../talk/utils/animations/PulseAnimation.java | 98 +- .../animations/SharedElementTransition.java | 88 +- .../ViewHidingBehaviourAnimation.java | 71 +- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 74 +- .../ArbitraryStorageModule.java | 15 +- .../ArbitraryStorageUtils.java | 58 +- .../talk/utils/database/user/UserModule.java | 14 +- .../talk/utils/database/user/UserUtils.java | 434 +- .../talk/utils/power/PowerManagerUtils.java | 280 +- .../talk/utils/power/ProximityLock.java | 59 +- .../utils/preferences/AppPreferences.java | 366 +- .../preferences/MagicUserInputModule.java | 90 +- .../DatabaseStorageFactory.java | 21 +- .../DatabaseStorageModule.java | 249 +- .../ApplicationWideCurrentRoomHolder.java | 93 +- .../ApplicationWideMessageHolder.java | 32 +- .../singletons/AvatarStatusCodeHolder.java | 22 +- .../talk/utils/ssl/MagicKeyManager.java | 285 +- .../talk/utils/ssl/MagicTrustManager.java | 246 +- .../talk/utils/ssl/SSLSocketFactoryCompat.kt | 270 +- .../com/nextcloud/talk/utils/text/Spans.java | 20 +- ...terialPreferenceCategoryWithRightLink.java | 201 +- .../talk/webrtc/MagicAudioManager.java | 1127 +++-- .../talk/webrtc/MagicBluetoothManager.java | 982 ++-- .../webrtc/MagicPeerConnectionWrapper.java | 821 ++-- .../talk/webrtc/MagicProximitySensor.java | 228 +- .../talk/webrtc/MagicWebRTCUtils.java | 244 +- .../talk/webrtc/MagicWebSocketInstance.java | 705 +-- .../webrtc/WebSocketConnectionHelper.java | 212 +- 234 files changed, 18720 insertions(+), 17790 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt index 006adc33d..76865365b 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt @@ -47,102 +47,111 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) open class BaseActivity : AppCompatActivity() { - @Inject - lateinit var eventBus: EventBus + @Inject + lateinit var eventBus: EventBus - @Inject - lateinit var appPreferences: AppPreferences + @Inject + lateinit var appPreferences: AppPreferences - @Inject - lateinit var context: Context + @Inject + lateinit var context: Context - override fun onCreate(savedInstanceState: Bundle?) { - NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - super.onCreate(savedInstanceState) + override fun onCreate(savedInstanceState: Bundle?) { + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + super.onCreate(savedInstanceState) + } + + public override fun onResume() { + super.onResume() + if (appPreferences.isScreenSecured) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) } - public override fun onResume() { - super.onResume() - if (appPreferences.isScreenSecured) { - window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) - } else { - window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + if (appPreferences.isScreenLocked) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + SecurityUtils.createKey(appPreferences.screenLockTimeout) + } + } + } + + fun showCertificateDialog( + cert: X509Certificate, + magicTrustManager: MagicTrustManager, + sslErrorHandler: SslErrorHandler? + ) { + val formatter = DateFormat.getDateInstance(DateFormat.LONG) + val validFrom = formatter.format(cert.notBefore) + val validUntil = formatter.format(cert.notAfter) + + val issuedBy = cert.issuerDN.toString() + val issuedFor: String + + try { + if (cert.subjectAlternativeNames != null) { + val stringBuilder = StringBuilder() + for (o in cert.subjectAlternativeNames) { + val list = o as List<*> + val type = list[0] as Int + if (type == 2) { + val name = list[1] as String + stringBuilder.append("[") + .append(type) + .append("]") + .append(name) + .append(" ") + } } + issuedFor = stringBuilder.toString() + } else { + issuedFor = cert.subjectDN.name + } - if (appPreferences.isScreenLocked) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - SecurityUtils.createKey(appPreferences.screenLockTimeout) - } - } + @SuppressLint("StringFormatMatches") val dialogText = String.format( + resources + .getString(R.string.nc_certificate_dialog_text), + issuedBy, issuedFor, validFrom, validUntil + ) + + LovelyStandardDialog(this) + .setTopColorRes(R.color.nc_darkRed) + .setNegativeButtonColorRes(R.color.nc_darkRed) + .setPositiveButtonColorRes(R.color.colorPrimaryDark) + .setIcon(R.drawable.ic_security_white_24dp) + .setTitle(R.string.nc_certificate_dialog_title) + .setMessage(dialogText) + .setPositiveButton(R.string.nc_yes) { v -> + magicTrustManager.addCertInTrustStore(cert) + sslErrorHandler?.proceed() + } + .setNegativeButton(R.string.nc_no) { view1 -> + sslErrorHandler?.cancel() + } + .show() + + } catch (e: CertificateParsingException) { + Log.d(TAG, "Failed to parse the certificate") } - fun showCertificateDialog(cert: X509Certificate, magicTrustManager: MagicTrustManager, - sslErrorHandler: SslErrorHandler?) { - val formatter = DateFormat.getDateInstance(DateFormat.LONG) - val validFrom = formatter.format(cert.notBefore) - val validUntil = formatter.format(cert.notAfter) + } - val issuedBy = cert.issuerDN.toString() - val issuedFor: String + @Subscribe(threadMode = ThreadMode.MAIN) + fun onMessageEvent(event: CertificateEvent) { + showCertificateDialog(event.x509Certificate, event.magicTrustManager, event.sslErrorHandler) + } - try { - if (cert.subjectAlternativeNames != null) { - val stringBuilder = StringBuilder() - for (o in cert.subjectAlternativeNames) { - val list = o as List<*> - val type = list[0] as Int - if (type == 2) { - val name = list[1] as String - stringBuilder.append("[").append(type).append("]").append(name).append(" ") - } - } - issuedFor = stringBuilder.toString() - } else { - issuedFor = cert.subjectDN.name - } + public override fun onStart() { + super.onStart() + eventBus.register(this) + } - @SuppressLint("StringFormatMatches") val dialogText = String.format(resources - .getString(R.string.nc_certificate_dialog_text), - issuedBy, issuedFor, validFrom, validUntil) + public override fun onStop() { + super.onStop() + eventBus.unregister(this) + } - LovelyStandardDialog(this) - .setTopColorRes(R.color.nc_darkRed) - .setNegativeButtonColorRes(R.color.nc_darkRed) - .setPositiveButtonColorRes(R.color.colorPrimaryDark) - .setIcon(R.drawable.ic_security_white_24dp) - .setTitle(R.string.nc_certificate_dialog_title) - .setMessage(dialogText) - .setPositiveButton(R.string.nc_yes) { v -> - magicTrustManager.addCertInTrustStore(cert) - sslErrorHandler?.proceed() - } - .setNegativeButton(R.string.nc_no) { view1 -> - sslErrorHandler?.cancel() - } - .show() - - } catch (e: CertificateParsingException) { - Log.d(TAG, "Failed to parse the certificate") - } - - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(event: CertificateEvent) { - showCertificateDialog(event.x509Certificate, event.magicTrustManager, event.sslErrorHandler) - } - - public override fun onStart() { - super.onStart() - eventBus.register(this) - } - - public override fun onStop() { - super.onStop() - eventBus.unregister(this) - } - - companion object { - private val TAG = "BaseActivity" - } + companion object { + private val TAG = "BaseActivity" + } } diff --git a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt index 0a864f24b..2fb0a27e9 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt @@ -43,54 +43,60 @@ import com.nextcloud.talk.utils.bundle.BundleKeys @AutoInjector(NextcloudTalkApplication::class) class MagicCallActivity : BaseActivity() { - @BindView(R.id.controller_container) - lateinit var container: ViewGroup + @BindView(R.id.controller_container) + lateinit var container: ViewGroup - private var router: Router? = null + private var router: Router? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - requestWindowFeature(Window.FEATURE_NO_TITLE) - window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or - WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or - WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or - WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) - window.decorView.systemUiVisibility = systemUiVisibility + requestWindowFeature(Window.FEATURE_NO_TITLE) + window.addFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN or + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or + WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + ) + window.decorView.systemUiVisibility = systemUiVisibility - setContentView(R.layout.activity_magic_call) + setContentView(R.layout.activity_magic_call) - ButterKnife.bind(this) - router = Conductor.attachRouter(this, container, savedInstanceState) - router!!.setPopsLastView(false) + ButterKnife.bind(this) + router = Conductor.attachRouter(this, container, savedInstanceState) + router!!.setPopsLastView(false) - if (!router!!.hasRootController()) { - if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { - router!!.setRoot(RouterTransaction.with(CallNotificationController(intent.extras)) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - } else { - router!!.setRoot(RouterTransaction.with(CallController(intent.extras)) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - } - } + if (!router!!.hasRootController()) { + if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { + router!!.setRoot( + RouterTransaction.with(CallNotificationController(intent.extras)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } else { + router!!.setRoot( + RouterTransaction.with(CallController(intent.extras)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } } + } - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - eventBus.post(ConfigurationChangeEvent()) - } + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + eventBus.post(ConfigurationChangeEvent()) + } - companion object { - private val TAG = "MagicCallActivity" + companion object { + private val TAG = "MagicCallActivity" - private val systemUiVisibility: Int - get() { - var flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN - flags = flags or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - return flags - } - } + private val systemUiVisibility: Int + get() { + var flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN + flags = flags or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + return flags + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index 7836ff2aa..269d83c1c 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -39,7 +39,6 @@ import com.google.android.material.appbar.MaterialToolbar import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.controllers.CallNotificationController -import com.nextcloud.talk.controllers.ConversationsListController import com.nextcloud.talk.controllers.LockedController import com.nextcloud.talk.controllers.ServerSelectionController import com.nextcloud.talk.controllers.base.providers.ActionBarProvider @@ -56,117 +55,129 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class MainActivity : BaseActivity(), ActionBarProvider { - @BindView(R.id.toolbar) - lateinit var toolbar: MaterialToolbar - @BindView(R.id.controller_container) - lateinit var container: ViewGroup + @BindView(R.id.toolbar) + lateinit var toolbar: MaterialToolbar + @BindView(R.id.controller_container) + lateinit var container: ViewGroup - @Inject - lateinit var userUtils: UserUtils - @Inject - lateinit var dataStore: ReactiveEntityStore - @Inject - lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource + @Inject + lateinit var userUtils: UserUtils + @Inject + lateinit var dataStore: ReactiveEntityStore + @Inject + lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource - private var router: Router? = null + private var router: Router? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + setContentView(R.layout.activity_main) - NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - ButterKnife.bind(this) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + ButterKnife.bind(this) - setSupportActionBar(toolbar) + setSupportActionBar(toolbar) - router = Conductor.attachRouter(this, container, savedInstanceState) + router = Conductor.attachRouter(this, container, savedInstanceState) - var hasDb = true + var hasDb = true - try { - sqlCipherDatabaseSource.writableDatabase - } catch (exception: Exception) { - hasDb = false - } - - if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { - if (!router!!.hasRootController()) { - router!!.setRoot(RouterTransaction.with(ConversationsListView()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - } - onNewIntent(intent) - } else if (!router!!.hasRootController()) { - if (hasDb) { - if (userUtils.anyUserExists()) { - router!!.setRoot(RouterTransaction.with(ConversationsListView()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - } else { - router!!.setRoot(RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - } - } else { - router!!.setRoot(RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - - } - } + try { + sqlCipherDatabaseSource.writableDatabase + } catch (exception: Exception) { + hasDb = false } - override fun onStart() { - super.onStart() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - checkIfWeAreSecure() + if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { + if (!router!!.hasRootController()) { + router!!.setRoot( + RouterTransaction.with(ConversationsListView()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } + onNewIntent(intent) + } else if (!router!!.hasRootController()) { + if (hasDb) { + if (userUtils.anyUserExists()) { + router!!.setRoot( + RouterTransaction.with(ConversationsListView()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } else { + router!!.setRoot( + RouterTransaction.with(ServerSelectionController()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) } + } else { + router!!.setRoot( + RouterTransaction.with(ServerSelectionController()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + + } + } + } + + override fun onStart() { + super.onStart() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + checkIfWeAreSecure() + } + } + + @RequiresApi(api = Build.VERSION_CODES.M) + fun checkIfWeAreSecure() { + val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) { + if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) { + if (router != null && router!!.getControllerWithTag(LockedController.TAG) == null) { + router!!.pushController( + RouterTransaction.with(LockedController()) + .pushChangeHandler(VerticalChangeHandler()) + .popChangeHandler(VerticalChangeHandler()) + .tag(LockedController.TAG) + ) + } + } + } + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + + if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { + if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { + router!!.pushController( + RouterTransaction.with(CallNotificationController(intent.extras)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } else { + ConductorRemapping.remapChatController( + router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1), + intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false + ) + } + } + } + + override fun onBackPressed() { + if (router!!.getControllerWithTag(LockedController.TAG) != null) { + return } - - @RequiresApi(api = Build.VERSION_CODES.M) - fun checkIfWeAreSecure() { - val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager - if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) { - if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) { - if (router != null && router!!.getControllerWithTag(LockedController.TAG) == null) { - router!!.pushController(RouterTransaction.with(LockedController()) - .pushChangeHandler(VerticalChangeHandler()) - .popChangeHandler(VerticalChangeHandler()) - .tag(LockedController.TAG)) - } - } - } + if (!router!!.handleBack()) { + super.onBackPressed() } + } - - override fun onNewIntent(intent: Intent) { - super.onNewIntent(intent) - - if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { - if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { - router!!.pushController(RouterTransaction.with(CallNotificationController(intent.extras)) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - } else { - ConductorRemapping.remapChatController(router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1), - intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false) - } - } - } - - override fun onBackPressed() { - if (router!!.getControllerWithTag(LockedController.TAG) != null) { - return - } - - if (!router!!.handleBack()) { - super.onBackPressed() - } - } - - companion object { - private val TAG = "MainActivity" - } + companion object { + private val TAG = "MainActivity" + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java index 1b8d6b6d0..cd76ccd26 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java @@ -48,126 +48,134 @@ import eu.davidea.viewholders.FlexibleViewHolder; import java.util.List; import java.util.regex.Pattern; -public class AdvancedUserItem extends AbstractFlexibleItem implements - IFilterable { +public class AdvancedUserItem extends AbstractFlexibleItem + implements + IFilterable { - private Participant participant; - private UserEntity userEntity; - @Nullable - private Account account; + private Participant participant; + private UserEntity userEntity; + @Nullable + private Account account; - public AdvancedUserItem(Participant participant, UserEntity userEntity, @Nullable Account account) { - this.participant = participant; - this.userEntity = userEntity; - this.account = account; + public AdvancedUserItem(Participant participant, UserEntity userEntity, + @Nullable Account account) { + this.participant = participant; + this.userEntity = userEntity; + this.account = account; + } + + @Override + public boolean equals(Object o) { + if (o instanceof AdvancedUserItem) { + AdvancedUserItem inItem = (AdvancedUserItem) o; + return participant.equals(inItem.getModel()); + } + return false; + } + + @Override + public int hashCode() { + return participant.hashCode(); + } + + /** + * @return the model object + */ + + public Participant getModel() { + return participant; + } + + public UserEntity getEntity() { + return userEntity; + } + + @Nullable + public Account getAccount() { + return account; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_conversation; + } + + @Override + public UserItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new UserItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, + List payloads) { + holder.avatarImageView.setController(null); + + if (adapter.hasFilter()) { + FlexibleUtils.highlightText(holder.contactDisplayName, participant.getName(), + String.valueOf(adapter.getFilter(String.class)), + NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.colorPrimary)); + } else { + holder.contactDisplayName.setText(participant.getName()); } - @Override - public boolean equals(Object o) { - if (o instanceof AdvancedUserItem) { - AdvancedUserItem inItem = (AdvancedUserItem) o; - return participant.equals(inItem.getModel()); - } - return false; - } + holder.serverUrl.setText(userEntity.getBaseUrl()); - @Override - public int hashCode() { - return participant.hashCode(); + if (userEntity != null && userEntity.getBaseUrl() != null && userEntity.getBaseUrl() + .startsWith("http://") || userEntity.getBaseUrl().startsWith("https://")) { + holder.avatarImageView.setVisibility(View.VISIBLE); + + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.avatarImageView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + participant.getUserId(), R.dimen.avatar_size), null)) + .build(); + holder.avatarImageView.setController(draweeController); + } else { + holder.avatarImageView.setVisibility(View.GONE); + RelativeLayout.LayoutParams layoutParams = + (RelativeLayout.LayoutParams) holder.linearLayout.getLayoutParams(); + layoutParams.setMarginStart( + (int) NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext() + .getResources().getDimension(R.dimen.activity_horizontal_margin)); + layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START); + holder.linearLayout.setLayoutParams(layoutParams); } + } + + @Override + public boolean filter(String constraint) { + return participant.getName() != null && + Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL) + .matcher(participant.getName().trim()) + .find(); + } + + static class UserItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.name_text) + public EmojiTextView contactDisplayName; + @BindView(R.id.secondary_text) + public TextView serverUrl; + @BindView(R.id.avatar_image) + public SimpleDraweeView avatarImageView; + @BindView(R.id.linear_layout) + LinearLayout linearLayout; + @BindView(R.id.more_menu) + ImageButton moreMenuButton; + @BindView(R.id.password_protected_image_view) + ImageView passwordProtectedImageView; /** - * @return the model object + * Default constructor. */ - - public Participant getModel() { - return participant; - } - - public UserEntity getEntity() { - return userEntity; - } - - @Nullable - public Account getAccount() { - return account; - } - - @Override - public int getLayoutRes() { - return R.layout.rv_item_conversation; - } - - @Override - public UserItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new UserItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) { - holder.avatarImageView.setController(null); - - if (adapter.hasFilter()) { - FlexibleUtils.highlightText(holder.contactDisplayName, participant.getName(), - String.valueOf(adapter.getFilter(String.class)), NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.colorPrimary)); - } else { - holder.contactDisplayName.setText(participant.getName()); - } - - holder.serverUrl.setText(userEntity.getBaseUrl()); - - if (userEntity != null && userEntity.getBaseUrl() != null && userEntity.getBaseUrl().startsWith("http://") || userEntity.getBaseUrl().startsWith("https://")) { - holder.avatarImageView.setVisibility(View.VISIBLE); - - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.avatarImageView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), - participant.getUserId(), R.dimen.avatar_size), null)) - .build(); - holder.avatarImageView.setController(draweeController); - - } else { - holder.avatarImageView.setVisibility(View.GONE); - RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) holder.linearLayout.getLayoutParams(); - layoutParams.setMarginStart((int) NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext() - .getResources().getDimension(R.dimen.activity_horizontal_margin)); - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START); - holder.linearLayout.setLayoutParams(layoutParams); - } - } - - @Override - public boolean filter(String constraint) { - return participant.getName() != null && - Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getName().trim()).find(); - } - - - static class UserItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.name_text) - public EmojiTextView contactDisplayName; - @BindView(R.id.secondary_text) - public TextView serverUrl; - @BindView(R.id.avatar_image) - public SimpleDraweeView avatarImageView; - @BindView(R.id.linear_layout) - LinearLayout linearLayout; - @BindView(R.id.more_menu) - ImageButton moreMenuButton; - @BindView(R.id.password_protected_image_view) - ImageView passwordProtectedImageView; - - /** - * Default constructor. - */ - UserItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - moreMenuButton.setVisibility(View.GONE); - passwordProtectedImageView.setVisibility(View.GONE); - } + UserItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + moreMenuButton.setVisibility(View.GONE); + passwordProtectedImageView.setVisibility(View.GONE); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/AppItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/AppItem.java index 2735d8410..c0d7f3f46 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/AppItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/AppItem.java @@ -39,86 +39,89 @@ import eu.davidea.viewholders.FlexibleViewHolder; import java.util.List; public class AppItem extends AbstractFlexibleItem { - private String title; - private String packageName; - private String name; - @Nullable - private Drawable drawable; + private String title; + private String packageName; + private String name; + @Nullable + private Drawable drawable; - public AppItem(String title, String packageName, String name, @Nullable Drawable drawable) { - this.title = title; - this.packageName = packageName; - this.name = name; - this.drawable = drawable; + public AppItem(String title, String packageName, String name, @Nullable Drawable drawable) { + this.title = title; + this.packageName = packageName; + this.name = name; + this.drawable = drawable; + } + + @Override + public boolean equals(Object o) { + if (o instanceof AppItem) { + AppItem inItem = (AppItem) o; + return title.equals(inItem.getTitle()) + && packageName.equals(inItem.getPackageName()) + && name.equals(inItem + .getName()); } - @Override - public boolean equals(Object o) { - if (o instanceof AppItem) { - AppItem inItem = (AppItem) o; - return title.equals(inItem.getTitle()) && packageName.equals(inItem.getPackageName()) && name.equals(inItem - .getName()); - } + return false; + } - return false; + public String getTitle() { + return title; + } + + public String getPackageName() { + return packageName; + } + + public String getName() { + return name; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_app; + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, AppItemViewHolder holder, + int position, List payloads) { + if (drawable != null) { + holder.iconImageView.setVisibility(View.VISIBLE); + holder.iconImageView.setImageDrawable(drawable); + } else { + holder.iconImageView.setVisibility(View.GONE); } - public String getTitle() { - return title; + if (position == 0) { + Spannable spannableString = new SpannableString(title); + spannableString.setSpan( + new ForegroundColorSpan(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.grey_600)), 0, + spannableString.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + holder.appTitleTextView.setText(spannableString); + } else { + holder.appTitleTextView.setText(title); } + } - public String getPackageName() { - return packageName; + @Override + public AppItem.AppItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new AppItemViewHolder(view, adapter); + } + + static class AppItemViewHolder extends FlexibleViewHolder { + @BindView(R.id.icon_image_view) + public ImageView iconImageView; + @BindView(R.id.app_title_text_view) + public TextView appTitleTextView; + + /** + * Default constructor. + */ + AppItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); } - - public String getName() { - return name; - } - - @Override - public int getLayoutRes() { - return R.layout.rv_item_app; - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, AppItemViewHolder holder, int position, List payloads) { - if (drawable != null) { - holder.iconImageView.setVisibility(View.VISIBLE); - holder.iconImageView.setImageDrawable(drawable); - } else { - holder.iconImageView.setVisibility(View.GONE); - } - - if (position == 0) { - Spannable spannableString = new SpannableString(title); - spannableString.setSpan(new ForegroundColorSpan(NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.grey_600)), 0, - spannableString.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - holder.appTitleTextView.setText(spannableString); - } else { - holder.appTitleTextView.setText(title); - } - } - - @Override - public AppItem.AppItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new AppItemViewHolder(view, adapter); - } - - static class AppItemViewHolder extends FlexibleViewHolder { - @BindView(R.id.icon_image_view) - public ImageView iconImageView; - @BindView(R.id.app_title_text_view) - public TextView appTitleTextView; - - /** - * Default constructor. - */ - AppItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java index b63b8ea41..ce6adfd67 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/CallItem.java @@ -48,138 +48,145 @@ import java.util.List; import java.util.regex.Pattern; import org.greenrobot.eventbus.EventBus; -public class CallItem extends AbstractFlexibleItem implements IFilterable { +public class CallItem extends AbstractFlexibleItem + implements IFilterable { - private Conversation conversation; - private UserEntity userEntity; + private Conversation conversation; + private UserEntity userEntity; - public CallItem(Conversation conversation, UserEntity userEntity) { - this.conversation = conversation; - this.userEntity = userEntity; + public CallItem(Conversation conversation, UserEntity userEntity) { + this.conversation = conversation; + this.userEntity = userEntity; + } + + @Override + public boolean equals(Object o) { + if (o instanceof CallItem) { + CallItem inItem = (CallItem) o; + return conversation.equals(inItem.getModel()); + } + return false; + } + + @Override + public int hashCode() { + return conversation.hashCode(); + } + + /** + * @return the model object + */ + + public Conversation getModel() { + return conversation; + } + + /** + * Filter is applied to the model fields. + */ + + @Override + public int getLayoutRes() { + return R.layout.rv_item_conversation; + } + + @Override + public RoomItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new RoomItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(final FlexibleAdapter adapter, RoomItemViewHolder holder, int position, + List payloads) { + if (adapter.hasFilter()) { + FlexibleUtils.highlightText(holder.roomDisplayName, conversation.getDisplayName(), + String.valueOf(adapter.getFilter(String.class)), + NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.colorPrimary)); + } else { + holder.roomDisplayName.setText(conversation.getDisplayName()); } - @Override - public boolean equals(Object o) { - if (o instanceof CallItem) { - CallItem inItem = (CallItem) o; - return conversation.equals(inItem.getModel()); - } - return false; + if (conversation.getLastPing() == 0) { + holder.roomLastPing.setText(R.string.nc_never); + } else { + holder.roomLastPing.setText( + DateUtils.getRelativeTimeSpanString(conversation.getLastPing() * 1000L, + System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE)); } - @Override - public int hashCode() { - return conversation.hashCode(); + if (conversation.hasPassword) { + holder.passwordProtectedImageView.setVisibility(View.VISIBLE); + } else { + holder.passwordProtectedImageView.setVisibility(View.GONE); } - /** - * @return the model object - */ + Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); + switch (conversation.getType()) { + case ROOM_TYPE_ONE_TO_ONE_CALL: + holder.avatarImageView.setVisibility(View.VISIBLE); - public Conversation getModel() { - return conversation; - } + holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string + .nc_description_more_menu_one_to_one), conversation.getDisplayName())); - /** - * Filter is applied to the model fields. - */ - - @Override - public int getLayoutRes() { - return R.layout.rv_item_conversation; - } - - @Override - public RoomItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new RoomItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(final FlexibleAdapter adapter, RoomItemViewHolder holder, int position, List payloads) { - if (adapter.hasFilter()) { - FlexibleUtils.highlightText(holder.roomDisplayName, conversation.getDisplayName(), - String.valueOf(adapter.getFilter(String.class)), NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.colorPrimary)); + if (!TextUtils.isEmpty(conversation.getName())) { + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.avatarImageView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + conversation.getName(), + R.dimen.avatar_size), null)) + .build(); + holder.avatarImageView.setController(draweeController); } else { - holder.roomDisplayName.setText(conversation.getDisplayName()); + holder.avatarImageView.setVisibility(View.GONE); } - - if (conversation.getLastPing() == 0) { - holder.roomLastPing.setText(R.string.nc_never); - } else { - holder.roomLastPing.setText(DateUtils.getRelativeTimeSpanString(conversation.getLastPing() * 1000L, - System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE)); - } - - if (conversation.hasPassword) { - holder.passwordProtectedImageView.setVisibility(View.VISIBLE); - } else { - holder.passwordProtectedImageView.setVisibility(View.GONE); - } - - Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); - switch (conversation.getType()) { - case ROOM_TYPE_ONE_TO_ONE_CALL: - holder.avatarImageView.setVisibility(View.VISIBLE); - - holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string - .nc_description_more_menu_one_to_one), conversation.getDisplayName())); - - if (!TextUtils.isEmpty(conversation.getName())) { - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.avatarImageView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), - conversation.getName(), - R.dimen.avatar_size), null)) - .build(); - holder.avatarImageView.setController(draweeController); - } else { - holder.avatarImageView.setVisibility(View.GONE); - } - break; - case ROOM_GROUP_CALL: - holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string - .nc_description_more_menu_group), conversation.getDisplayName())); - holder.avatarImageView.setActualImageResource(R.drawable.ic_people_group_white_24px); - holder.avatarImageView.setVisibility(View.VISIBLE); - break; - case ROOM_PUBLIC_CALL: - holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string - .nc_description_more_menu_public), conversation.getDisplayName())); - holder.avatarImageView.setActualImageResource(R.drawable.ic_link_white_24px); - holder.avatarImageView.setVisibility(View.VISIBLE); - break; - default: - holder.avatarImageView.setVisibility(View.GONE); - - } - - holder.moreMenuButton.setOnClickListener(view -> EventBus.getDefault().post(new MoreMenuClickEvent(conversation))); + break; + case ROOM_GROUP_CALL: + holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string + .nc_description_more_menu_group), conversation.getDisplayName())); + holder.avatarImageView.setActualImageResource(R.drawable.ic_people_group_white_24px); + holder.avatarImageView.setVisibility(View.VISIBLE); + break; + case ROOM_PUBLIC_CALL: + holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string + .nc_description_more_menu_public), conversation.getDisplayName())); + holder.avatarImageView.setActualImageResource(R.drawable.ic_link_white_24px); + holder.avatarImageView.setVisibility(View.VISIBLE); + break; + default: + holder.avatarImageView.setVisibility(View.GONE); } - @Override - public boolean filter(String constraint) { - return conversation.getDisplayName() != null && - Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(conversation.getDisplayName().trim()).find(); - } - - static class RoomItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.name_text) - public EmojiTextView roomDisplayName; - @BindView(R.id.secondary_text) - public EmojiTextView roomLastPing; - @BindView(R.id.avatar_image) - public SimpleDraweeView avatarImageView; - @BindView(R.id.more_menu) - public ImageButton moreMenuButton; - @BindView(R.id.password_protected_image_view) - ImageView passwordProtectedImageView; - - RoomItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } + holder.moreMenuButton.setOnClickListener( + view -> EventBus.getDefault().post(new MoreMenuClickEvent(conversation))); + } + + @Override + public boolean filter(String constraint) { + return conversation.getDisplayName() != null && + Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL) + .matcher(conversation.getDisplayName().trim()) + .find(); + } + + static class RoomItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.name_text) + public EmojiTextView roomDisplayName; + @BindView(R.id.secondary_text) + public EmojiTextView roomLastPing; + @BindView(R.id.avatar_image) + public SimpleDraweeView avatarImageView; + @BindView(R.id.more_menu) + public ImageButton moreMenuButton; + @BindView(R.id.password_protected_image_view) + ImageView passwordProtectedImageView; + + RoomItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index 95ef35e41..a06aaa03c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -35,11 +35,11 @@ import butterknife.ButterKnife; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; -import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.chat.ChatMessage; +import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import eu.davidea.flexibleadapter.FlexibleAdapter; @@ -51,215 +51,233 @@ import eu.davidea.viewholders.FlexibleViewHolder; import java.util.List; import java.util.regex.Pattern; -public class ConversationItem extends AbstractFlexibleItem implements - IFilterable { +public class ConversationItem + extends AbstractFlexibleItem implements + IFilterable { + private Conversation conversation; + private UserEntity userEntity; + private Context context; - private Conversation conversation; - private UserEntity userEntity; - private Context context; + public ConversationItem(Conversation conversation, UserEntity userEntity, + Context activityContext) { + this.conversation = conversation; + this.userEntity = userEntity; + this.context = activityContext; + } - public ConversationItem(Conversation conversation, UserEntity userEntity, - Context activityContext) { - this.conversation = conversation; - this.userEntity = userEntity; - this.context = activityContext; + @Override + public boolean equals(Object o) { + if (o instanceof ConversationItem) { + ConversationItem inItem = (ConversationItem) o; + return conversation.equals(inItem.getModel()); + } + return false; + } + + public Conversation getModel() { + return conversation; + } + + @Override + public int hashCode() { + return conversation.hashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_conversation_with_last_message; + } + + @Override + public ConversationItemViewHolder createViewHolder(View view, + FlexibleAdapter adapter) { + return new ConversationItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, ConversationItemViewHolder holder, + int position, List payloads) { + Context appContext = + NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); + + holder.dialogAvatar.setController(null); + + if (adapter.hasFilter()) { + FlexibleUtils.highlightText(holder.dialogName, conversation.getDisplayName(), + String.valueOf(adapter.getFilter(String.class)), + NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.colorPrimary)); + } else { + holder.dialogName.setText(conversation.getDisplayName()); } - @Override - public boolean equals(Object o) { - if (o instanceof ConversationItem) { - ConversationItem inItem = (ConversationItem) o; - return conversation.equals(inItem.getModel()); - } - return false; + if (conversation.getUnreadMessages() > 0) { + holder.dialogUnreadBubble.setVisibility(View.VISIBLE); + if (conversation.getUnreadMessages() < 100) { + holder.dialogUnreadBubble.setText(Long.toString(conversation.getUnreadMessages())); + } else { + holder.dialogUnreadBubble.setText("99+"); + } + + if (conversation.isUnreadMention()) { + holder.dialogUnreadBubble.setBackground( + context.getDrawable(R.drawable.bubble_circle_unread_mention)); + } else { + holder.dialogUnreadBubble.setBackground( + context.getDrawable(R.drawable.bubble_circle_unread)); + } + } else { + holder.dialogUnreadBubble.setVisibility(View.GONE); } - public Conversation getModel() { - return conversation; + if (conversation.isHasPassword()) { + holder.passwordProtectedRoomImageView.setVisibility(View.VISIBLE); + } else { + holder.passwordProtectedRoomImageView.setVisibility(View.GONE); } - @Override - public int hashCode() { - return conversation.hashCode(); + if (conversation.isFavorite()) { + holder.pinnedConversationImageView.setVisibility(View.VISIBLE); + } else { + holder.pinnedConversationImageView.setVisibility(View.GONE); } - @Override - public int getLayoutRes() { - return R.layout.rv_item_conversation_with_last_message; - } + if (conversation.getLastMessage() != null) { + holder.dialogDate.setVisibility(View.VISIBLE); + holder.dialogDate.setText( + DateUtils.getRelativeTimeSpanString(conversation.getLastActivity() * 1000L, + System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE)); - @Override - public ConversationItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new ConversationItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, ConversationItemViewHolder holder, int position, List payloads) { - Context appContext = - NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); - - holder.dialogAvatar.setController(null); - - if (adapter.hasFilter()) { - FlexibleUtils.highlightText(holder.dialogName, conversation.getDisplayName(), - String.valueOf(adapter.getFilter(String.class)), NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.colorPrimary)); + if (!TextUtils.isEmpty(conversation.getLastMessage().getSystemMessage()) + || Conversation.ConversationType.ROOM_SYSTEM.equals(conversation.getType())) { + holder.dialogLastMessage.setText(conversation.getLastMessage().getText()); + } else { + String authorDisplayName = ""; + conversation.getLastMessage().setActiveUser(userEntity); + String text; + if (conversation.getLastMessage() + .getMessageType() + .equals(ChatMessage.MessageType.REGULAR_TEXT_MESSAGE)) { + if (conversation.getLastMessage().getActorId().equals(userEntity.getUserId())) { + text = String.format(appContext.getString(R.string.nc_formatted_message_you), + conversation.getLastMessage().getLastMessageDisplayText()); + } else { + authorDisplayName = + !TextUtils.isEmpty(conversation.getLastMessage().getActorDisplayName()) ? + conversation.getLastMessage().getActorDisplayName() : + "guests".equals(conversation.getLastMessage().getActorType()) + ? appContext.getString(R.string.nc_guest) : ""; + text = String.format(appContext.getString(R.string.nc_formatted_message), + authorDisplayName, + conversation.getLastMessage().getLastMessageDisplayText()); + } } else { - holder.dialogName.setText(conversation.getDisplayName()); + text = conversation.getLastMessage().getLastMessageDisplayText(); } - if (conversation.getUnreadMessages() > 0) { - holder.dialogUnreadBubble.setVisibility(View.VISIBLE); - if (conversation.getUnreadMessages() < 100) { - holder.dialogUnreadBubble.setText(Long.toString(conversation.getUnreadMessages())); - } else { - holder.dialogUnreadBubble.setText("99+"); - } - - if (conversation.isUnreadMention()) { - holder.dialogUnreadBubble.setBackground(context.getDrawable(R.drawable.bubble_circle_unread_mention)); - } else { - holder.dialogUnreadBubble.setBackground(context.getDrawable(R.drawable.bubble_circle_unread)); - } - } else { - holder.dialogUnreadBubble.setVisibility(View.GONE); - } - - - if (conversation.isHasPassword()) { - holder.passwordProtectedRoomImageView.setVisibility(View.VISIBLE); - } else { - holder.passwordProtectedRoomImageView.setVisibility(View.GONE); - } - - if (conversation.isFavorite()) { - holder.pinnedConversationImageView.setVisibility(View.VISIBLE); - } else { - holder.pinnedConversationImageView.setVisibility(View.GONE); - } - - if (conversation.getLastMessage() != null) { - holder.dialogDate.setVisibility(View.VISIBLE); - holder.dialogDate.setText(DateUtils.getRelativeTimeSpanString(conversation.getLastActivity() * 1000L, - System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE)); - - if (!TextUtils.isEmpty(conversation.getLastMessage().getSystemMessage()) || Conversation.ConversationType.ROOM_SYSTEM.equals(conversation.getType())) { - holder.dialogLastMessage.setText(conversation.getLastMessage().getText()); - } else { - String authorDisplayName = ""; - conversation.getLastMessage().setActiveUser(userEntity); - String text; - if (conversation.getLastMessage().getMessageType().equals(ChatMessage.MessageType.REGULAR_TEXT_MESSAGE)) { - if (conversation.getLastMessage().getActorId().equals(userEntity.getUserId())) { - text = String.format(appContext.getString(R.string.nc_formatted_message_you), - conversation.getLastMessage().getLastMessageDisplayText()); - } else { - authorDisplayName = !TextUtils.isEmpty(conversation.getLastMessage().getActorDisplayName()) ? - conversation.getLastMessage().getActorDisplayName() : - "guests".equals(conversation.getLastMessage().getActorType()) ? appContext.getString(R.string.nc_guest) : ""; - text = String.format(appContext.getString(R.string.nc_formatted_message), - authorDisplayName, - conversation.getLastMessage().getLastMessageDisplayText()); - } - } else { - text = conversation.getLastMessage().getLastMessageDisplayText(); - } - - holder.dialogLastMessage.setText(text); - } - } else { - holder.dialogDate.setVisibility(View.GONE); - holder.dialogLastMessage.setText(R.string.nc_no_messages_yet); - } - - holder.dialogAvatar.setVisibility(View.VISIBLE); - - boolean shouldLoadAvatar = true; - String objectType; - if (!TextUtils.isEmpty(objectType = conversation.getObjectType())) { - switch (objectType) { - case "share:password": - shouldLoadAvatar = false; - holder.dialogAvatar.getHierarchy().setImage(new BitmapDrawable(DisplayUtils - .getRoundedBitmapFromVectorDrawableResource(context.getResources(), - R.drawable.ic_file_password_request)), 100, true); - break; - case "file": - shouldLoadAvatar = false; - holder.dialogAvatar.getHierarchy().setImage(new BitmapDrawable(DisplayUtils - .getRoundedBitmapFromVectorDrawableResource(context.getResources(), - R.drawable.ic_file_icon)), 100, true); - break; - default: - break; - } - } - - if (Conversation.ConversationType.ROOM_SYSTEM.equals(conversation.getType())) { - Drawable[] layers = new Drawable[2]; - layers[0] = context.getDrawable(R.drawable.ic_launcher_background); - layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground); - LayerDrawable layerDrawable = new LayerDrawable(layers); - - holder.dialogAvatar.getHierarchy().setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); - - shouldLoadAvatar = false; - } - - if (shouldLoadAvatar) { - switch (conversation.getType()) { - case ROOM_TYPE_ONE_TO_ONE_CALL: - if (!TextUtils.isEmpty(conversation.getName())) { - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.dialogAvatar.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), conversation.getName(), R.dimen.avatar_size), null)) - .build(); - holder.dialogAvatar.setController(draweeController); - } else { - holder.dialogAvatar.setVisibility(View.GONE); - } - break; - case ROOM_GROUP_CALL: - holder.dialogAvatar.getHierarchy().setImage(new BitmapDrawable(DisplayUtils.getRoundedBitmapFromVectorDrawableResource(context.getResources(), R.drawable.ic_people_group_white_24px)), 100, true); - break; - case ROOM_PUBLIC_CALL: - holder.dialogAvatar.getHierarchy().setImage(new BitmapDrawable(DisplayUtils - .getRoundedBitmapFromVectorDrawableResource(context.getResources(), - R.drawable.ic_link_white_24px)), 100, true); - break; - default: - holder.dialogAvatar.setVisibility(View.GONE); - } - } + holder.dialogLastMessage.setText(text); + } + } else { + holder.dialogDate.setVisibility(View.GONE); + holder.dialogLastMessage.setText(R.string.nc_no_messages_yet); } - @Override - public boolean filter(String constraint) { - return conversation.getDisplayName() != null && - Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(conversation.getDisplayName().trim()).find(); + holder.dialogAvatar.setVisibility(View.VISIBLE); + + boolean shouldLoadAvatar = true; + String objectType; + if (!TextUtils.isEmpty(objectType = conversation.getObjectType())) { + switch (objectType) { + case "share:password": + shouldLoadAvatar = false; + holder.dialogAvatar.getHierarchy().setImage(new BitmapDrawable(DisplayUtils + .getRoundedBitmapFromVectorDrawableResource(context.getResources(), + R.drawable.ic_file_password_request)), 100, true); + break; + case "file": + shouldLoadAvatar = false; + holder.dialogAvatar.getHierarchy().setImage(new BitmapDrawable(DisplayUtils + .getRoundedBitmapFromVectorDrawableResource(context.getResources(), + R.drawable.ic_file_icon)), 100, true); + break; + default: + break; + } } - static class ConversationItemViewHolder extends FlexibleViewHolder { - @BindView(R.id.dialogAvatar) - SimpleDraweeView dialogAvatar; - @BindView(R.id.dialogName) - EmojiTextView dialogName; - @BindView(R.id.dialogDate) - TextView dialogDate; - @BindView(R.id.dialogLastMessage) - EmojiTextView dialogLastMessage; - @BindView(R.id.dialogUnreadBubble) - TextView dialogUnreadBubble; - @BindView(R.id.passwordProtectedRoomImageView) - ImageView passwordProtectedRoomImageView; - @BindView(R.id.favoriteConversationImageView) - ImageView pinnedConversationImageView; + if (Conversation.ConversationType.ROOM_SYSTEM.equals(conversation.getType())) { + Drawable[] layers = new Drawable[2]; + layers[0] = context.getDrawable(R.drawable.ic_launcher_background); + layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground); + LayerDrawable layerDrawable = new LayerDrawable(layers); - ConversationItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } + holder.dialogAvatar.getHierarchy() + .setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); + + shouldLoadAvatar = false; } + + if (shouldLoadAvatar) { + switch (conversation.getType()) { + case ROOM_TYPE_ONE_TO_ONE_CALL: + if (!TextUtils.isEmpty(conversation.getName())) { + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.dialogAvatar.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + conversation.getName(), R.dimen.avatar_size), null)) + .build(); + holder.dialogAvatar.setController(draweeController); + } else { + holder.dialogAvatar.setVisibility(View.GONE); + } + break; + case ROOM_GROUP_CALL: + holder.dialogAvatar.getHierarchy() + .setImage(new BitmapDrawable( + DisplayUtils.getRoundedBitmapFromVectorDrawableResource(context.getResources(), + R.drawable.ic_people_group_white_24px)), 100, true); + break; + case ROOM_PUBLIC_CALL: + holder.dialogAvatar.getHierarchy().setImage(new BitmapDrawable(DisplayUtils + .getRoundedBitmapFromVectorDrawableResource(context.getResources(), + R.drawable.ic_link_white_24px)), 100, true); + break; + default: + holder.dialogAvatar.setVisibility(View.GONE); + } + } + } + + @Override + public boolean filter(String constraint) { + return conversation.getDisplayName() != null && + Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL) + .matcher(conversation.getDisplayName().trim()) + .find(); + } + + static class ConversationItemViewHolder extends FlexibleViewHolder { + @BindView(R.id.dialogAvatar) + SimpleDraweeView dialogAvatar; + @BindView(R.id.dialogName) + EmojiTextView dialogName; + @BindView(R.id.dialogDate) + TextView dialogDate; + @BindView(R.id.dialogLastMessage) + EmojiTextView dialogLastMessage; + @BindView(R.id.dialogUnreadBubble) + TextView dialogUnreadBubble; + @BindView(R.id.passwordProtectedRoomImageView) + ImageView passwordProtectedRoomImageView; + @BindView(R.id.favoriteConversationImageView) + ImageView pinnedConversationImageView; + + ConversationItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.java index 42067663b..e095a3c6d 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.java @@ -32,63 +32,63 @@ import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.FlexibleViewHolder; import java.util.List; -public class GenericTextHeaderItem extends AbstractHeaderItem { - private static final String TAG = "GenericTextHeaderItem"; +public class GenericTextHeaderItem + extends AbstractHeaderItem { + private static final String TAG = "GenericTextHeaderItem"; - private String title; + private String title; - public GenericTextHeaderItem(String title) { - super(); - setHidden(false); - setSelectable(false); - this.title = title; + public GenericTextHeaderItem(String title) { + super(); + setHidden(false); + setSelectable(false); + this.title = title; + } + + public String getModel() { + return title; + } + + @Override + public boolean equals(Object o) { + if (o instanceof GenericTextHeaderItem) { + GenericTextHeaderItem inItem = (GenericTextHeaderItem) o; + return title.equals(inItem.getModel()); } + return false; + } - public String getModel() { - return title; + @Override + public int getLayoutRes() { + return R.layout.rv_item_title_header; + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, + int position, List payloads) { + if (payloads.size() > 0) { + Log.d(TAG, "We have payloads, so ignoring!"); + } else { + holder.titleTextView.setText(title); } + } - @Override - public boolean equals(Object o) { - if (o instanceof GenericTextHeaderItem) { - GenericTextHeaderItem inItem = (GenericTextHeaderItem) o; - return title.equals(inItem.getModel()); - } - return false; + @Override + public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new HeaderViewHolder(view, adapter); + } + + static class HeaderViewHolder extends FlexibleViewHolder { + + @BindView(R.id.title_text_view) + public TextView titleTextView; + + /** + * Default constructor. + */ + HeaderViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter, true); + ButterKnife.bind(this, view); } - - @Override - public int getLayoutRes() { - return R.layout.rv_item_title_header; - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) { - if (payloads.size() > 0) { - Log.d(TAG, "We have payloads, so ignoring!"); - } else { - holder.titleTextView.setText(title); - } - - } - - @Override - public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderViewHolder(view, adapter); - } - - static class HeaderViewHolder extends FlexibleViewHolder { - - @BindView(R.id.title_text_view) - public TextView titleTextView; - - /** - * Default constructor. - */ - HeaderViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter, true); - ButterKnife.bind(this, view); - } - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java index 59aba15ba..7f0afee98 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.java @@ -38,103 +38,112 @@ import java.util.List; import java.util.regex.Pattern; public class MentionAutocompleteItem extends AbstractFlexibleItem - implements IFilterable { + implements IFilterable { - private String objectId; - private String displayName; - private String source; - private UserEntity currentUser; + private String objectId; + private String displayName; + private String source; + private UserEntity currentUser; - public MentionAutocompleteItem(String objectId, String displayName, String source, UserEntity currentUser) { - this.objectId = objectId; - this.displayName = displayName; - this.source = source; - this.currentUser = currentUser; + public MentionAutocompleteItem(String objectId, String displayName, String source, + UserEntity currentUser) { + this.objectId = objectId; + this.displayName = displayName; + this.source = source; + this.currentUser = currentUser; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getObjectId() { + return objectId; + } + + public String getDisplayName() { + return displayName; + } + + @Override + public boolean equals(Object o) { + if (o instanceof MentionAutocompleteItem) { + MentionAutocompleteItem inItem = (MentionAutocompleteItem) o; + return (objectId.equals(inItem.objectId) && displayName.equals(inItem.displayName)); } - public String getSource() { - return source; + return false; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_mention; + } + + @Override + public UserItem.UserItemViewHolder createViewHolder(View view, + FlexibleAdapter adapter) { + return new UserItem.UserItemViewHolder(view, adapter); + } + + @SuppressLint("SetTextI18n") + @Override + public void bindViewHolder(FlexibleAdapter adapter, UserItem.UserItemViewHolder holder, + int position, List payloads) { + + if (adapter.hasFilter()) { + FlexibleUtils.highlightText(holder.contactDisplayName, displayName, + String.valueOf(adapter.getFilter(String.class)), + NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.colorPrimary)); + if (holder.contactMentionId != null) { + FlexibleUtils.highlightText(holder.contactMentionId, "@" + objectId, + String.valueOf(adapter.getFilter(String.class)), + NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.colorPrimary)); + } + } else { + holder.contactDisplayName.setText(displayName); + if (holder.contactMentionId != null) { + holder.contactMentionId.setText("@" + objectId); + } } - public void setSource(String source) { - this.source = source; + if (source.equals("calls")) { + holder.simpleDraweeView.getHierarchy() + .setPlaceholderImage(DisplayUtils.getRoundedBitmapDrawableFromVectorDrawableResource( + NextcloudTalkApplication.Companion.getSharedApplication().getResources(), + R.drawable.ic_people_group_white_24px)); + } else { + String avatarId = objectId; + String avatarUrl = ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(), + avatarId, R.dimen.avatar_size_big); + + if (source.equals("guests")) { + avatarId = displayName; + avatarUrl = ApiUtils.getUrlForAvatarWithNameForGuests(currentUser.getBaseUrl(), avatarId, + R.dimen.avatar_size_big); + } + + holder.simpleDraweeView.setController(null); + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.simpleDraweeView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(avatarUrl, null)) + .build(); + holder.simpleDraweeView.setController(draweeController); } + } - public String getObjectId() { - return objectId; - } - - public String getDisplayName() { - return displayName; - } - - @Override - public boolean equals(Object o) { - if (o instanceof MentionAutocompleteItem) { - MentionAutocompleteItem inItem = (MentionAutocompleteItem) o; - return (objectId.equals(inItem.objectId) && displayName.equals(inItem.displayName)); - } - - return false; - } - - @Override - public int getLayoutRes() { - return R.layout.rv_item_mention; - } - - @Override - public UserItem.UserItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new UserItem.UserItemViewHolder(view, adapter); - } - - - @SuppressLint("SetTextI18n") - @Override - public void bindViewHolder(FlexibleAdapter adapter, UserItem.UserItemViewHolder holder, int position, List payloads) { - - if (adapter.hasFilter()) { - FlexibleUtils.highlightText(holder.contactDisplayName, displayName, - String.valueOf(adapter.getFilter(String.class)), NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.colorPrimary)); - if (holder.contactMentionId != null) { - FlexibleUtils.highlightText(holder.contactMentionId, "@" + objectId, - String.valueOf(adapter.getFilter(String.class)), NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.colorPrimary)); - } - } else { - holder.contactDisplayName.setText(displayName); - if (holder.contactMentionId != null) { - holder.contactMentionId.setText("@" + objectId); - } - } - - if (source.equals("calls")) { - holder.simpleDraweeView.getHierarchy().setPlaceholderImage(DisplayUtils.getRoundedBitmapDrawableFromVectorDrawableResource(NextcloudTalkApplication.Companion.getSharedApplication().getResources(), R.drawable.ic_people_group_white_24px)); - } else { - String avatarId = objectId; - String avatarUrl = ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(), - avatarId, R.dimen.avatar_size_big); - - if (source.equals("guests")) { - avatarId = displayName; - avatarUrl = ApiUtils.getUrlForAvatarWithNameForGuests(currentUser.getBaseUrl(), avatarId, R.dimen.avatar_size_big); - } - - holder.simpleDraweeView.setController(null); - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.simpleDraweeView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(avatarUrl, null)) - .build(); - holder.simpleDraweeView.setController(draweeController); - } - } - - @Override - public boolean filter(String constraint) { - return objectId != null && Pattern.compile(constraint, - Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(objectId).find() - || displayName != null && Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(displayName).find(); - } + @Override + public boolean filter(String constraint) { + return objectId != null && Pattern.compile(constraint, + Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(objectId).find() + || displayName != null && Pattern.compile(constraint, + Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(displayName).find(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java index 120bae1db..9ddc09c50 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java @@ -39,69 +39,71 @@ import eu.davidea.viewholders.FlexibleViewHolder; import java.util.List; public class MenuItem extends AbstractFlexibleItem { - private String title; - private Drawable icon; - private int tag; - private int padding; + private String title; + private Drawable icon; + private int tag; + private int padding; - public MenuItem(String title, int tag, Drawable icon) { - this.title = title; - this.tag = tag; - this.icon = icon; - padding = (int) DisplayUtils.convertDpToPixel(16, - NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext()); + public MenuItem(String title, int tag, Drawable icon) { + this.title = title; + this.tag = tag; + this.icon = icon; + padding = (int) DisplayUtils.convertDpToPixel(16, + NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext()); + } + + @Override + public boolean equals(Object o) { + if (o instanceof MenuItem) { + MenuItem inItem = (MenuItem) o; + return tag == inItem.tag; } + return false; + } - @Override - public boolean equals(Object o) { - if (o instanceof MenuItem) { - MenuItem inItem = (MenuItem) o; - return tag == inItem.tag; - } - return false; + public int getTag() { + return tag; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_menu; + } + + @Override + public MenuItem.MenuItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new MenuItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, MenuItem.MenuItemViewHolder holder, + int position, List payloads) { + if (position == 0) { + Spannable spannableString = new SpannableString(title); + spannableString.setSpan( + new ForegroundColorSpan(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources().getColor(R.color.grey_600)), 0, + spannableString.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + holder.menuTitle.setText(spannableString); + } else { + holder.menuTitle.setText(title); + holder.menuTitle.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); + holder.menuTitle.setCompoundDrawablePadding(padding); } + } - public int getTag() { - return tag; - } - - @Override - public int getLayoutRes() { - return R.layout.rv_item_menu; - } - - @Override - public MenuItem.MenuItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new MenuItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, MenuItem.MenuItemViewHolder holder, int position, List payloads) { - if (position == 0) { - Spannable spannableString = new SpannableString(title); - spannableString.setSpan(new ForegroundColorSpan(NextcloudTalkApplication.Companion.getSharedApplication() - .getResources().getColor(R.color.grey_600)), 0, - spannableString.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - holder.menuTitle.setText(spannableString); - } else { - holder.menuTitle.setText(title); - holder.menuTitle.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); - holder.menuTitle.setCompoundDrawablePadding(padding); - } - } - - static class MenuItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.menu_text) - public TextView menuTitle; - - /** - * Default constructor. - */ - MenuItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } + static class MenuItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.menu_text) + public TextView menuTitle; + + /** + * Default constructor. + */ + MenuItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/NotificationSoundItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/NotificationSoundItem.java index e950c00af..ebd5922da 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/NotificationSoundItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/NotificationSoundItem.java @@ -36,76 +36,80 @@ import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.FlexibleViewHolder; import java.util.List; -public class NotificationSoundItem extends AbstractFlexibleItem { +public class NotificationSoundItem + extends AbstractFlexibleItem { - private String notificationSoundName; - private String notificationSoundUri; + private String notificationSoundName; + private String notificationSoundUri; - public NotificationSoundItem(String notificationSoundName, String notificationSoundUri) { - this.notificationSoundName = notificationSoundName; - this.notificationSoundUri = notificationSoundUri; + public NotificationSoundItem(String notificationSoundName, String notificationSoundUri) { + this.notificationSoundName = notificationSoundName; + this.notificationSoundUri = notificationSoundUri; + } + + public String getNotificationSoundUri() { + return notificationSoundUri; + } + + public String getNotificationSoundName() { + return notificationSoundName; + } + + @Override + public boolean equals(Object o) { + return false; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_notification_sound; + } + + @Override + public NotificationSoundItemViewHolder createViewHolder(View view, + FlexibleAdapter adapter) { + return new NotificationSoundItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, + NotificationSoundItemViewHolder holder, int position, List payloads) { + holder.notificationName.setText(notificationSoundName); + + if (adapter.isSelected(position)) { + holder.checkedImageView.setVisibility(View.VISIBLE); + } else { + holder.checkedImageView.setVisibility(View.GONE); } - public String getNotificationSoundUri() { - return notificationSoundUri; + Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); + holder.simpleDraweeView.getHierarchy() + .setBackgroundImage(new ColorDrawable(resources.getColor(R.color.colorPrimary))); + if (position == 0) { + holder.simpleDraweeView.getHierarchy() + .setImage(resources.getDrawable(R.drawable.ic_stop_white_24dp), 100, + true); + } else { + holder.simpleDraweeView.getHierarchy() + .setImage(resources.getDrawable(R.drawable.ic_play_circle_outline_white_24dp), 100, + true); } + } - public String getNotificationSoundName() { - return notificationSoundName; + static class NotificationSoundItemViewHolder extends FlexibleViewHolder { + @BindView(R.id.notificationNameTextView) + public TextView notificationName; + @BindView(R.id.simpleDraweeView) + SimpleDraweeView simpleDraweeView; + @BindView(R.id.checkedImageView) + ImageView checkedImageView; + + /** + * Default constructor. + */ + NotificationSoundItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); } - - @Override - public boolean equals(Object o) { - return false; - } - - @Override - public int getLayoutRes() { - return R.layout.rv_item_notification_sound; - } - - @Override - public NotificationSoundItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new NotificationSoundItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, NotificationSoundItemViewHolder holder, int position, List payloads) { - holder.notificationName.setText(notificationSoundName); - - if (adapter.isSelected(position)) { - holder.checkedImageView.setVisibility(View.VISIBLE); - } else { - holder.checkedImageView.setVisibility(View.GONE); - } - - Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); - holder.simpleDraweeView.getHierarchy().setBackgroundImage(new ColorDrawable(resources.getColor(R.color.colorPrimary))); - if (position == 0) { - holder.simpleDraweeView.getHierarchy().setImage(resources.getDrawable(R.drawable.ic_stop_white_24dp), 100, - true); - } else { - holder.simpleDraweeView.getHierarchy().setImage(resources.getDrawable(R.drawable.ic_play_circle_outline_white_24dp), 100, - true); - } - } - - static class NotificationSoundItemViewHolder extends FlexibleViewHolder { - @BindView(R.id.notificationNameTextView) - public TextView notificationName; - @BindView(R.id.simpleDraweeView) - SimpleDraweeView simpleDraweeView; - @BindView(R.id.checkedImageView) - ImageView checkedImageView; - - /** - * Default constructor. - */ - NotificationSoundItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } - } - - + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java index 83d889a83..eb10cf4c1 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java @@ -47,95 +47,95 @@ import java.util.List; */ public class ProgressItem extends AbstractFlexibleItem { - private StatusEnum status = StatusEnum.MORE_TO_LOAD; + private StatusEnum status = StatusEnum.MORE_TO_LOAD; - @Override - public boolean equals(Object o) { - return this == o;//The default implementation + @Override + public boolean equals(Object o) { + return this == o;//The default implementation + } + + public StatusEnum getStatus() { + return status; + } + + public void setStatus(StatusEnum status) { + this.status = status; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_progress; + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, ProgressViewHolder holder, + int position, List payloads) { + Context context = holder.itemView.getContext(); + holder.progressBar.setVisibility(View.GONE); + holder.progressMessage.setVisibility(View.VISIBLE); + + if (!adapter.isEndlessScrollEnabled()) { + setStatus(StatusEnum.DISABLE_ENDLESS); + } else if (payloads.contains(Payload.NO_MORE_LOAD)) { + setStatus(StatusEnum.NO_MORE_LOAD); } - public StatusEnum getStatus() { - return status; + switch (this.status) { + case NO_MORE_LOAD: + holder.progressMessage.setText( + context.getString(R.string.nc_no_more_load_retry)); + // Reset to default status for next binding + setStatus(StatusEnum.MORE_TO_LOAD); + break; + case DISABLE_ENDLESS: + holder.progressMessage.setText(context.getString(R.string.nc_endless_disabled)); + break; + case ON_CANCEL: + holder.progressMessage.setText(context.getString(R.string.nc_endless_cancel)); + // Reset to default status for next binding + setStatus(StatusEnum.MORE_TO_LOAD); + break; + case ON_ERROR: + holder.progressMessage.setText(context.getString(R.string.nc_endless_error)); + // Reset to default status for next binding + setStatus(StatusEnum.MORE_TO_LOAD); + break; + default: + holder.progressBar.setVisibility(View.VISIBLE); + holder.progressMessage.setVisibility(View.GONE); + break; } + } - public void setStatus(StatusEnum status) { - this.status = status; + @Override + public ProgressViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new ProgressViewHolder(view, adapter); + } + + public enum StatusEnum { + MORE_TO_LOAD, //Default = should have an empty Payload + DISABLE_ENDLESS, //Endless is disabled because user has set limits + NO_MORE_LOAD, //Non-empty Payload = Payload.NO_MORE_LOAD + ON_CANCEL, + ON_ERROR + } + + static class ProgressViewHolder extends FlexibleViewHolder { + + @BindView(R.id.progress_bar) + ProgressBar progressBar; + @BindView(R.id.progress_message) + TextView progressMessage; + + ProgressViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); } @Override - public int getLayoutRes() { - return R.layout.rv_item_progress; + public void scrollAnimators(@NonNull List animators, int position, + boolean isForward) { + AnimatorHelper.scaleAnimator(animators, itemView, 0f); } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, ProgressViewHolder holder, int position, List payloads) { - Context context = holder.itemView.getContext(); - holder.progressBar.setVisibility(View.GONE); - holder.progressMessage.setVisibility(View.VISIBLE); - - if (!adapter.isEndlessScrollEnabled()) { - setStatus(StatusEnum.DISABLE_ENDLESS); - } else if (payloads.contains(Payload.NO_MORE_LOAD)) { - setStatus(StatusEnum.NO_MORE_LOAD); - } - - switch (this.status) { - case NO_MORE_LOAD: - holder.progressMessage.setText( - context.getString(R.string.nc_no_more_load_retry)); - // Reset to default status for next binding - setStatus(StatusEnum.MORE_TO_LOAD); - break; - case DISABLE_ENDLESS: - holder.progressMessage.setText(context.getString(R.string.nc_endless_disabled)); - break; - case ON_CANCEL: - holder.progressMessage.setText(context.getString(R.string.nc_endless_cancel)); - // Reset to default status for next binding - setStatus(StatusEnum.MORE_TO_LOAD); - break; - case ON_ERROR: - holder.progressMessage.setText(context.getString(R.string.nc_endless_error)); - // Reset to default status for next binding - setStatus(StatusEnum.MORE_TO_LOAD); - break; - default: - holder.progressBar.setVisibility(View.VISIBLE); - holder.progressMessage.setVisibility(View.GONE); - break; - } - } - - @Override - public ProgressViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new ProgressViewHolder(view, adapter); - } - - - public enum StatusEnum { - MORE_TO_LOAD, //Default = should have an empty Payload - DISABLE_ENDLESS, //Endless is disabled because user has set limits - NO_MORE_LOAD, //Non-empty Payload = Payload.NO_MORE_LOAD - ON_CANCEL, - ON_ERROR - } - - static class ProgressViewHolder extends FlexibleViewHolder { - - @BindView(R.id.progress_bar) - ProgressBar progressBar; - @BindView(R.id.progress_message) - TextView progressMessage; - - ProgressViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } - - @Override - public void scrollAnimators(@NonNull List animators, int position, boolean isForward) { - AnimatorHelper.scaleAnimator(animators, itemView, 0f); - } - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java index ec1c81844..d613fdec0 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/UserItem.java @@ -50,248 +50,266 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; public class UserItem extends AbstractFlexibleItem implements - ISectionable, IFilterable { + ISectionable, IFilterable { - private Participant participant; - private UserEntity userEntity; - private GenericTextHeaderItem header; - private Context activityContext; - public boolean isOnline = true; + public boolean isOnline = true; + private Participant participant; + private UserEntity userEntity; + private GenericTextHeaderItem header; + private Context activityContext; - public UserItem(Participant participant, UserEntity userEntity, - GenericTextHeaderItem genericTextHeaderItem, Context activityContext) { - this.participant = participant; - this.userEntity = userEntity; - this.header = genericTextHeaderItem; - this.activityContext = activityContext; + public UserItem(Participant participant, UserEntity userEntity, + GenericTextHeaderItem genericTextHeaderItem, Context activityContext) { + this.participant = participant; + this.userEntity = userEntity; + this.header = genericTextHeaderItem; + this.activityContext = activityContext; + } + + @Override + public boolean equals(Object o) { + if (o instanceof UserItem) { + UserItem inItem = (UserItem) o; + return participant.getUserId().equals(inItem.getModel().getUserId()); + } + return false; + } + + @Override + public int hashCode() { + return participant.hashCode(); + } + + /** + * @return the model object + */ + + public Participant getModel() { + return participant; + } + + public UserEntity getEntity() { + return userEntity; + } + + @Override + public int getLayoutRes() { + if (header != null) { + return R.layout.rv_item_contact; + } else { + return R.layout.rv_item_conversation_info_participant; + } + } + + @Override + public UserItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new UserItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, + List payloads) { + + holder.simpleDraweeView.setController(null); + + if (holder.checkedImageView != null) { + if (participant.isSelected()) { + holder.checkedImageView.setVisibility(View.VISIBLE); + } else { + holder.checkedImageView.setVisibility(View.GONE); + } } - @Override - public boolean equals(Object o) { - if (o instanceof UserItem) { - UserItem inItem = (UserItem) o; - return participant.getUserId().equals(inItem.getModel().getUserId()); + if (!isOnline) { + if (holder.contactMentionId != null) { + holder.contactMentionId.setAlpha(0.38f); + } + holder.contactDisplayName.setAlpha(0.38f); + holder.simpleDraweeView.setAlpha(0.38f); + } else { + if (holder.contactMentionId != null) { + holder.contactMentionId.setAlpha(1.0f); + } + holder.contactDisplayName.setAlpha(1.0f); + holder.simpleDraweeView.setAlpha(1.0f); + } + + if (adapter.hasFilter()) { + FlexibleUtils.highlightText(holder.contactDisplayName, participant.getDisplayName(), + String.valueOf(adapter.getFilter(String.class)), + activityContext.getResources().getColor(R.color.colorPrimary)); + } + + holder.contactDisplayName.setText(participant.getDisplayName()); + + if (TextUtils.isEmpty(participant.getDisplayName()) && + (participant.getType().equals(Participant.ParticipantType.GUEST) || participant.getType() + .equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) { + holder.contactDisplayName.setText( + NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest)); + } + + if (TextUtils.isEmpty(participant.getSource()) || participant.getSource().equals("users")) { + if (Participant.ParticipantType.GUEST.equals(participant.getType()) || + Participant.ParticipantType.USER_FOLLOWING_LINK.equals(participant.getType())) { + String displayName = NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getString(R.string.nc_guest); + + if (!TextUtils.isEmpty(participant.getDisplayName())) { + displayName = participant.getDisplayName(); } - return false; + + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.simpleDraweeView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithNameForGuests(userEntity.getBaseUrl(), + displayName, R.dimen.avatar_size), null)) + .build(); + holder.simpleDraweeView.setController(draweeController); + } else { + + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.simpleDraweeView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + participant.getUserId(), R.dimen.avatar_size), null)) + .build(); + holder.simpleDraweeView.setController(draweeController); + } + } else if ("groups".equals(participant.getSource())) { + holder.simpleDraweeView.getHierarchy() + .setImage(new BitmapDrawable(DisplayUtils.getRoundedBitmapFromVectorDrawableResource( + activityContext.getResources(), R.drawable.ic_people_group_white_24px)), 100, true); } - @Override - public int hashCode() { - return participant.hashCode(); + Resources resources = activityContext.getResources(); + + if (header == null) { + Participant.ParticipantFlags participantFlags = participant.getParticipantFlags(); + switch (participantFlags) { + case NOT_IN_CALL: + holder.voiceOrSimpleCallImageView.setVisibility(View.GONE); + holder.videoCallImageView.setVisibility(View.GONE); + break; + case IN_CALL: + holder.voiceOrSimpleCallImageView.setBackground( + resources.getDrawable(R.drawable.shape_call_bubble)); + holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); + holder.videoCallImageView.setVisibility(View.GONE); + break; + case IN_CALL_WITH_AUDIO: + holder.voiceOrSimpleCallImageView.setBackground( + resources.getDrawable(R.drawable.shape_voice_bubble)); + holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); + holder.videoCallImageView.setVisibility(View.GONE); + break; + case IN_CALL_WITH_VIDEO: + holder.voiceOrSimpleCallImageView.setBackground( + resources.getDrawable(R.drawable.shape_call_bubble)); + holder.videoCallImageView.setBackground( + resources.getDrawable(R.drawable.shape_video_bubble)); + holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); + holder.videoCallImageView.setVisibility(View.VISIBLE); + break; + case IN_CALL_WITH_AUDIO_AND_VIDEO: + holder.voiceOrSimpleCallImageView.setBackground( + resources.getDrawable(R.drawable.shape_voice_bubble)); + holder.videoCallImageView.setBackground( + resources.getDrawable(R.drawable.shape_video_bubble)); + holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); + holder.videoCallImageView.setVisibility(View.VISIBLE); + break; + default: + holder.voiceOrSimpleCallImageView.setVisibility(View.GONE); + holder.videoCallImageView.setVisibility(View.GONE); + break; + } + + if (holder.contactMentionId != null) { + String userType = ""; + + switch (new EnumParticipantTypeConverter().convertToInt(participant.getType())) { + case 1: + //userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_owner); + //break; + case 2: + userType = NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_moderator); + break; + case 3: + userType = NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_user); + break; + case 4: + userType = NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest); + break; + case 5: + userType = NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_following_link); + break; + default: + break; + } + + if (!holder.contactMentionId.getText().equals(userType)) { + holder.contactMentionId.setText(userType); + holder.contactMentionId.setTextColor( + activityContext.getResources().getColor(R.color.colorPrimary)); + } + } } + } + + @Override + public boolean filter(String constraint) { + return participant.getDisplayName() != null && + (Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL) + .matcher(participant.getDisplayName().trim()) + .find() || + Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL) + .matcher(participant.getUserId().trim()) + .find()); + } + + @Override + public GenericTextHeaderItem getHeader() { + return header; + } + + @Override + public void setHeader(GenericTextHeaderItem header) { + this.header = header; + } + + static class UserItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.name_text) + public EmojiTextView contactDisplayName; + @BindView(R.id.simple_drawee_view) + public SimpleDraweeView simpleDraweeView; + @Nullable + @BindView(R.id.secondary_text) + public EmojiTextView contactMentionId; + @Nullable + @BindView(R.id.voiceOrSimpleCallImageView) + ImageView voiceOrSimpleCallImageView; + @Nullable + @BindView(R.id.videoCallImageView) + ImageView videoCallImageView; + @Nullable + @BindView(R.id.checkedImageView) + ImageView checkedImageView; /** - * @return the model object + * Default constructor. */ - - public Participant getModel() { - return participant; + UserItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); } - - public UserEntity getEntity() { - return userEntity; - } - - - @Override - public int getLayoutRes() { - if (header != null) { - return R.layout.rv_item_contact; - } else { - return R.layout.rv_item_conversation_info_participant; - } - } - - @Override - public UserItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new UserItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) { - - holder.simpleDraweeView.setController(null); - - if (holder.checkedImageView != null) { - if (participant.isSelected()) { - holder.checkedImageView.setVisibility(View.VISIBLE); - } else { - holder.checkedImageView.setVisibility(View.GONE); - } - } - - if (!isOnline) { - if (holder.contactMentionId != null) { - holder.contactMentionId.setAlpha(0.38f); - } - holder.contactDisplayName.setAlpha(0.38f); - holder.simpleDraweeView.setAlpha(0.38f); - } else { - if (holder.contactMentionId != null) { - holder.contactMentionId.setAlpha(1.0f); - } - holder.contactDisplayName.setAlpha(1.0f); - holder.simpleDraweeView.setAlpha(1.0f); - } - - if (adapter.hasFilter()) { - FlexibleUtils.highlightText(holder.contactDisplayName, participant.getDisplayName(), - String.valueOf(adapter.getFilter(String.class)), activityContext.getResources().getColor(R.color.colorPrimary)); - } - - holder.contactDisplayName.setText(participant.getDisplayName()); - - if (TextUtils.isEmpty(participant.getDisplayName()) && - (participant.getType().equals(Participant.ParticipantType.GUEST) || participant.getType().equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) { - holder.contactDisplayName.setText(NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest)); - } - - if (TextUtils.isEmpty(participant.getSource()) || participant.getSource().equals("users")) { - if (Participant.ParticipantType.GUEST.equals(participant.getType()) || - Participant.ParticipantType.USER_FOLLOWING_LINK.equals(participant.getType())) { - String displayName = NextcloudTalkApplication.Companion.getSharedApplication().getResources().getString(R.string.nc_guest); - - if (!TextUtils.isEmpty(participant.getDisplayName())) { - displayName = participant.getDisplayName(); - } - - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.simpleDraweeView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameForGuests(userEntity.getBaseUrl(), - displayName, R.dimen.avatar_size), null)) - .build(); - holder.simpleDraweeView.setController(draweeController); - - } else { - - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(holder.simpleDraweeView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), - participant.getUserId(), R.dimen.avatar_size), null)) - .build(); - holder.simpleDraweeView.setController(draweeController); - - } - } else if ("groups".equals(participant.getSource())) { - holder.simpleDraweeView.getHierarchy().setImage(new BitmapDrawable(DisplayUtils.getRoundedBitmapFromVectorDrawableResource(activityContext.getResources(), R.drawable.ic_people_group_white_24px)), 100, true); - } - - Resources resources = activityContext.getResources(); - - if (header == null) { - Participant.ParticipantFlags participantFlags = participant.getParticipantFlags(); - switch (participantFlags) { - case NOT_IN_CALL: - holder.voiceOrSimpleCallImageView.setVisibility(View.GONE); - holder.videoCallImageView.setVisibility(View.GONE); - break; - case IN_CALL: - holder.voiceOrSimpleCallImageView.setBackground(resources.getDrawable(R.drawable.shape_call_bubble)); - holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); - holder.videoCallImageView.setVisibility(View.GONE); - break; - case IN_CALL_WITH_AUDIO: - holder.voiceOrSimpleCallImageView.setBackground(resources.getDrawable(R.drawable.shape_voice_bubble)); - holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); - holder.videoCallImageView.setVisibility(View.GONE); - break; - case IN_CALL_WITH_VIDEO: - holder.voiceOrSimpleCallImageView.setBackground(resources.getDrawable(R.drawable.shape_call_bubble)); - holder.videoCallImageView.setBackground(resources.getDrawable(R.drawable.shape_video_bubble)); - holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); - holder.videoCallImageView.setVisibility(View.VISIBLE); - break; - case IN_CALL_WITH_AUDIO_AND_VIDEO: - holder.voiceOrSimpleCallImageView.setBackground(resources.getDrawable(R.drawable.shape_voice_bubble)); - holder.videoCallImageView.setBackground(resources.getDrawable(R.drawable.shape_video_bubble)); - holder.voiceOrSimpleCallImageView.setVisibility(View.VISIBLE); - holder.videoCallImageView.setVisibility(View.VISIBLE); - break; - default: - holder.voiceOrSimpleCallImageView.setVisibility(View.GONE); - holder.videoCallImageView.setVisibility(View.GONE); - break; - } - - - if (holder.contactMentionId != null) { - String userType = ""; - - switch (new EnumParticipantTypeConverter().convertToInt(participant.getType())) { - case 1: - //userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_owner); - //break; - case 2: - userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_moderator); - break; - case 3: - userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_user); - break; - case 4: - userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest); - break; - case 5: - userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_following_link); - break; - default: - break; - } - - if (!holder.contactMentionId.getText().equals(userType)) { - holder.contactMentionId.setText(userType); - holder.contactMentionId.setTextColor(activityContext.getResources().getColor(R.color.colorPrimary)); - } - } - } - } - - @Override - public boolean filter(String constraint) { - return participant.getDisplayName() != null && - (Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getDisplayName().trim()).find() || - Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getUserId().trim()).find()); - } - - @Override - public GenericTextHeaderItem getHeader() { - return header; - } - - @Override - public void setHeader(GenericTextHeaderItem header) { - this.header = header; - } - - - static class UserItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.name_text) - public EmojiTextView contactDisplayName; - @BindView(R.id.simple_drawee_view) - public SimpleDraweeView simpleDraweeView; - @Nullable - @BindView(R.id.secondary_text) - public EmojiTextView contactMentionId; - @Nullable - @BindView(R.id.voiceOrSimpleCallImageView) - ImageView voiceOrSimpleCallImageView; - @Nullable - @BindView(R.id.videoCallImageView) - ImageView videoCallImageView; - @Nullable - @BindView(R.id.checkedImageView) - ImageView checkedImageView; - - /** - * Default constructor. - */ - UserItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } - } - - + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java index 1fd945e7a..f73846b6f 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java @@ -54,150 +54,154 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class MagicIncomingTextMessageViewHolder - extends MessageHolders.IncomingTextMessageViewHolder { + extends MessageHolders.IncomingTextMessageViewHolder { - @BindView(R.id.messageAuthor) - EmojiTextView messageAuthor; + @BindView(R.id.messageAuthor) + EmojiTextView messageAuthor; - @BindView(R.id.messageText) - EmojiTextView messageText; + @BindView(R.id.messageText) + EmojiTextView messageText; - @BindView(R.id.messageUserAvatar) - SimpleDraweeView messageUserAvatarView; + @BindView(R.id.messageUserAvatar) + SimpleDraweeView messageUserAvatarView; - @BindView(R.id.messageTime) - TextView messageTimeView; + @BindView(R.id.messageTime) + TextView messageTimeView; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - Context context; + @Inject + Context context; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - private View itemView; + private View itemView; - public MagicIncomingTextMessageViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public MagicIncomingTextMessageViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - this.itemView = itemView; + this.itemView = itemView; + } + + @Override + public void onBind(ChatMessage message) { + super.onBind(message); + String author; + + if (!TextUtils.isEmpty(author = message.getActorDisplayName())) { + messageAuthor.setText(author); + } else { + messageAuthor.setText(R.string.nc_nick_guest); } + if (!message.isGrouped() && !message.isOneToOneConversation()) { + messageUserAvatarView.setVisibility(View.VISIBLE); + if (message.getActorType().equals("guests")) { + // do nothing, avatar is set + } else if (message.getActorType().equals("bots") && message.getActorId() + .equals("changelog")) { + messageUserAvatarView.setController(null); + Drawable[] layers = new Drawable[2]; + layers[0] = context.getDrawable(R.drawable.ic_launcher_background); + layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground); + LayerDrawable layerDrawable = new LayerDrawable(layers); - @Override - public void onBind(ChatMessage message) { - super.onBind(message); - String author; + messageUserAvatarView.getHierarchy() + .setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); + } else if (message.getActorType().equals("bots")) { + messageUserAvatarView.setController(null); + TextDrawable drawable = + TextDrawable.builder().beginConfig().bold().endConfig().buildRound(">", + context.getResources().getColor(R.color.black)); + messageUserAvatarView.setVisibility(View.VISIBLE); + messageUserAvatarView.getHierarchy().setPlaceholderImage(drawable); + } + } else { + if (message.isOneToOneConversation()) { + messageUserAvatarView.setVisibility(View.GONE); + } else { + messageUserAvatarView.setVisibility(View.INVISIBLE); + } + messageAuthor.setVisibility(View.GONE); + } - if (!TextUtils.isEmpty(author = message.getActorDisplayName())) { - messageAuthor.setText(author); - } else { - messageAuthor.setText(R.string.nc_nick_guest); - } + Resources resources = itemView.getResources(); - if (!message.isGrouped() && !message.isOneToOneConversation()) { - messageUserAvatarView.setVisibility(View.VISIBLE); - if (message.getActorType().equals("guests")) { - // do nothing, avatar is set - } else if (message.getActorType().equals("bots") && message.getActorId().equals("changelog")) { - messageUserAvatarView.setController(null); - Drawable[] layers = new Drawable[2]; - layers[0] = context.getDrawable(R.drawable.ic_launcher_background); - layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground); - LayerDrawable layerDrawable = new LayerDrawable(layers); + int bg_bubble_color = resources.getColor(R.color.bg_message_list_incoming_bubble); - messageUserAvatarView.getHierarchy().setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); - } else if (message.getActorType().equals("bots")) { - messageUserAvatarView.setController(null); - TextDrawable drawable = TextDrawable.builder().beginConfig().bold().endConfig().buildRound(">", - context.getResources().getColor(R.color.black)); - messageUserAvatarView.setVisibility(View.VISIBLE); - messageUserAvatarView.getHierarchy().setPlaceholderImage(drawable); - } - } else { - if (message.isOneToOneConversation()) { - messageUserAvatarView.setVisibility(View.GONE); + int bubbleResource = R.drawable.shape_incoming_message; + + if (message.isGrouped) { + bubbleResource = R.drawable.shape_grouped_incoming_message; + } + + Drawable bubbleDrawable = DisplayUtils.getMessageSelector(bg_bubble_color, + resources.getColor(R.color.transparent), + bg_bubble_color, bubbleResource); + ViewCompat.setBackground(bubble, bubbleDrawable); + + HashMap> messageParameters = message.getMessageParameters(); + + itemView.setSelected(false); + messageTimeView.setTextColor(context.getResources().getColor(R.color.warm_grey_four)); + + FlexboxLayout.LayoutParams layoutParams = + (FlexboxLayout.LayoutParams) messageTimeView.getLayoutParams(); + layoutParams.setWrapBefore(false); + + Spannable messageString = new SpannableString(message.getText()); + + float textSize = context.getResources().getDimension(R.dimen.chat_text_size); + + if (messageParameters != null && messageParameters.size() > 0) { + for (String key : messageParameters.keySet()) { + Map individualHashMap = message.getMessageParameters().get(key); + if (individualHashMap != null) { + if (individualHashMap.get("type").equals("user") || individualHashMap.get("type") + .equals("guest") || individualHashMap.get("type").equals("call")) { + if (individualHashMap.get("id").equals(message.getActiveUser().getUserId())) { + messageString = + DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), + messageString, + individualHashMap.get("id"), + individualHashMap.get("name"), + individualHashMap.get("type"), + userUtils.getUserById(message.getActiveUser().getUserId()), + R.xml.chip_you); } else { - messageUserAvatarView.setVisibility(View.INVISIBLE); + messageString = + DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), + messageString, + individualHashMap.get("id"), + individualHashMap.get("name"), + individualHashMap.get("type"), + userUtils.getUserById(message.getActiveUser().getUserId()), + R.xml.chip_others); } - messageAuthor.setVisibility(View.GONE); + } else if (individualHashMap.get("type").equals("file")) { + itemView.setOnClickListener(v -> { + Intent browserIntent = + new Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap.get("link"))); + context.startActivity(browserIntent); + }); + } } - - Resources resources = itemView.getResources(); - - int bg_bubble_color = resources.getColor(R.color.bg_message_list_incoming_bubble); - - int bubbleResource = R.drawable.shape_incoming_message; - - if (message.isGrouped) { - bubbleResource = R.drawable.shape_grouped_incoming_message; - } - - Drawable bubbleDrawable = DisplayUtils.getMessageSelector(bg_bubble_color, - resources.getColor(R.color.transparent), - bg_bubble_color, bubbleResource); - ViewCompat.setBackground(bubble, bubbleDrawable); - - HashMap> messageParameters = message.getMessageParameters(); - - itemView.setSelected(false); - messageTimeView.setTextColor(context.getResources().getColor(R.color.warm_grey_four)); - - FlexboxLayout.LayoutParams layoutParams = (FlexboxLayout.LayoutParams) messageTimeView.getLayoutParams(); - layoutParams.setWrapBefore(false); - - Spannable messageString = new SpannableString(message.getText()); - - float textSize = context.getResources().getDimension(R.dimen.chat_text_size); - - if (messageParameters != null && messageParameters.size() > 0) { - for (String key : messageParameters.keySet()) { - Map individualHashMap = message.getMessageParameters().get(key); - if (individualHashMap != null) { - if (individualHashMap.get("type").equals("user") || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call")) { - if (individualHashMap.get("id").equals(message.getActiveUser().getUserId())) { - messageString = - DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), - messageString, - individualHashMap.get("id"), - individualHashMap.get("name"), - individualHashMap.get("type"), - userUtils.getUserById(message.getActiveUser().getUserId()), - R.xml.chip_you); - } else { - messageString = - DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), - messageString, - individualHashMap.get("id"), - individualHashMap.get("name"), - individualHashMap.get("type"), - userUtils.getUserById(message.getActiveUser().getUserId()), - R.xml.chip_others); - } - - } else if (individualHashMap.get("type").equals("file")) { - itemView.setOnClickListener(v -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap.get("link"))); - context.startActivity(browserIntent); - }); - - } - } - } - - } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.getText())) { - textSize = (float) (textSize * 2.5); - layoutParams.setWrapBefore(true); - itemView.setSelected(true); - messageAuthor.setVisibility(View.GONE); - } - - messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); - messageTimeView.setLayoutParams(layoutParams); - messageText.setText(messageString); + } + } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.getText())) { + textSize = (float) (textSize * 2.5); + layoutParams.setWrapBefore(true); + itemView.setSelected(true); + messageAuthor.setVisibility(View.GONE); } + + messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); + messageTimeView.setLayoutParams(layoutParams); + messageText.setText(messageString); + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java index 3c3abd881..971bb2a2f 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java @@ -48,91 +48,99 @@ import java.util.Map; import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) -public class MagicOutcomingTextMessageViewHolder extends MessageHolders.OutcomingTextMessageViewHolder { - @BindView(R.id.messageText) - EmojiTextView messageText; +public class MagicOutcomingTextMessageViewHolder + extends MessageHolders.OutcomingTextMessageViewHolder { + @BindView(R.id.messageText) + EmojiTextView messageText; - @BindView(R.id.messageTime) - TextView messageTimeView; + @BindView(R.id.messageTime) + TextView messageTimeView; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - Context context; + @Inject + Context context; - private View itemView; + private View itemView; - public MagicOutcomingTextMessageViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public MagicOutcomingTextMessageViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - this.itemView = itemView; + this.itemView = itemView; + } + + @Override + public void onBind(ChatMessage message) { + super.onBind(message); + + HashMap> messageParameters = message.getMessageParameters(); + + Spannable messageString = new SpannableString(message.getText()); + + itemView.setSelected(false); + messageTimeView.setTextColor(context.getResources().getColor(R.color.white60)); + + FlexboxLayout.LayoutParams layoutParams = + (FlexboxLayout.LayoutParams) messageTimeView.getLayoutParams(); + layoutParams.setWrapBefore(false); + + float textSize = context.getResources().getDimension(R.dimen.chat_text_size); + + if (messageParameters != null && messageParameters.size() > 0) { + for (String key : messageParameters.keySet()) { + Map individualHashMap = message.getMessageParameters().get(key); + if (individualHashMap != null) { + if (individualHashMap.get("type").equals("user") || individualHashMap.get("type") + .equals("guest") || individualHashMap.get("type").equals("call")) { + messageString = + DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), + messageString, + individualHashMap.get("id"), + individualHashMap.get("name"), + individualHashMap.get("type"), + userUtils.getUserById(message.getActiveUser().getUserId()), + R.xml.chip_others); + } else if (individualHashMap.get("type").equals("file")) { + itemView.setOnClickListener(v -> { + Intent browserIntent = + new Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap.get("link"))); + context.startActivity(browserIntent); + }); + } + } + } + } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.getText())) { + textSize = (float) (textSize * 2.5); + layoutParams.setWrapBefore(true); + messageTimeView.setTextColor(context.getResources().getColor(R.color.warm_grey_four)); + itemView.setSelected(true); } - @Override - public void onBind(ChatMessage message) { - super.onBind(message); - - HashMap> messageParameters = message.getMessageParameters(); - - Spannable messageString = new SpannableString(message.getText()); - - itemView.setSelected(false); - messageTimeView.setTextColor(context.getResources().getColor(R.color.white60)); - - FlexboxLayout.LayoutParams layoutParams = (FlexboxLayout.LayoutParams) messageTimeView.getLayoutParams(); - layoutParams.setWrapBefore(false); - - float textSize = context.getResources().getDimension(R.dimen.chat_text_size); - - if (messageParameters != null && messageParameters.size() > 0) { - for (String key : messageParameters.keySet()) { - Map individualHashMap = message.getMessageParameters().get(key); - if (individualHashMap != null) { - if (individualHashMap.get("type").equals("user") || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call")) { - messageString = - DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), - messageString, - individualHashMap.get("id"), - individualHashMap.get("name"), - individualHashMap.get("type"), - userUtils.getUserById(message.getActiveUser().getUserId()), - R.xml.chip_others); - } else if (individualHashMap.get("type").equals("file")) { - itemView.setOnClickListener(v -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap.get("link"))); - context.startActivity(browserIntent); - }); - - } - } - } - - } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.getText())) { - textSize = (float) (textSize * 2.5); - layoutParams.setWrapBefore(true); - messageTimeView.setTextColor(context.getResources().getColor(R.color.warm_grey_four)); - itemView.setSelected(true); - } - - Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); - if (message.isGrouped) { - Drawable bubbleDrawable = - DisplayUtils.getMessageSelector(resources.getColor(R.color.bg_message_list_outcoming_bubble), - resources.getColor(R.color.transparent), - resources.getColor(R.color.bg_message_list_outcoming_bubble), R.drawable.shape_grouped_outcoming_message); - ViewCompat.setBackground(bubble, bubbleDrawable); - } else { - Drawable bubbleDrawable = DisplayUtils.getMessageSelector(resources.getColor(R.color.bg_message_list_outcoming_bubble), - resources.getColor(R.color.transparent), - resources.getColor(R.color.bg_message_list_outcoming_bubble), R.drawable.shape_outcoming_message); - ViewCompat.setBackground(bubble, bubbleDrawable); - } - - messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); - messageTimeView.setLayoutParams(layoutParams); - messageText.setText(messageString); + Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); + if (message.isGrouped) { + Drawable bubbleDrawable = + DisplayUtils.getMessageSelector( + resources.getColor(R.color.bg_message_list_outcoming_bubble), + resources.getColor(R.color.transparent), + resources.getColor(R.color.bg_message_list_outcoming_bubble), + R.drawable.shape_grouped_outcoming_message); + ViewCompat.setBackground(bubble, bubbleDrawable); + } else { + Drawable bubbleDrawable = DisplayUtils.getMessageSelector( + resources.getColor(R.color.bg_message_list_outcoming_bubble), + resources.getColor(R.color.transparent), + resources.getColor(R.color.bg_message_list_outcoming_bubble), + R.drawable.shape_outcoming_message); + ViewCompat.setBackground(bubble, bubbleDrawable); } + + messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); + messageTimeView.setLayoutParams(layoutParams); + messageText.setText(messageString); + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java index db73a5d95..15db7da4f 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java @@ -55,126 +55,144 @@ import javax.inject.Inject; import okhttp3.OkHttpClient; @AutoInjector(NextcloudTalkApplication.class) -public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageMessageViewHolder { +public class MagicPreviewMessageViewHolder + extends MessageHolders.IncomingImageMessageViewHolder { - @BindView(R.id.messageText) - EmojiTextView messageText; + @BindView(R.id.messageText) + EmojiTextView messageText; - @Inject - Context context; + @Inject + Context context; - @Inject - OkHttpClient okHttpClient; + @Inject + OkHttpClient okHttpClient; - public MagicPreviewMessageViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - } + public MagicPreviewMessageViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } - @SuppressLint("SetTextI18n") - @Override - public void onBind(ChatMessage message) { - super.onBind(message); - if (userAvatar != null) { - if (message.isGrouped || message.isOneToOneConversation) { - if (message.isOneToOneConversation) { - userAvatar.setVisibility(View.GONE); - } else { - userAvatar.setVisibility(View.INVISIBLE); - } - } else { - userAvatar.setVisibility(View.VISIBLE); - - if ("bots".equals(message.getActorType()) && "changelog".equals(message.getActorId())) { - Drawable[] layers = new Drawable[2]; - layers[0] = context.getDrawable(R.drawable.ic_launcher_background); - layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground); - LayerDrawable layerDrawable = new LayerDrawable(layers); - - userAvatar.getHierarchy().setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); - } - } - } - - if (message.getMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) { - // it's a preview for a Nextcloud share - messageText.setText(message.getSelectedIndividualHashMap().get("name")); - DisplayUtils.setClickableString(message.getSelectedIndividualHashMap().get("name"), message.getSelectedIndividualHashMap().get("link"), messageText); - if (message.getSelectedIndividualHashMap().containsKey("mimetype")) { - image.getHierarchy().setPlaceholderImage(context.getDrawable(DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(message.getSelectedIndividualHashMap().get("mimetype")))); - } else { - fetchFileInformation("/" + message.getSelectedIndividualHashMap().get("path"), message.getActiveUser()); - } - - image.setOnClickListener(v -> { - - String accountString = - message.getActiveUser().getUsername() + "@" + message.getActiveUser().getBaseUrl().replace("https://", "").replace("http://", ""); - - if (AccountUtils.INSTANCE.canWeOpenFilesApp(context, accountString)) { - Intent filesAppIntent = new Intent(Intent.ACTION_VIEW, null); - final ComponentName componentName = new ComponentName(context.getString(R.string.nc_import_accounts_from), "com.owncloud.android.ui.activity.FileDisplayActivity"); - filesAppIntent.setComponent(componentName); - filesAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - filesAppIntent.setPackage(context.getString(R.string.nc_import_accounts_from)); - filesAppIntent.putExtra(BundleKeys.INSTANCE.getKEY_ACCOUNT(), accountString); - filesAppIntent.putExtra(BundleKeys.INSTANCE.getKEY_FILE_ID(), message.getSelectedIndividualHashMap().get("id")); - context.startActivity(filesAppIntent); - } else { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(message.getSelectedIndividualHashMap().get("link"))); - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(browserIntent); - } - }); - } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { - messageText.setText("GIPHY"); - DisplayUtils.setClickableString("GIPHY", "https://giphy.com", messageText); - } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE) { - messageText.setText("Tenor"); - DisplayUtils.setClickableString("Tenor", "https://tenor.com", messageText); + @SuppressLint("SetTextI18n") + @Override + public void onBind(ChatMessage message) { + super.onBind(message); + if (userAvatar != null) { + if (message.isGrouped || message.isOneToOneConversation) { + if (message.isOneToOneConversation) { + userAvatar.setVisibility(View.GONE); } else { - if (message.getMessageType().equals(ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE)) { - image.setOnClickListener(v -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(message.getImageUrl())); - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(browserIntent); - }); - } else { - image.setOnClickListener(null); - } - messageText.setText(""); + userAvatar.setVisibility(View.INVISIBLE); } + } else { + userAvatar.setVisibility(View.VISIBLE); + + if ("bots".equals(message.getActorType()) && "changelog".equals(message.getActorId())) { + Drawable[] layers = new Drawable[2]; + layers[0] = context.getDrawable(R.drawable.ic_launcher_background); + layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground); + LayerDrawable layerDrawable = new LayerDrawable(layers); + + userAvatar.getHierarchy() + .setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); + } + } } - private void fetchFileInformation(String url, UserEntity activeUser) { - Single.fromCallable(new Callable() { - @Override - public ReadFilesystemOperation call() { - return new ReadFilesystemOperation(okHttpClient, activeUser, url, 0); + if (message.getMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) { + // it's a preview for a Nextcloud share + messageText.setText(message.getSelectedIndividualHashMap().get("name")); + DisplayUtils.setClickableString(message.getSelectedIndividualHashMap().get("name"), + message.getSelectedIndividualHashMap().get("link"), messageText); + if (message.getSelectedIndividualHashMap().containsKey("mimetype")) { + image.getHierarchy() + .setPlaceholderImage(context.getDrawable( + DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType( + message.getSelectedIndividualHashMap().get("mimetype")))); + } else { + fetchFileInformation("/" + message.getSelectedIndividualHashMap().get("path"), + message.getActiveUser()); + } + + image.setOnClickListener(v -> { + + String accountString = + message.getActiveUser().getUsername() + "@" + message.getActiveUser() + .getBaseUrl() + .replace("https://", "") + .replace("http://", ""); + + if (AccountUtils.INSTANCE.canWeOpenFilesApp(context, accountString)) { + Intent filesAppIntent = new Intent(Intent.ACTION_VIEW, null); + final ComponentName componentName = + new ComponentName(context.getString(R.string.nc_import_accounts_from), + "com.owncloud.android.ui.activity.FileDisplayActivity"); + filesAppIntent.setComponent(componentName); + filesAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + filesAppIntent.setPackage(context.getString(R.string.nc_import_accounts_from)); + filesAppIntent.putExtra(BundleKeys.INSTANCE.getKEY_ACCOUNT(), accountString); + filesAppIntent.putExtra(BundleKeys.INSTANCE.getKEY_FILE_ID(), + message.getSelectedIndividualHashMap().get("id")); + context.startActivity(filesAppIntent); + } else { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, + Uri.parse(message.getSelectedIndividualHashMap().get("link"))); + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(browserIntent); + } + }); + } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { + messageText.setText("GIPHY"); + DisplayUtils.setClickableString("GIPHY", "https://giphy.com", messageText); + } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE) { + messageText.setText("Tenor"); + DisplayUtils.setClickableString("Tenor", "https://tenor.com", messageText); + } else { + if (message.getMessageType().equals(ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE)) { + image.setOnClickListener(v -> { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(message.getImageUrl())); + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(browserIntent); + }); + } else { + image.setOnClickListener(null); + } + messageText.setText(""); + } + } + + private void fetchFileInformation(String url, UserEntity activeUser) { + Single.fromCallable(new Callable() { + @Override + public ReadFilesystemOperation call() { + return new ReadFilesystemOperation(okHttpClient, activeUser, url, 0); + } + }).observeOn(Schedulers.io()) + .subscribe(new SingleObserver() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onSuccess(ReadFilesystemOperation readFilesystemOperation) { + DavResponse davResponse = readFilesystemOperation.readRemotePath(); + if (davResponse.getData() != null) { + List browserFileList = (List) davResponse.getData(); + if (!browserFileList.isEmpty()) { + new Handler(context.getMainLooper()).post(() -> image.getHierarchy() + .setPlaceholderImage(context.getDrawable( + DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType( + browserFileList.get(0).getMimeType())))); + } } - }).observeOn(Schedulers.io()) - .subscribe(new SingleObserver() { - @Override - public void onSubscribe(Disposable d) { + } - } - - @Override - public void onSuccess(ReadFilesystemOperation readFilesystemOperation) { - DavResponse davResponse = readFilesystemOperation.readRemotePath(); - if (davResponse.getData() != null) { - List browserFileList = (List) davResponse.getData(); - if (!browserFileList.isEmpty()) { - new Handler(context.getMainLooper()).post(() -> image.getHierarchy().setPlaceholderImage(context.getDrawable(DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(browserFileList.get(0).getMimeType())))); - } - } - } - - @Override - public void onError(Throwable e) { - } - }); - - } + @Override + public void onError(Throwable e) { + } + }); + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java index e95067bf8..fc09ad2f3 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java @@ -38,48 +38,54 @@ import java.util.Map; import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) -public class MagicSystemMessageViewHolder extends MessageHolders.IncomingTextMessageViewHolder { +public class MagicSystemMessageViewHolder + extends MessageHolders.IncomingTextMessageViewHolder { - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @Inject - Context context; + @Inject + Context context; - public MagicSystemMessageViewHolder(View itemView) { - super(itemView); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - } + public MagicSystemMessageViewHolder(View itemView) { + super(itemView); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } - @Override - public void onBind(ChatMessage message) { - super.onBind(message); + @Override + public void onBind(ChatMessage message) { + super.onBind(message); - Resources resources = itemView.getResources(); - int normalColor = resources.getColor(R.color.bg_message_list_incoming_bubble); - int pressedColor; - int mentionColor; + Resources resources = itemView.getResources(); + int normalColor = resources.getColor(R.color.bg_message_list_incoming_bubble); + int pressedColor; + int mentionColor; + pressedColor = normalColor; + mentionColor = resources.getColor(R.color.nc_author_text); - pressedColor = normalColor; - mentionColor = resources.getColor(R.color.nc_author_text); + Drawable bubbleDrawable = DisplayUtils.getMessageSelector(normalColor, + resources.getColor(R.color.transparent), pressedColor, + R.drawable.shape_grouped_incoming_message); + ViewCompat.setBackground(bubble, bubbleDrawable); - Drawable bubbleDrawable = DisplayUtils.getMessageSelector(normalColor, - resources.getColor(R.color.transparent), pressedColor, - R.drawable.shape_grouped_incoming_message); - ViewCompat.setBackground(bubble, bubbleDrawable); + Spannable messageString = new SpannableString(message.getText()); - Spannable messageString = new SpannableString(message.getText()); - - if (message.getMessageParameters() != null && message.getMessageParameters().size() > 0) { - for (String key : message.getMessageParameters().keySet()) { - Map individualHashMap = message.getMessageParameters().get(key); - if (individualHashMap != null && (individualHashMap.get("type").equals("user") || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call"))) { - messageString = DisplayUtils.searchAndColor(messageString, "@" + individualHashMap.get("name"), mentionColor); - } - } + if (message.getMessageParameters() != null && message.getMessageParameters().size() > 0) { + for (String key : message.getMessageParameters().keySet()) { + Map individualHashMap = message.getMessageParameters().get(key); + if (individualHashMap != null && (individualHashMap.get("type").equals("user") + || individualHashMap.get("type").equals("guest") + || individualHashMap.get("type").equals("call"))) { + messageString = + DisplayUtils.searchAndColor(messageString, "@" + individualHashMap.get("name"), + mentionColor); } - - text.setText(messageString); + } } + + text.setText(messageString); + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicUnreadNoticeMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicUnreadNoticeMessageViewHolder.java index d5a184d30..9086f4c3c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicUnreadNoticeMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicUnreadNoticeMessageViewHolder.java @@ -24,27 +24,28 @@ import android.view.View; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.stfalcon.chatkit.messages.MessageHolders; -public class MagicUnreadNoticeMessageViewHolder extends MessageHolders.SystemMessageViewHolder { +public class MagicUnreadNoticeMessageViewHolder + extends MessageHolders.SystemMessageViewHolder { - public MagicUnreadNoticeMessageViewHolder(View itemView) { - super(itemView); - } + public MagicUnreadNoticeMessageViewHolder(View itemView) { + super(itemView); + } - public MagicUnreadNoticeMessageViewHolder(View itemView, Object payload) { - super(itemView, payload); - } + public MagicUnreadNoticeMessageViewHolder(View itemView, Object payload) { + super(itemView, payload); + } - @Override - public void viewDetached() { - messagesListAdapter.deleteById("-1"); - } + @Override + public void viewDetached() { + messagesListAdapter.deleteById("-1"); + } - @Override - public void viewAttached() { - } + @Override + public void viewAttached() { + } - @Override - public void viewRecycled() { + @Override + public void viewRecycled() { - } + } } diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index ca583b251..eed08d541 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -54,36 +54,37 @@ import retrofit2.http.Url; public interface NcApi { - /* - QueryMap items are as follows: - - "format" : "json" - - "search" : "" - - "perPage" : "200" - - "itemType" : "call" + /* + QueryMap items are as follows: + - "format" : "json" + - "search" : "" + - "perPage" : "200" + - "itemType" : "call" - Server URL is: baseUrl + ocsApiVersion + /apps/files_sharing/api/v1/sharees + Server URL is: baseUrl + ocsApiVersion + /apps/files_sharing/api/v1/sharees - or if we're on 14 and up: + or if we're on 14 and up: - baseUrl + ocsApiVersion + "/core/autocomplete/get"); + baseUrl + ocsApiVersion + "/core/autocomplete/get"); - */ - @GET - Observable getContactsWithSearchParam(@Header("Authorization") String authorization, @Url String url, - @Nullable @Query("shareTypes[]") List listOfShareTypes, @QueryMap Map options); + */ + @GET + Observable getContactsWithSearchParam(@Header("Authorization") String authorization, + @Url String url, + @Nullable @Query("shareTypes[]") List listOfShareTypes, + @QueryMap Map options); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room + */ + @GET + Observable getRooms(@Header("Authorization") String authorization, @Url String url); - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room - */ - @GET - Observable getRooms(@Header("Authorization") String authorization, @Url String url); - - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken - */ - @GET - Observable getRoom(@Header("Authorization") String authorization, @Url String url); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken + */ + @GET + Observable getRoom(@Header("Authorization") String authorization, @Url String url); /* QueryMap items are as follows: @@ -93,9 +94,9 @@ public interface NcApi { Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room */ - @POST - Observable createRoom(@Header("Authorization") String authorization, @Url String url, - @QueryMap Map options); + @POST + Observable createRoom(@Header("Authorization") String authorization, @Url String url, + @QueryMap Map options); /* QueryMap items are as follows: @@ -104,109 +105,126 @@ public interface NcApi { Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken */ - @FormUrlEncoded - @PUT - Observable renameRoom(@Header("Authorization") String authorization, @Url String url, - @Field("roomName") String roomName); + @FormUrlEncoded + @PUT + Observable renameRoom(@Header("Authorization") String authorization, + @Url String url, + @Field("roomName") String roomName); + /* + QueryMap items are as follows: + - "newParticipant" : "user" - /* - QueryMap items are as follows: - - "newParticipant" : "user" + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/participants + */ + @POST + Observable addParticipant(@Header("Authorization") String authorization, + @Url String url, + @QueryMap Map options); - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/participants - */ - @POST - Observable addParticipant(@Header("Authorization") String authorization, @Url String url, - @QueryMap Map options); + // also used for removing a guest from a conversation + @DELETE + Observable removeParticipantFromConversation( + @Header("Authorization") String authorization, @Url String url, + @Query("participant") String participantId); + @POST + Observable promoteUserToModerator(@Header("Authorization") String authorization, + @Url String url, @Query("participant") String participantId); - // also used for removing a guest from a conversation - @DELETE - Observable removeParticipantFromConversation(@Header("Authorization") String authorization, @Url String url, @Query("participant") String participantId); - - @POST - Observable promoteUserToModerator(@Header("Authorization") String authorization, @Url String url, @Query("participant") String participantId); - - @DELETE - Observable demoteModeratorToUser(@Header("Authorization") String authorization, @Url String url, @Query("participant") String participantId); + @DELETE + Observable demoteModeratorToUser(@Header("Authorization") String authorization, + @Url String url, @Query("participant") String participantId); /* Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/participants/self */ - @DELETE - Observable removeSelfFromRoom(@Header("Authorization") String authorization, @Url String url); + @DELETE + Observable removeSelfFromRoom(@Header("Authorization") String authorization, + @Url String url); - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/public - */ - @POST - Observable makeRoomPublic(@Header("Authorization") String authorization, @Url String url); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/public + */ + @POST + Observable makeRoomPublic(@Header("Authorization") String authorization, + @Url String url); - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/public - */ - @DELETE - Observable makeRoomPrivate(@Header("Authorization") String authorization, @Url String url); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken/public + */ + @DELETE + Observable makeRoomPrivate(@Header("Authorization") String authorization, + @Url String url); - @DELETE - Observable deleteRoom(@Header("Authorization") String authorization, @Url String url); + @DELETE + Observable deleteRoom(@Header("Authorization") String authorization, + @Url String url); - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken - */ - @GET - Observable getPeersForCall(@Header("Authorization") String authorization, @Url String url); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken + */ + @GET + Observable getPeersForCall(@Header("Authorization") String authorization, + @Url String url); - @FormUrlEncoded - @POST - Observable joinRoom(@Nullable @Header("Authorization") String authorization, @Url String url, @Nullable @Field("password") String password); + @FormUrlEncoded + @POST + Observable joinRoom(@Nullable @Header("Authorization") String authorization, + @Url String url, @Nullable @Field("password") String password); - @DELETE - Observable leaveRoom(@Nullable @Header("Authorization") String authorization, @Url String url); + @DELETE + Observable leaveRoom(@Nullable @Header("Authorization") String authorization, + @Url String url); /* Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken */ - @POST - Observable joinCall(@Nullable @Header("Authorization") String authorization, @Url String url); + @POST + Observable joinCall(@Nullable @Header("Authorization") String authorization, + @Url String url); - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken - */ - @DELETE - Observable leaveCall(@Nullable @Header("Authorization") String authorization, @Url String url); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken + */ + @DELETE + Observable leaveCall(@Nullable @Header("Authorization") String authorization, + @Url String url); - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken/ping - */ - @POST - Observable pingCall(@Nullable @Header("Authorization") String authorization, @Url String url); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken/ping + */ + @POST + Observable pingCall(@Nullable @Header("Authorization") String authorization, + @Url String url); - @GET - Observable getSignalingSettings(@Nullable @Header("Authorization") String authorization, - @Url String url); + @GET + Observable getSignalingSettings( + @Nullable @Header("Authorization") String authorization, + @Url String url); - /* - QueryMap items are as follows: - - "messages" : "message" + /* + QueryMap items are as follows: + - "messages" : "message" - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /signaling - */ - @FormUrlEncoded - @POST - Observable sendSignalingMessages(@Nullable @Header("Authorization") String authorization, @Url String url, - @Field("messages") String messages); + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /signaling + */ + @FormUrlEncoded + @POST + Observable sendSignalingMessages( + @Nullable @Header("Authorization") String authorization, @Url String url, + @Field("messages") String messages); - /* - Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /signaling - */ - @GET - Observable pullSignalingMessages(@Nullable @Header("Authorization") String authorization, @Url - String - url); + /* + Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /signaling + */ + @GET + Observable pullSignalingMessages( + @Nullable @Header("Authorization") String authorization, @Url + String + url); /* QueryMap items are as follows: @@ -215,14 +233,15 @@ public interface NcApi { Server URL is: baseUrl + ocsApiVersion + "/cloud/user" */ - @GET - Observable getUserProfile(@Header("Authorization") String authorization, @Url String url); + @GET + Observable getUserProfile(@Header("Authorization") String authorization, + @Url String url); - /* - Server URL is: baseUrl + /status.php - */ - @GET - Observable getServerStatus(@Url String url); + /* + Server URL is: baseUrl + /status.php + */ + @GET + Observable getServerStatus(@Url String url); /* @@ -235,52 +254,55 @@ public interface NcApi { Server URL is: baseUrl + ocsApiVersion + "/apps/notifications/api/v2/push */ - @POST - Observable registerDeviceForNotificationsWithNextcloud(@Header("Authorization") - String authorization, - @Url String url, - @QueryMap Map options); + @POST + Observable registerDeviceForNotificationsWithNextcloud( + @Header("Authorization") + String authorization, + @Url String url, + @QueryMap Map options); - @DELETE - Observable unregisterDeviceForNotificationsWithNextcloud(@Header("Authorization") - String authorization, - @Url String url); + @DELETE + Observable unregisterDeviceForNotificationsWithNextcloud(@Header("Authorization") + String authorization, + @Url String url); - @FormUrlEncoded - @POST - Observable registerDeviceForNotificationsWithProxy(@Url String url, - @FieldMap Map fields); + @FormUrlEncoded + @POST + Observable registerDeviceForNotificationsWithProxy(@Url String url, + @FieldMap Map fields); + /* + QueryMap items are as follows: + - "deviceIdentifier": "{{deviceIdentifier}}", + - "deviceIdentifierSignature": "{{signature}}", + - "userPublicKey": "{{userPublicKey}}" + */ + @DELETE + Observable unregisterDeviceForNotificationsWithProxy(@Url String url, + @QueryMap Map fields); - /* - QueryMap items are as follows: - - "deviceIdentifier": "{{deviceIdentifier}}", - - "deviceIdentifierSignature": "{{signature}}", - - "userPublicKey": "{{userPublicKey}}" - */ - @DELETE - Observable unregisterDeviceForNotificationsWithProxy(@Url String url, - @QueryMap Map fields); + @FormUrlEncoded + @PUT + Observable setPassword(@Header("Authorization") String authorization, + @Url String url, + @Field("password") String password); - @FormUrlEncoded - @PUT - Observable setPassword(@Header("Authorization") String authorization, @Url String url, - @Field("password") String password); + @GET + Observable getCapabilities(@Header("Authorization") String authorization, + @Url String url); - @GET - Observable getCapabilities(@Header("Authorization") String authorization, @Url String url); - - /* - QueryMap items are as follows: - - "lookIntoFuture": int (0 or 1), - - "limit" : int, range 100-200, - - "timeout": used with look into future, 30 default, 60 at most - - "lastKnownMessageId", int, use one from X-Chat-Last-Given - */ - @GET - Observable> pullChatMessages(@Header("Authorization") String authorization, @Url String url, - @QueryMap Map fields); + /* + QueryMap items are as follows: + - "lookIntoFuture": int (0 or 1), + - "limit" : int, range 100-200, + - "timeout": used with look into future, 30 default, 60 at most + - "lastKnownMessageId", int, use one from X-Chat-Last-Given + */ + @GET + Observable> pullChatMessages(@Header("Authorization") String authorization, + @Url String url, + @QueryMap Map fields); /* Fieldmap items are as follows: @@ -288,51 +310,56 @@ public interface NcApi { - "actorDisplayName" */ - @FormUrlEncoded - @POST - Observable sendChatMessage(@Header("Authorization") String authorization, @Url String url, - @Field("message") CharSequence message, - @Field("actorDisplayName") String actorDisplayName); + @FormUrlEncoded + @POST + Observable sendChatMessage(@Header("Authorization") String authorization, + @Url String url, + @Field("message") CharSequence message, + @Field("actorDisplayName") String actorDisplayName); - @GET - Observable getMentionAutocompleteSuggestions(@Header("Authorization") String authorization, - @Url String url, @Query("search") String query, - @Nullable @Query("limit") Integer limit); + @GET + Observable getMentionAutocompleteSuggestions( + @Header("Authorization") String authorization, + @Url String url, @Query("search") String query, + @Nullable @Query("limit") Integer limit); - // Url is: /api/{apiVersion}/room/{token}/pin - @POST - Observable addConversationToFavorites(@Header("Authorization") String authorization, - @Url String url); + // Url is: /api/{apiVersion}/room/{token}/pin + @POST + Observable addConversationToFavorites( + @Header("Authorization") String authorization, + @Url String url); - // Url is: /api/{apiVersion}/room/{token}/favorites - @DELETE - Observable removeConversationFromFavorites(@Header("Authorization") String authorization, - @Url String url); + // Url is: /api/{apiVersion}/room/{token}/favorites + @DELETE + Observable removeConversationFromFavorites( + @Header("Authorization") String authorization, + @Url String url); - @GET - Observable getNotification(@Header("Authorization") String authorization, - @Url String url); + @GET + Observable getNotification(@Header("Authorization") String authorization, + @Url String url); - @FormUrlEncoded - @POST - Observable setNotificationLevel(@Header("Authorization") String authorization, @Url String url, @Field("level") int level); + @FormUrlEncoded + @POST + Observable setNotificationLevel(@Header("Authorization") String authorization, + @Url String url, @Field("level") int level); - @FormUrlEncoded - @PUT - Observable setReadOnlyState(@Header("Authorization") String authorization, @Url String url, @Field("viewState") int state); + @FormUrlEncoded + @PUT + Observable setReadOnlyState(@Header("Authorization") String authorization, + @Url String url, @Field("viewState") int state); + @FormUrlEncoded + @POST + Observable createRemoteShare(@Nullable @Header("Authorization") String authorization, + @Url String url, + @Field("path") String remotePath, + @Field("shareWith") String roomToken, + @Field("shareType") String shareType); - @FormUrlEncoded - @POST - Observable createRemoteShare(@Nullable @Header("Authorization") String authorization, @Url String url, - @Field("path") String remotePath, - @Field("shareWith") String roomToken, - @Field("shareType") String shareType); - - @FormUrlEncoded - @PUT - Observable setLobbyForConversation(@Header("Authorization") String authorization, - @Url String url, @Field("viewState") Integer state, - @Field("timer") Long timer); - + @FormUrlEncoded + @PUT + Observable setLobbyForConversation(@Header("Authorization") String authorization, + @Url String url, @Field("viewState") Integer state, + @Field("timer") Long timer); } diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt index 0adfabe85..863168a26 100644 --- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt +++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt @@ -29,7 +29,6 @@ import androidx.emoji.bundled.BundledEmojiCompatConfig import androidx.emoji.text.EmojiCompat import androidx.lifecycle.LifecycleObserver import androidx.multidex.MultiDex -import androidx.multidex.MultiDexApplication import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.OneTimeWorkRequest import androidx.work.PeriodicWorkRequest @@ -54,7 +53,6 @@ import com.nextcloud.talk.newarch.di.module.NetworkModule import com.nextcloud.talk.newarch.di.module.StorageModule import com.nextcloud.talk.newarch.features.conversationsList.di.module.ConversationsListModule import com.nextcloud.talk.utils.ClosedInterfaceImpl -import com.nextcloud.talk.utils.DeviceUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.OkHttpNetworkFetcherWithCache import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageModule @@ -78,150 +76,174 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton -@AutoComponent(modules = [BusModule::class, ContextModule::class, DatabaseModule::class, RestModule::class, UserModule::class, ArbitraryStorageModule::class]) +@AutoComponent( + modules = [BusModule::class, ContextModule::class, DatabaseModule::class, RestModule::class, UserModule::class, ArbitraryStorageModule::class] +) @Singleton @AutoInjector(NextcloudTalkApplication::class) class NextcloudTalkApplication : Application(), LifecycleObserver { - //region Fields (components) - lateinit var componentApplication: NextcloudTalkApplicationComponent - private set - //endregion + //region Fields (components) + lateinit var componentApplication: NextcloudTalkApplicationComponent + private set + //endregion - //region Getters + //region Getters - @Inject - lateinit var appPreferences: AppPreferences - @Inject - lateinit var okHttpClient: OkHttpClient - //endregion + @Inject + lateinit var appPreferences: AppPreferences + @Inject + lateinit var okHttpClient: OkHttpClient + //endregion - //region private methods - private fun initializeWebRtc() { - try { - if (MagicWebRTCUtils.HARDWARE_AEC_BLACKLIST.contains(Build.MODEL)) { - WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true) - } + //region private methods + private fun initializeWebRtc() { + try { + if (MagicWebRTCUtils.HARDWARE_AEC_BLACKLIST.contains(Build.MODEL)) { + WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true) + } - if (!MagicWebRTCUtils.OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) { - WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true) - } + if (!MagicWebRTCUtils.OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) { + WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true) + } - PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(this) - .setEnableVideoHwAcceleration(MagicWebRTCUtils.shouldEnableVideoHardwareAcceleration()) - .createInitializationOptions()) - } catch (e: UnsatisfiedLinkError) { - Log.w(TAG, e) - } - + PeerConnectionFactory.initialize( + PeerConnectionFactory.InitializationOptions.builder(this) + .setEnableVideoHwAcceleration( + MagicWebRTCUtils.shouldEnableVideoHardwareAcceleration() + ) + .createInitializationOptions() + ) + } catch (e: UnsatisfiedLinkError) { + Log.w(TAG, e) } - //endregion + } - //region Overridden methods - override fun onCreate() { - sharedApplication = this + //endregion - val securityKeyManager = SecurityKeyManager.getInstance() - val securityKeyConfig = SecurityKeyManagerConfig.Builder() - .setEnableDebugLogging(BuildConfig.DEBUG) + //region Overridden methods + override fun onCreate() { + sharedApplication = this + + val securityKeyManager = SecurityKeyManager.getInstance() + val securityKeyConfig = SecurityKeyManagerConfig.Builder() + .setEnableDebugLogging(BuildConfig.DEBUG) + .build() + securityKeyManager.init(this, securityKeyConfig) + + initializeWebRtc() + DisplayUtils.useCompatVectorIfNeeded() + buildComponent() + DavUtils.registerCustomFactories() + + componentApplication.inject(this) + + setAppTheme(appPreferences.theme) + super.onCreate() + + val imagePipelineConfig = ImagePipelineConfig.newBuilder(this) + .setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient)) + .setMainDiskCacheConfig( + DiskCacheConfig.newBuilder(this) + .setMaxCacheSize(0) + .setMaxCacheSizeOnLowDiskSpace(0) + .setMaxCacheSizeOnVeryLowDiskSpace(0) .build() - securityKeyManager.init(this, securityKeyConfig) + ) + .build() - initializeWebRtc() - DisplayUtils.useCompatVectorIfNeeded() - buildComponent() - DavUtils.registerCustomFactories() + Fresco.initialize(this, imagePipelineConfig) + Security.insertProviderAt(Conscrypt.newProvider(), 1) - componentApplication.inject(this) + ClosedInterfaceImpl().providerInstallerInstallIfNeededAsync() - setAppTheme(appPreferences.theme) - super.onCreate() + val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java) + .build() + val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java) + .build() + val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder( + CapabilitiesWorker::class.java, + 12, TimeUnit.HOURS + ) + .build() + val capabilitiesUpdateWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java) + .build() + val signalingSettingsWork = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java) + .build() - val imagePipelineConfig = ImagePipelineConfig.newBuilder(this) - .setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient)) - .setMainDiskCacheConfig(DiskCacheConfig.newBuilder(this) - .setMaxCacheSize(0) - .setMaxCacheSizeOnLowDiskSpace(0) - .setMaxCacheSizeOnVeryLowDiskSpace(0) - .build()) - .build() + WorkManager.getInstance() + .enqueue(pushRegistrationWork) + WorkManager.getInstance() + .enqueue(accountRemovalWork) + WorkManager.getInstance() + .enqueue(capabilitiesUpdateWork) + WorkManager.getInstance() + .enqueue(signalingSettingsWork) + WorkManager.getInstance() + .enqueueUniquePeriodicWork( + "DailyCapabilitiesUpdateWork", ExistingPeriodicWorkPolicy.REPLACE, + periodicCapabilitiesUpdateWork + ) - Fresco.initialize(this, imagePipelineConfig) - Security.insertProviderAt(Conscrypt.newProvider(), 1) + val config = BundledEmojiCompatConfig(this) + config.setReplaceAll(true) + val emojiCompat = EmojiCompat.init(config) - ClosedInterfaceImpl().providerInstallerInstallIfNeededAsync() + EmojiManager.install(GoogleCompatEmojiProvider(emojiCompat)) + } - val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build() - val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() - val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(CapabilitiesWorker::class.java, - 12, TimeUnit.HOURS).build() - val capabilitiesUpdateWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java).build() - val signalingSettingsWork = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java).build() + override fun onTerminate() { + super.onTerminate() + sharedApplication = null + } + //endregion - WorkManager.getInstance().enqueue(pushRegistrationWork) - WorkManager.getInstance().enqueue(accountRemovalWork) - WorkManager.getInstance().enqueue(capabilitiesUpdateWork) - WorkManager.getInstance().enqueue(signalingSettingsWork) - WorkManager.getInstance().enqueueUniquePeriodicWork("DailyCapabilitiesUpdateWork", ExistingPeriodicWorkPolicy.REPLACE, periodicCapabilitiesUpdateWork) + //region Protected methods + protected fun buildComponent() { + componentApplication = DaggerNextcloudTalkApplicationComponent.builder() + .busModule(BusModule()) + .contextModule(ContextModule(applicationContext)) + .databaseModule(DatabaseModule()) + .restModule(RestModule(applicationContext)) + .userModule(UserModule()) + .arbitraryStorageModule(ArbitraryStorageModule()) + .build() - val config = BundledEmojiCompatConfig(this) - config.setReplaceAll(true) - val emojiCompat = EmojiCompat.init(config) - - EmojiManager.install(GoogleCompatEmojiProvider(emojiCompat)) + startKoin { + androidContext(this@NextcloudTalkApplication) + androidLogger() + modules(listOf(CommunicationModule, StorageModule, NetworkModule, ConversationsListModule)) } + } - override fun onTerminate() { - super.onTerminate() - sharedApplication = null - } + override fun attachBaseContext(base: Context) { + super.attachBaseContext(base) + MultiDex.install(this) + } + + companion object { + private val TAG = NextcloudTalkApplication::class.java.simpleName + //region Singleton //endregion - - //region Protected methods - protected fun buildComponent() { - componentApplication = DaggerNextcloudTalkApplicationComponent.builder() - .busModule(BusModule()) - .contextModule(ContextModule(applicationContext)) - .databaseModule(DatabaseModule()) - .restModule(RestModule(applicationContext)) - .userModule(UserModule()) - .arbitraryStorageModule(ArbitraryStorageModule()) - .build() - - startKoin { - androidContext(this@NextcloudTalkApplication) - androidLogger() - modules(listOf(CommunicationModule, StorageModule, NetworkModule, ConversationsListModule)) - } - } - - override fun attachBaseContext(base: Context) { - super.attachBaseContext(base) - MultiDex.install(this) - } - - companion object { - private val TAG = NextcloudTalkApplication::class.java.simpleName - //region Singleton - //endregion - - var sharedApplication: NextcloudTalkApplication? = null - protected set - //endregion - - //region Setters - fun setAppTheme(theme: String) { - when (theme) { - "night_no" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - "night_yes" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - "battery_saver" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY) - else -> - // will be "follow_system" only for now - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) - } - } - } + var sharedApplication: NextcloudTalkApplication? = null + protected set //endregion + + //region Setters + fun setAppTheme(theme: String) { + when (theme) { + "night_no" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + "night_yes" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + "battery_saver" -> AppCompatDelegate.setDefaultNightMode( + AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY + ) + else -> + // will be "follow_system" only for now + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + } + } + } + //endregion } diff --git a/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java b/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java index d27e06120..23ca7855c 100644 --- a/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java +++ b/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java @@ -25,9 +25,9 @@ import android.text.Editable; import android.text.Spanned; import android.widget.EditText; import com.facebook.widget.text.span.BetterImageSpan; -import com.nextcloud.talk.models.json.mention.Mention; import com.nextcloud.talk.R; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.mention.Mention; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.MagicCharPolicy; import com.nextcloud.talk.utils.text.Spans; @@ -36,44 +36,44 @@ import com.vanniktech.emoji.EmojiRange; import com.vanniktech.emoji.EmojiUtils; public class MentionAutocompleteCallback implements AutocompleteCallback { - private Context context; - private UserEntity conversationUser; - private EditText editText; + private Context context; + private UserEntity conversationUser; + private EditText editText; - public MentionAutocompleteCallback(Context context, UserEntity conversationUser, - EditText editText) { - this.context = context; - this.conversationUser = conversationUser; - this.editText = editText; + public MentionAutocompleteCallback(Context context, UserEntity conversationUser, + EditText editText) { + this.context = context; + this.conversationUser = conversationUser; + this.editText = editText; + } + + @Override + public boolean onPopupItemClicked(Editable editable, Mention item) { + int[] range = MagicCharPolicy.getQueryRange(editable); + if (range == null) return false; + int start = range[0]; + int end = range[1]; + String replacement = item.getLabel(); + + StringBuilder replacementStringBuilder = new StringBuilder(item.getLabel()); + for (EmojiRange emojiRange : EmojiUtils.emojis(replacement)) { + replacementStringBuilder.delete(emojiRange.start, emojiRange.end); } - @Override - public boolean onPopupItemClicked(Editable editable, Mention item) { - int[] range = MagicCharPolicy.getQueryRange(editable); - if (range == null) return false; - int start = range[0]; - int end = range[1]; - String replacement = item.getLabel(); + editable.replace(start, end, replacementStringBuilder.toString() + " "); + Spans.MentionChipSpan mentionChipSpan = + new Spans.MentionChipSpan(DisplayUtils.getDrawableForMentionChipSpan(context, + item.getId(), item.getLabel(), conversationUser, item.getSource(), + R.xml.chip_you, editText), + BetterImageSpan.ALIGN_CENTER, + item.getId(), item.getLabel()); + editable.setSpan(mentionChipSpan, start, start + replacementStringBuilder.toString().length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + return true; + } - StringBuilder replacementStringBuilder = new StringBuilder(item.getLabel()); - for(EmojiRange emojiRange : EmojiUtils.emojis(replacement)) { - replacementStringBuilder.delete(emojiRange.start, emojiRange.end); - } + @Override + public void onPopupVisibilityChanged(boolean shown) { - editable.replace(start, end, replacementStringBuilder.toString() + " "); - Spans.MentionChipSpan mentionChipSpan = - new Spans.MentionChipSpan(DisplayUtils.getDrawableForMentionChipSpan(context, - item.getId(), item.getLabel(), conversationUser, item.getSource(), - R.xml.chip_you, editText), - BetterImageSpan.ALIGN_CENTER, - item.getId(), item.getLabel()); - editable.setSpan(mentionChipSpan, start, start + replacementStringBuilder.toString().length(), - Spanned.SPAN_INCLUSIVE_INCLUSIVE); - return true; - } - - @Override - public void onPopupVisibilityChanged(boolean shown) { - - } + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/adapters/items/BrowserFileItem.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/adapters/items/BrowserFileItem.java index 8a83fb653..faeaf13d1 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/adapters/items/BrowserFileItem.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/adapters/items/BrowserFileItem.java @@ -50,136 +50,143 @@ import java.util.List; import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) -public class BrowserFileItem extends AbstractFlexibleItem implements IFilterable { - @Inject - Context context; - private BrowserFile browserFile; - private UserEntity activeUser; - private SelectionInterface selectionInterface; - private boolean selected; +public class BrowserFileItem extends AbstractFlexibleItem + implements IFilterable { + @Inject + Context context; + private BrowserFile browserFile; + private UserEntity activeUser; + private SelectionInterface selectionInterface; + private boolean selected; - public BrowserFileItem(BrowserFile browserFile, UserEntity activeUser, SelectionInterface selectionInterface) { - this.browserFile = browserFile; - this.activeUser = activeUser; - this.selectionInterface = selectionInterface; - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public BrowserFileItem(BrowserFile browserFile, UserEntity activeUser, + SelectionInterface selectionInterface) { + this.browserFile = browserFile; + this.activeUser = activeUser; + this.selectionInterface = selectionInterface; + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } + + @Override + public boolean equals(Object o) { + if (o instanceof BrowserFileItem) { + BrowserFileItem inItem = (BrowserFileItem) o; + return browserFile.getPath().equals(inItem.getModel().getPath()); } - @Override - public boolean equals(Object o) { - if (o instanceof BrowserFileItem) { - BrowserFileItem inItem = (BrowserFileItem) o; - return browserFile.getPath().equals(inItem.getModel().getPath()); + return false; + } + + public BrowserFile getModel() { + return browserFile; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_browser_file; + } + + @Override + public ViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new ViewHolder(view, adapter); + } + + private boolean isSelected() { + return selected; + } + + private void setSelected(boolean selected) { + this.selected = selected; + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, ViewHolder holder, int position, + List payloads) { + holder.fileIconImageView.setController(null); + + if (browserFile.isEncrypted()) { + holder.fileEncryptedImageView.setVisibility(View.VISIBLE); + holder.itemView.setEnabled(false); + holder.itemView.setAlpha(0.38f); + } else { + holder.fileEncryptedImageView.setVisibility(View.GONE); + holder.itemView.setEnabled(true); + holder.itemView.setAlpha(1.0f); + } + + if (browserFile.isFavorite()) { + holder.fileFavoriteImageView.setVisibility(View.VISIBLE); + } else { + holder.fileFavoriteImageView.setVisibility(View.GONE); + } + + holder.fileIconImageView.getHierarchy() + .setPlaceholderImage(context.getDrawable( + DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(browserFile.getMimeType()))); + + if (browserFile.isHasPreview()) { + String path = ApiUtils.getUrlForFilePreviewWithRemotePath(activeUser.getBaseUrl(), + browserFile.getPath(), + context.getResources().getDimensionPixelSize(R.dimen.small_item_height)); + + if (path.length() > 0) { + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(path, null)) + .build(); + holder.fileIconImageView.setController(draweeController); + } + } + + holder.filenameTextView.setText(browserFile.getDisplayName()); + holder.fileModifiedTextView.setText(String.format(context.getString(R.string.nc_last_modified), + Formatter.formatShortFileSize(context, browserFile.getSize()), + DateUtils.INSTANCE.getLocalDateTimeStringFromTimestamp( + browserFile.getModifiedTimestamp()))); + setSelected(selectionInterface.isPathSelected(browserFile.getPath())); + holder.selectFileCheckbox.setChecked(isSelected()); + + if (!browserFile.isEncrypted()) { + holder.selectFileCheckbox.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (((CheckBox) v).isChecked() != isSelected()) { + setSelected(((CheckBox) v).isChecked()); + selectionInterface.toggleBrowserItemSelection(browserFile.getPath()); + } } - - return false; + }); } - public BrowserFile getModel() { - return browserFile; - } - - @Override - public int getLayoutRes() { - return R.layout.rv_item_browser_file; - } - - @Override - public ViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new ViewHolder(view, adapter); - - } - - private boolean isSelected() { - return selected; - } - - private void setSelected(boolean selected) { - this.selected = selected; - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, ViewHolder holder, int position, List payloads) { - holder.fileIconImageView.setController(null); - - if (browserFile.isEncrypted()) { - holder.fileEncryptedImageView.setVisibility(View.VISIBLE); - holder.itemView.setEnabled(false); - holder.itemView.setAlpha(0.38f); - } else { - holder.fileEncryptedImageView.setVisibility(View.GONE); - holder.itemView.setEnabled(true); - holder.itemView.setAlpha(1.0f); - } - - if (browserFile.isFavorite()) { - holder.fileFavoriteImageView.setVisibility(View.VISIBLE); - } else { - holder.fileFavoriteImageView.setVisibility(View.GONE); - } - - holder.fileIconImageView.getHierarchy().setPlaceholderImage(context.getDrawable(DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(browserFile.getMimeType()))); - - if (browserFile.isHasPreview()) { - String path = ApiUtils.getUrlForFilePreviewWithRemotePath(activeUser.getBaseUrl(), - browserFile.getPath(), - context.getResources().getDimensionPixelSize(R.dimen.small_item_height)); - - if (path.length() > 0) { - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(path, null)) - .build(); - holder.fileIconImageView.setController(draweeController); - } - } - - holder.filenameTextView.setText(browserFile.getDisplayName()); - holder.fileModifiedTextView.setText(String.format(context.getString(R.string.nc_last_modified), - Formatter.formatShortFileSize(context, browserFile.getSize()), - DateUtils.INSTANCE.getLocalDateTimeStringFromTimestamp(browserFile.getModifiedTimestamp()))); - setSelected(selectionInterface.isPathSelected(browserFile.getPath())); - holder.selectFileCheckbox.setChecked(isSelected()); - - if (!browserFile.isEncrypted()) { - holder.selectFileCheckbox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (((CheckBox) v).isChecked() != isSelected()) { - setSelected(((CheckBox) v).isChecked()); - selectionInterface.toggleBrowserItemSelection(browserFile.getPath()); - } - } - }); - } - - holder.filenameTextView.setSelected(true); - holder.fileModifiedTextView.setSelected(true); - } - - @Override - public boolean filter(String constraint) { - return false; - } - - static class ViewHolder extends FlexibleViewHolder { - - @BindView(R.id.file_icon) - public SimpleDraweeView fileIconImageView; - @BindView(R.id.file_modified_info) - public TextView fileModifiedTextView; - @BindView(R.id.filename_text_view) - public TextView filenameTextView; - @BindView(R.id.select_file_checkbox) - public CheckBox selectFileCheckbox; - @BindView(R.id.fileEncryptedImageView) - public ImageView fileEncryptedImageView; - @BindView(R.id.fileFavoriteImageView) - public ImageView fileFavoriteImageView; - - ViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } + holder.filenameTextView.setSelected(true); + holder.fileModifiedTextView.setSelected(true); + } + + @Override + public boolean filter(String constraint) { + return false; + } + + static class ViewHolder extends FlexibleViewHolder { + + @BindView(R.id.file_icon) + public SimpleDraweeView fileIconImageView; + @BindView(R.id.file_modified_info) + public TextView fileModifiedTextView; + @BindView(R.id.filename_text_view) + public TextView filenameTextView; + @BindView(R.id.select_file_checkbox) + public CheckBox selectFileCheckbox; + @BindView(R.id.fileEncryptedImageView) + public ImageView fileEncryptedImageView; + @BindView(R.id.fileFavoriteImageView) + public ImageView fileFavoriteImageView; + + ViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java index d788c56c7..ac37945b3 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java @@ -71,278 +71,281 @@ import org.parceler.Parcels; @AutoInjector(NextcloudTalkApplication.class) public class BrowserController extends BaseController implements ListingInterface, - FlexibleAdapter.OnItemClickListener, SelectionInterface { - private final Set selectedPaths; - @Inject - UserUtils userUtils; - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - @BindView(R.id.fast_scroller) - FastScroller fastScroller; - @BindView(R.id.action_back) - BottomNavigationItemView backMenuItem; - @BindView(R.id.action_refresh) - BottomNavigationItemView actionRefreshMenuItem; - @Inject - Context context; - @Inject - OkHttpClient okHttpClient; + FlexibleAdapter.OnItemClickListener, SelectionInterface { + private final Set selectedPaths; + @Inject + UserUtils userUtils; + @BindView(R.id.recyclerView) + RecyclerView recyclerView; + @BindView(R.id.fast_scroller) + FastScroller fastScroller; + @BindView(R.id.action_back) + BottomNavigationItemView backMenuItem; + @BindView(R.id.action_refresh) + BottomNavigationItemView actionRefreshMenuItem; + @Inject + Context context; + @Inject + OkHttpClient okHttpClient; - private MenuItem filesSelectionDoneMenuItem; - private RecyclerView.LayoutManager layoutManager; + private MenuItem filesSelectionDoneMenuItem; + private RecyclerView.LayoutManager layoutManager; - private FlexibleAdapter adapter; - private List recyclerViewItems = new ArrayList<>(); + private FlexibleAdapter adapter; + private List recyclerViewItems = new ArrayList<>(); - private ListingAbstractClass listingAbstractClass; - private BrowserType browserType; - private String currentPath; - private UserEntity activeUser; - private String roomToken; + private ListingAbstractClass listingAbstractClass; + private BrowserType browserType; + private String currentPath; + private UserEntity activeUser; + private String roomToken; - public BrowserController(Bundle args) { - super(); - setHasOptionsMenu(true); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - browserType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_BROWSER_TYPE())); - activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY())); - roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); + public BrowserController(Bundle args) { + super(); + setHasOptionsMenu(true); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + browserType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_BROWSER_TYPE())); + activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY())); + roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); - currentPath = "/"; - if (BrowserType.DAV_BROWSER.equals(browserType)) { - listingAbstractClass = new DavListing(this); - } else { - //listingAbstractClass = new LocalListing(this); + currentPath = "/"; + if (BrowserType.DAV_BROWSER.equals(browserType)) { + listingAbstractClass = new DavListing(this); + } else { + //listingAbstractClass = new LocalListing(this); + } + + selectedPaths = Collections.synchronizedSet(new TreeSet<>()); + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_browser, container, false); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + if (adapter == null) { + adapter = new FlexibleAdapter<>(recyclerViewItems, context, false); + } + + changeEnabledStatusForBarItems(true); + prepareViews(); + } + + private void onFileSelectionDone() { + synchronized (selectedPaths) { + Iterator iterator = selectedPaths.iterator(); + + List paths = new ArrayList<>(); + Data data; + OneTimeWorkRequest shareWorker; + + while (iterator.hasNext()) { + String path = iterator.next(); + paths.add(path); + iterator.remove(); + if (paths.size() == 10 || !iterator.hasNext()) { + data = new Data.Builder() + .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), activeUser.getId()) + .putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), roomToken) + .putStringArray(BundleKeys.INSTANCE.getKEY_FILE_PATHS(), paths.toArray(new String[0])) + .build(); + shareWorker = new OneTimeWorkRequest.Builder(ShareOperationWorker.class) + .setInputData(data) + .build(); + WorkManager.getInstance().enqueue(shareWorker); + paths = new ArrayList<>(); } - - selectedPaths = Collections.synchronizedSet(new TreeSet<>()); + } } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_browser, container, false); + getRouter().popCurrentController(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_share_files, menu); + filesSelectionDoneMenuItem = menu.findItem(R.id.files_selection_done); + filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case R.id.files_selection_done: + onFileSelectionDone(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + refreshCurrentPath(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + listingAbstractClass.tearDown(); + } + + @Override + public String getTitle() { + return currentPath; + } + + @OnClick(R.id.action_back) + void goBack() { + fetchPath(new File(currentPath).getParent()); + } + + @OnClick(R.id.action_refresh) + void refreshCurrentPath() { + fetchPath(currentPath); + } + + @SuppressLint("RestrictedApi") + private void changeEnabledStatusForBarItems(boolean shouldBeEnabled) { + if (actionRefreshMenuItem != null) { + actionRefreshMenuItem.setEnabled(shouldBeEnabled); } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - if (adapter == null) { - adapter = new FlexibleAdapter<>(recyclerViewItems, context, false); - } + if (backMenuItem != null) { + backMenuItem.setEnabled(shouldBeEnabled && !currentPath.equals("/")); + } + } - changeEnabledStatusForBarItems(true); - prepareViews(); + private void fetchPath(String path) { + listingAbstractClass.cancelAllJobs(); + changeEnabledStatusForBarItems(false); + + listingAbstractClass.getFiles(path, activeUser, + BrowserType.DAV_BROWSER.equals(browserType) ? okHttpClient : null); + } + + @Override + public void listingResult(DavResponse davResponse) { + adapter.clear(); + List fileBrowserItems = new ArrayList<>(); + if (davResponse.getData() != null) { + final List objectList = (List) davResponse.getData(); + + currentPath = objectList.get(0).getPath(); + + if (getActivity() != null) { + getActivity().runOnUiThread(() -> setTitle()); + } + + for (int i = 1; i < objectList.size(); i++) { + fileBrowserItems.add(new BrowserFileItem(objectList.get(i), activeUser, this)); + } } - private void onFileSelectionDone() { - synchronized (selectedPaths) { - Iterator iterator = selectedPaths.iterator(); - - List paths = new ArrayList<>(); - Data data; - OneTimeWorkRequest shareWorker; - - while (iterator.hasNext()) { - String path = iterator.next(); - paths.add(path); - iterator.remove(); - if (paths.size() == 10 || !iterator.hasNext()) { - data = new Data.Builder() - .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), activeUser.getId()) - .putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), roomToken) - .putStringArray(BundleKeys.INSTANCE.getKEY_FILE_PATHS(), paths.toArray(new String[0])) - .build(); - shareWorker = new OneTimeWorkRequest.Builder(ShareOperationWorker.class) - .setInputData(data) - .build(); - WorkManager.getInstance().enqueue(shareWorker); - paths = new ArrayList<>(); - } - } - } - - getRouter().popCurrentController(); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_share_files, menu); - filesSelectionDoneMenuItem = menu.findItem(R.id.files_selection_done); - filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.files_selection_done: - onFileSelectionDone(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - refreshCurrentPath(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - listingAbstractClass.tearDown(); - } - - @Override - public String getTitle() { - return currentPath; - } - - @OnClick(R.id.action_back) - void goBack() { - fetchPath(new File(currentPath).getParent()); - } - - @OnClick(R.id.action_refresh) - void refreshCurrentPath() { - fetchPath(currentPath); - } - - @SuppressLint("RestrictedApi") - private void changeEnabledStatusForBarItems(boolean shouldBeEnabled) { - if (actionRefreshMenuItem != null) { - actionRefreshMenuItem.setEnabled(shouldBeEnabled); - } - - if (backMenuItem != null) { - backMenuItem.setEnabled(shouldBeEnabled && !currentPath.equals("/")); - } - } - - private void fetchPath(String path) { - listingAbstractClass.cancelAllJobs(); - changeEnabledStatusForBarItems(false); - - listingAbstractClass.getFiles(path, activeUser, BrowserType.DAV_BROWSER.equals(browserType) ? okHttpClient : null); - } - - @Override - public void listingResult(DavResponse davResponse) { - adapter.clear(); - List fileBrowserItems = new ArrayList<>(); - if (davResponse.getData() != null) { - final List objectList = (List) davResponse.getData(); - - currentPath = objectList.get(0).getPath(); - - if (getActivity() != null) { - getActivity().runOnUiThread(() -> setTitle()); - } - - for (int i = 1; i < objectList.size(); i++) { - fileBrowserItems.add(new BrowserFileItem(objectList.get(i), activeUser, this)); - } - } - - adapter.addItems(0, fileBrowserItems); - - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - adapter.notifyDataSetChanged(); - changeEnabledStatusForBarItems(true); - - }); - } - } - - private boolean shouldPathBeSelectedDueToParent(String currentPath) { - if (selectedPaths.size() > 0) { - File file = new File(currentPath); - if (!file.getParent().equals("/")) { - while (file.getParent() != null) { - String parent = file.getParent(); - if (new File(file.getParent()).getParent() != null) { - parent += "/"; - } - - if (selectedPaths.contains(parent)) { - return true; - } - - file = new File(file.getParent()); - } - } - } - - return false; - } - - private void checkAndRemoveAnySelectedParents(String currentPath) { - File file = new File(currentPath); - selectedPaths.remove(currentPath); - while (file.getParent() != null) { - selectedPaths.remove(file.getParent() + "/"); - file = new File(file.getParent()); - } + adapter.addItems(0, fileBrowserItems); + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { adapter.notifyDataSetChanged(); + changeEnabledStatusForBarItems(true); + }); } + } - @Override - public boolean onItemClick(View view, int position) { - BrowserFile browserFile = ((BrowserFileItem) adapter.getItem(position)).getModel(); - if ("inode/directory".equals((browserFile.getMimeType()))) { - fetchPath(browserFile.getPath()); + private boolean shouldPathBeSelectedDueToParent(String currentPath) { + if (selectedPaths.size() > 0) { + File file = new File(currentPath); + if (!file.getParent().equals("/")) { + while (file.getParent() != null) { + String parent = file.getParent(); + if (new File(file.getParent()).getParent() != null) { + parent += "/"; + } + + if (selectedPaths.contains(parent)) { return true; - } + } - return false; + file = new File(file.getParent()); + } + } } - private void prepareViews() { - if (getActivity() != null) { - layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - recyclerView.setAdapter(adapter); + return false; + } - adapter.setFastScroller(fastScroller); - adapter.addListener(this); - - fastScroller.setBubbleTextCreator(position -> { - IFlexible abstractFlexibleItem = adapter.getItem(position); - if (abstractFlexibleItem instanceof BrowserFileItem) { - return String.valueOf(((BrowserFileItem) adapter.getItem(position)).getModel().getDisplayName().charAt(0)); - } else { - return ""; - } - }); - } + private void checkAndRemoveAnySelectedParents(String currentPath) { + File file = new File(currentPath); + selectedPaths.remove(currentPath); + while (file.getParent() != null) { + selectedPaths.remove(file.getParent() + "/"); + file = new File(file.getParent()); } - @SuppressLint("RestrictedApi") - @Override - public void toggleBrowserItemSelection(String path) { - if (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)) { - checkAndRemoveAnySelectedParents(path); + adapter.notifyDataSetChanged(); + } + + @Override + public boolean onItemClick(View view, int position) { + BrowserFile browserFile = ((BrowserFileItem) adapter.getItem(position)).getModel(); + if ("inode/directory".equals((browserFile.getMimeType()))) { + fetchPath(browserFile.getPath()); + return true; + } + + return false; + } + + private void prepareViews() { + if (getActivity() != null) { + layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setHasFixedSize(true); + recyclerView.setAdapter(adapter); + + adapter.setFastScroller(fastScroller); + adapter.addListener(this); + + fastScroller.setBubbleTextCreator(position -> { + IFlexible abstractFlexibleItem = adapter.getItem(position); + if (abstractFlexibleItem instanceof BrowserFileItem) { + return String.valueOf( + ((BrowserFileItem) adapter.getItem(position)).getModel().getDisplayName().charAt(0)); } else { - // TOOD: if it's a folder, remove all the children we added manually - selectedPaths.add(path); + return ""; } + }); + } + } - filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); + @SuppressLint("RestrictedApi") + @Override + public void toggleBrowserItemSelection(String path) { + if (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)) { + checkAndRemoveAnySelectedParents(path); + } else { + // TOOD: if it's a folder, remove all the children we added manually + selectedPaths.add(path); } - @Override - public boolean isPathSelected(String path) { - return (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)); - } + filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); + } - @Parcel - public enum BrowserType { - FILE_BROWSER, - DAV_BROWSER, - } + @Override + public boolean isPathSelected(String path) { + return (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)); + } + + @Parcel + public enum BrowserType { + FILE_BROWSER, + DAV_BROWSER, + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/interfaces/ListingInterface.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/interfaces/ListingInterface.java index c953d8a59..567149a02 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/interfaces/ListingInterface.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/interfaces/ListingInterface.java @@ -23,5 +23,5 @@ package com.nextcloud.talk.components.filebrowser.interfaces; import com.nextcloud.talk.components.filebrowser.models.DavResponse; public interface ListingInterface { - void listingResult(DavResponse davResponse); + void listingResult(DavResponse davResponse); } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.java index 9a6963962..7260acb26 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.java @@ -43,67 +43,68 @@ import org.parceler.Parcel; @JsonObject @Parcel public class BrowserFile { - public String path; - public String displayName; - public String mimeType; - public long modifiedTimestamp; - public long size; - public boolean isFile; - // Used for remote files - public String remoteId; - public boolean hasPreview; - public boolean favorite; - public boolean encrypted; + public String path; + public String displayName; + public String mimeType; + public long modifiedTimestamp; + public long size; + public boolean isFile; + // Used for remote files + public String remoteId; + public boolean hasPreview; + public boolean favorite; + public boolean encrypted; - public static BrowserFile getModelFromResponse(Response response, String remotePath) { - BrowserFile browserFile = new BrowserFile(); - browserFile.setPath(Uri.decode(remotePath)); - browserFile.setDisplayName(Uri.decode(new File(remotePath).getName())); - final List properties = response.getProperties(); + public static BrowserFile getModelFromResponse(Response response, String remotePath) { + BrowserFile browserFile = new BrowserFile(); + browserFile.setPath(Uri.decode(remotePath)); + browserFile.setDisplayName(Uri.decode(new File(remotePath).getName())); + final List properties = response.getProperties(); - for (Property property : properties) { - if (property instanceof OCId) { - browserFile.setRemoteId(((OCId) property).getOcId()); - } + for (Property property : properties) { + if (property instanceof OCId) { + browserFile.setRemoteId(((OCId) property).getOcId()); + } - if (property instanceof ResourceType) { - browserFile.isFile = - !(((ResourceType) property).getTypes().contains(ResourceType.Companion.getCOLLECTION())); - } + if (property instanceof ResourceType) { + browserFile.isFile = + !(((ResourceType) property).getTypes() + .contains(ResourceType.Companion.getCOLLECTION())); + } - if (property instanceof GetLastModified) { - browserFile.setModifiedTimestamp(((GetLastModified) property).getLastModified()); - } + if (property instanceof GetLastModified) { + browserFile.setModifiedTimestamp(((GetLastModified) property).getLastModified()); + } - if (property instanceof GetContentType) { - browserFile.setMimeType(((GetContentType) property).getType()); - } + if (property instanceof GetContentType) { + browserFile.setMimeType(((GetContentType) property).getType()); + } - if (property instanceof OCSize) { - browserFile.setSize(((OCSize) property).getOcSize()); - } + if (property instanceof OCSize) { + browserFile.setSize(((OCSize) property).getOcSize()); + } - if (property instanceof NCPreview) { - browserFile.setHasPreview(((NCPreview) property).isNcPreview()); - } + if (property instanceof NCPreview) { + browserFile.setHasPreview(((NCPreview) property).isNcPreview()); + } - if (property instanceof OCFavorite) { - browserFile.setFavorite(((OCFavorite) property).isOcFavorite()); - } + if (property instanceof OCFavorite) { + browserFile.setFavorite(((OCFavorite) property).isOcFavorite()); + } - if (property instanceof DisplayName) { - browserFile.setDisplayName(((DisplayName) property).getDisplayName()); - } + if (property instanceof DisplayName) { + browserFile.setDisplayName(((DisplayName) property).getDisplayName()); + } - if (property instanceof NCEncrypted) { - browserFile.setEncrypted(((NCEncrypted) property).isNcEncrypted()); - } - } - - if (TextUtils.isEmpty(browserFile.getMimeType()) && !browserFile.isFile()) { - browserFile.setMimeType("inode/directory"); - } - - return browserFile; + if (property instanceof NCEncrypted) { + browserFile.setEncrypted(((NCEncrypted) property).isNcEncrypted()); + } } + + if (TextUtils.isEmpty(browserFile.getMimeType()) && !browserFile.isFile()) { + browserFile.setMimeType("inode/directory"); + } + + return browserFile; + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java index 5e3e53ea3..934d45431 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java @@ -25,6 +25,6 @@ import lombok.Data; @Data public class DavResponse { - Response response; - Object data; + Response response; + Object data; } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCEncrypted.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCEncrypted.java index d88e12923..b665296b9 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCEncrypted.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCEncrypted.java @@ -34,39 +34,40 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class NCEncrypted implements Property { - public static final Name NAME = new Name(DavUtils.NC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_IS_ENCRYPTED); + public static final Name NAME = + new Name(DavUtils.NC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_IS_ENCRYPTED); - @Getter - @Setter - private boolean ncEncrypted; + @Getter + @Setter + private boolean ncEncrypted; - private NCEncrypted(boolean isEncrypted) { - ncEncrypted = isEncrypted; + private NCEncrypted(boolean isEncrypted) { + ncEncrypted = isEncrypted; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new NCEncrypted(Boolean.parseBoolean(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new NCEncrypted(false); } - public static class Factory implements PropertyFactory { - - @Nullable - @Override - public Property create(@NotNull XmlPullParser xmlPullParser) { - try { - String text = XmlUtils.INSTANCE.readText(xmlPullParser); - if (!TextUtils.isEmpty(text)) { - return new NCEncrypted(Boolean.parseBoolean(text)); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - - return new NCEncrypted(false); - } - - @NotNull - @Override - public Name getName() { - return NAME; - } + @NotNull + @Override + public Name getName() { + return NAME; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCPreview.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCPreview.java index 461ebd6d2..ce2120bb5 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCPreview.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCPreview.java @@ -34,39 +34,40 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class NCPreview implements Property { - public static final Property.Name NAME = new Property.Name(DavUtils.NC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_HAS_PREVIEW); + public static final Property.Name NAME = + new Property.Name(DavUtils.NC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_HAS_PREVIEW); - @Getter - @Setter - private boolean ncPreview; + @Getter + @Setter + private boolean ncPreview; - private NCPreview(boolean hasPreview) { - ncPreview = hasPreview; + private NCPreview(boolean hasPreview) { + ncPreview = hasPreview; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new NCPreview(Boolean.parseBoolean(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCFavorite(false); } - public static class Factory implements PropertyFactory { - - @Nullable - @Override - public Property create(@NotNull XmlPullParser xmlPullParser) { - try { - String text = XmlUtils.INSTANCE.readText(xmlPullParser); - if (!TextUtils.isEmpty(text)) { - return new NCPreview(Boolean.parseBoolean(text)); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - - return new OCFavorite(false); - } - - @NotNull - @Override - public Property.Name getName() { - return NAME; - } + @NotNull + @Override + public Property.Name getName() { + return NAME; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCFavorite.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCFavorite.java index b182c88bc..02ba71f9d 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCFavorite.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCFavorite.java @@ -34,39 +34,40 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class OCFavorite implements Property { - public static final Property.Name NAME = new Property.Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_FAVORITE); + public static final Property.Name NAME = + new Property.Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_FAVORITE); - @Getter - @Setter - private boolean ocFavorite; + @Getter + @Setter + private boolean ocFavorite; - OCFavorite(boolean isFavorite) { - ocFavorite = isFavorite; + OCFavorite(boolean isFavorite) { + ocFavorite = isFavorite; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new OCFavorite(Boolean.parseBoolean(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCFavorite(false); } - public static class Factory implements PropertyFactory { - - @Nullable - @Override - public Property create(@NotNull XmlPullParser xmlPullParser) { - try { - String text = XmlUtils.INSTANCE.readText(xmlPullParser); - if (!TextUtils.isEmpty(text)) { - return new OCFavorite(Boolean.parseBoolean(text)); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - - return new OCFavorite(false); - } - - @NotNull - @Override - public Property.Name getName() { - return NAME; - } + @NotNull + @Override + public Property.Name getName() { + return NAME; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCId.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCId.java index 60dc773ea..7172b8b63 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCId.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCId.java @@ -34,39 +34,40 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class OCId implements Property { - public static final Name NAME = new Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_NAME_REMOTE_ID); + public static final Name NAME = + new Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_NAME_REMOTE_ID); - @Getter - @Setter - private String ocId; + @Getter + @Setter + private String ocId; - private OCId(String id) { - ocId = id; + private OCId(String id) { + ocId = id; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new OCId(text); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCId(""); } - public static class Factory implements PropertyFactory { - - @Nullable - @Override - public Property create(@NotNull XmlPullParser xmlPullParser) { - try { - String text = XmlUtils.INSTANCE.readText(xmlPullParser); - if (!TextUtils.isEmpty(text)) { - return new OCId(text); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - - return new OCId(""); - } - - @NotNull - @Override - public Name getName() { - return NAME; - } + @NotNull + @Override + public Name getName() { + return NAME; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCSize.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCSize.java index 13b9e3224..0054860bb 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCSize.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCSize.java @@ -34,39 +34,40 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class OCSize implements Property { - public static final Property.Name NAME = new Property.Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_NAME_SIZE); + public static final Property.Name NAME = + new Property.Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_NAME_SIZE); - @Getter - @Setter - private long ocSize; + @Getter + @Setter + private long ocSize; - private OCSize(long size) { - ocSize = size; + private OCSize(long size) { + ocSize = size; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new OCSize(Long.parseLong(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCSize(-1); } - public static class Factory implements PropertyFactory { - - @Nullable - @Override - public Property create(@NotNull XmlPullParser xmlPullParser) { - try { - String text = XmlUtils.INSTANCE.readText(xmlPullParser); - if (!TextUtils.isEmpty(text)) { - return new OCSize(Long.parseLong(text)); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - - return new OCSize(-1); - } - - @NotNull - @Override - public Name getName() { - return NAME; - } + @NotNull + @Override + public Name getName() { + return NAME; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/DavListing.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/DavListing.java index 1d43d4d78..64d82941d 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/DavListing.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/DavListing.java @@ -33,36 +33,36 @@ import java.util.concurrent.Callable; import okhttp3.OkHttpClient; public class DavListing extends ListingAbstractClass { - private DavResponse davResponse = new DavResponse(); + private DavResponse davResponse = new DavResponse(); - public DavListing(ListingInterface listingInterface) { - super(listingInterface); - } + public DavListing(ListingInterface listingInterface) { + super(listingInterface); + } - @Override - public void getFiles(String path, UserEntity currentUser, @Nullable OkHttpClient okHttpClient) { - Single.fromCallable(new Callable() { - @Override - public ReadFilesystemOperation call() { - return new ReadFilesystemOperation(okHttpClient, currentUser, path, 1); - } - }).subscribeOn(Schedulers.io()) - .subscribe(new SingleObserver() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void getFiles(String path, UserEntity currentUser, @Nullable OkHttpClient okHttpClient) { + Single.fromCallable(new Callable() { + @Override + public ReadFilesystemOperation call() { + return new ReadFilesystemOperation(okHttpClient, currentUser, path, 1); + } + }).subscribeOn(Schedulers.io()) + .subscribe(new SingleObserver() { + @Override + public void onSubscribe(Disposable d) { - } + } - @Override - public void onSuccess(ReadFilesystemOperation readFilesystemOperation) { - davResponse = readFilesystemOperation.readRemotePath(); - listingInterface.listingResult(davResponse); - } + @Override + public void onSuccess(ReadFilesystemOperation readFilesystemOperation) { + davResponse = readFilesystemOperation.readRemotePath(); + listingInterface.listingResult(davResponse); + } - @Override - public void onError(Throwable e) { - listingInterface.listingResult(davResponse); - } - }); - } + @Override + public void onError(Throwable e) { + listingInterface.listingResult(davResponse); + } + }); + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/ListingAbstractClass.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/ListingAbstractClass.java index b01bc31cc..08f30e01a 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/ListingAbstractClass.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/ListingAbstractClass.java @@ -27,22 +27,23 @@ import com.nextcloud.talk.models.database.UserEntity; import okhttp3.OkHttpClient; public abstract class ListingAbstractClass { - Handler handler; - ListingInterface listingInterface; + Handler handler; + ListingInterface listingInterface; - ListingAbstractClass(ListingInterface listingInterface) { - handler = new Handler(); - this.listingInterface = listingInterface; - } + ListingAbstractClass(ListingInterface listingInterface) { + handler = new Handler(); + this.listingInterface = listingInterface; + } - public abstract void getFiles(String path, UserEntity currentUser, @Nullable OkHttpClient okHttpClient); + public abstract void getFiles(String path, UserEntity currentUser, + @Nullable OkHttpClient okHttpClient); - public void cancelAllJobs() { - handler.removeCallbacksAndMessages(null); - } + public void cancelAllJobs() { + handler.removeCallbacksAndMessages(null); + } - public void tearDown() { - cancelAllJobs(); - handler = null; - } + public void tearDown() { + cancelAllJobs(); + handler = null; + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java index d0528bd53..8693f4063 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java @@ -42,78 +42,76 @@ import java.util.List; import java.util.Map; public class DavUtils { - public static final String OC_NAMESPACE = "http://owncloud.org/ns"; - public static final String NC_NAMESPACE = "http://nextcloud.org/ns"; - public static final String DAV_PATH = "/remote.php/dav/files/"; + public static final String OC_NAMESPACE = "http://owncloud.org/ns"; + public static final String NC_NAMESPACE = "http://nextcloud.org/ns"; + public static final String DAV_PATH = "/remote.php/dav/files/"; - public static final String EXTENDED_PROPERTY_NAME_PERMISSIONS = "permissions"; - public static final String EXTENDED_PROPERTY_NAME_REMOTE_ID = "id"; - public static final String EXTENDED_PROPERTY_NAME_SIZE = "size"; - public static final String EXTENDED_PROPERTY_FAVORITE = "favorite"; - public static final String EXTENDED_PROPERTY_IS_ENCRYPTED = "is-encrypted"; - public static final String EXTENDED_PROPERTY_MOUNT_TYPE = "mount-type"; - public static final String EXTENDED_PROPERTY_OWNER_ID = "owner-id"; - public static final String EXTENDED_PROPERTY_OWNER_DISPLAY_NAME = "owner-display-name"; - public static final String EXTENDED_PROPERTY_UNREAD_COMMENTS = "comments-unread"; - public static final String EXTENDED_PROPERTY_HAS_PREVIEW = "has-preview"; - public static final String EXTENDED_PROPERTY_NOTE = "note"; - public static final String TRASHBIN_FILENAME = "trashbin-filename"; - public static final String TRASHBIN_ORIGINAL_LOCATION = "trashbin-original-location"; - public static final String TRASHBIN_DELETION_TIME = "trashbin-deletion-time"; + public static final String EXTENDED_PROPERTY_NAME_PERMISSIONS = "permissions"; + public static final String EXTENDED_PROPERTY_NAME_REMOTE_ID = "id"; + public static final String EXTENDED_PROPERTY_NAME_SIZE = "size"; + public static final String EXTENDED_PROPERTY_FAVORITE = "favorite"; + public static final String EXTENDED_PROPERTY_IS_ENCRYPTED = "is-encrypted"; + public static final String EXTENDED_PROPERTY_MOUNT_TYPE = "mount-type"; + public static final String EXTENDED_PROPERTY_OWNER_ID = "owner-id"; + public static final String EXTENDED_PROPERTY_OWNER_DISPLAY_NAME = "owner-display-name"; + public static final String EXTENDED_PROPERTY_UNREAD_COMMENTS = "comments-unread"; + public static final String EXTENDED_PROPERTY_HAS_PREVIEW = "has-preview"; + public static final String EXTENDED_PROPERTY_NOTE = "note"; + public static final String TRASHBIN_FILENAME = "trashbin-filename"; + public static final String TRASHBIN_ORIGINAL_LOCATION = "trashbin-original-location"; + public static final String TRASHBIN_DELETION_TIME = "trashbin-deletion-time"; - public static final String PROPERTY_QUOTA_USED_BYTES = "quota-used-bytes"; - public static final String PROPERTY_QUOTA_AVAILABLE_BYTES = "quota-available-bytes"; + public static final String PROPERTY_QUOTA_USED_BYTES = "quota-used-bytes"; + public static final String PROPERTY_QUOTA_AVAILABLE_BYTES = "quota-available-bytes"; - static Property.Name[] getAllPropSet() { - List propSet = new ArrayList<>(); + static Property.Name[] getAllPropSet() { + List propSet = new ArrayList<>(); - propSet.add(DisplayName.NAME); - propSet.add(GetContentType.NAME); - propSet.add(GetContentLength.NAME); - propSet.add(GetContentType.NAME); - propSet.add(GetContentLength.NAME); - propSet.add(GetLastModified.NAME); - propSet.add(CreationDate.NAME); - propSet.add(GetETag.NAME); - propSet.add(ResourceType.NAME); + propSet.add(DisplayName.NAME); + propSet.add(GetContentType.NAME); + propSet.add(GetContentLength.NAME); + propSet.add(GetContentType.NAME); + propSet.add(GetContentLength.NAME); + propSet.add(GetLastModified.NAME); + propSet.add(CreationDate.NAME); + propSet.add(GetETag.NAME); + propSet.add(ResourceType.NAME); - propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_NAME_PERMISSIONS)); - propSet.add(OCId.NAME); - propSet.add(OCSize.NAME); - propSet.add(OCFavorite.NAME); - propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_OWNER_ID)); - propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_OWNER_DISPLAY_NAME)); - propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_UNREAD_COMMENTS)); + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_NAME_PERMISSIONS)); + propSet.add(OCId.NAME); + propSet.add(OCSize.NAME); + propSet.add(OCFavorite.NAME); + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_OWNER_ID)); + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_OWNER_DISPLAY_NAME)); + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_UNREAD_COMMENTS)); - propSet.add(NCEncrypted.NAME); - propSet.add(new Property.Name(NC_NAMESPACE, EXTENDED_PROPERTY_MOUNT_TYPE)); - propSet.add(NCPreview.NAME); - propSet.add(new Property.Name(NC_NAMESPACE, EXTENDED_PROPERTY_NOTE)); + propSet.add(NCEncrypted.NAME); + propSet.add(new Property.Name(NC_NAMESPACE, EXTENDED_PROPERTY_MOUNT_TYPE)); + propSet.add(NCPreview.NAME); + propSet.add(new Property.Name(NC_NAMESPACE, EXTENDED_PROPERTY_NOTE)); - return propSet.toArray(new Property.Name[0]); + return propSet.toArray(new Property.Name[0]); + } + + public static void registerCustomFactories() { + PropertyRegistry propertyRegistry = PropertyRegistry.INSTANCE; + try { + Field factories = propertyRegistry.getClass().getDeclaredField("factories"); + factories.setAccessible(true); + Map reflectionMap = (HashMap) factories.get(propertyRegistry); + + reflectionMap.put(OCId.NAME, new OCId.Factory()); + reflectionMap.put(NCPreview.NAME, new NCPreview.Factory()); + reflectionMap.put(NCEncrypted.NAME, new NCEncrypted.Factory()); + reflectionMap.put(OCFavorite.NAME, new OCFavorite.Factory()); + reflectionMap.put(OCSize.NAME, new OCSize.Factory()); + + factories.set(propertyRegistry, reflectionMap); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); } - - public static void registerCustomFactories() { - PropertyRegistry propertyRegistry = PropertyRegistry.INSTANCE; - try { - Field factories = propertyRegistry.getClass().getDeclaredField("factories"); - factories.setAccessible(true); - Map reflectionMap = (HashMap) factories.get(propertyRegistry); - - reflectionMap.put(OCId.NAME, new OCId.Factory()); - reflectionMap.put(NCPreview.NAME, new NCPreview.Factory()); - reflectionMap.put(NCEncrypted.NAME, new NCEncrypted.Factory()); - reflectionMap.put(OCFavorite.NAME, new OCFavorite.Factory()); - reflectionMap.put(OCSize.NAME, new OCSize.Factory()); - - factories.set(propertyRegistry, reflectionMap); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java index 55eefa03b..00baed7c3 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java @@ -37,63 +37,64 @@ import okhttp3.HttpUrl; import okhttp3.OkHttpClient; public class ReadFilesystemOperation { - private final OkHttpClient okHttpClient; - private final String url; - private final int depth; - private final String basePath; + private final OkHttpClient okHttpClient; + private final String url; + private final int depth; + private final String basePath; - public ReadFilesystemOperation(OkHttpClient okHttpClient, UserEntity currentUser, String path, int depth) { - OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder(); - okHttpClientBuilder.followRedirects(false); - okHttpClientBuilder.followSslRedirects(false); - okHttpClientBuilder.authenticator(new RestModule.MagicAuthenticator(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), "Authorization")); - this.okHttpClient = okHttpClientBuilder.build(); - this.basePath = currentUser.getBaseUrl() + DavUtils.DAV_PATH + currentUser.getUserId(); - this.url = basePath + path; - this.depth = depth; + public ReadFilesystemOperation(OkHttpClient okHttpClient, UserEntity currentUser, String path, + int depth) { + OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder(); + okHttpClientBuilder.followRedirects(false); + okHttpClientBuilder.followSslRedirects(false); + okHttpClientBuilder.authenticator(new RestModule.MagicAuthenticator( + ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), + "Authorization")); + this.okHttpClient = okHttpClientBuilder.build(); + this.basePath = currentUser.getBaseUrl() + DavUtils.DAV_PATH + currentUser.getUserId(); + this.url = basePath + path; + this.depth = depth; + } + + public DavResponse readRemotePath() { + DavResponse davResponse = new DavResponse(); + final List memberElements = new ArrayList<>(); + final Response[] rootElement = new Response[1]; + final List remoteFiles = new ArrayList<>(); + + try { + new DavResource(okHttpClient, HttpUrl.parse(url)).propfind(depth, DavUtils.getAllPropSet(), + new Function2() { + @Override + public Unit invoke(Response response, Response.HrefRelation hrefRelation) { + davResponse.setResponse(response); + switch (hrefRelation) { + case MEMBER: + memberElements.add(response); + break; + case SELF: + rootElement[0] = response; + break; + case OTHER: + default: + } + return Unit.INSTANCE; + } + }); + } catch (IOException e) { + e.printStackTrace(); + } catch (DavException e) { + e.printStackTrace(); } - public DavResponse readRemotePath() { - DavResponse davResponse = new DavResponse(); - final List memberElements = new ArrayList<>(); - final Response[] rootElement = new Response[1]; - final List remoteFiles = new ArrayList<>(); - - try { - new DavResource(okHttpClient, HttpUrl.parse(url)).propfind(depth, DavUtils.getAllPropSet(), - new Function2() { - @Override - public Unit invoke(Response response, Response.HrefRelation hrefRelation) { - davResponse.setResponse(response); - switch (hrefRelation) { - case MEMBER: - memberElements.add(response); - break; - case SELF: - rootElement[0] = response; - break; - case OTHER: - default: - } - return Unit.INSTANCE; - } - }); - } catch (IOException e) { - e.printStackTrace(); - } catch (DavException e) { - e.printStackTrace(); - } - - - remoteFiles.add(BrowserFile.getModelFromResponse(rootElement[0], - rootElement[0].getHref().toString().substring(basePath.length()))); - for (Response memberElement : memberElements) { - remoteFiles.add(BrowserFile.getModelFromResponse(memberElement, - memberElement.getHref().toString().substring(basePath.length()))); - } - - davResponse.setData(remoteFiles); - return davResponse; + remoteFiles.add(BrowserFile.getModelFromResponse(rootElement[0], + rootElement[0].getHref().toString().substring(basePath.length()))); + for (Response memberElement : memberElements) { + remoteFiles.add(BrowserFile.getModelFromResponse(memberElement, + memberElement.getHref().toString().substring(basePath.length()))); } + davResponse.setData(remoteFiles); + return davResponse; + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java index 3193ea899..a9ebdf1fc 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java @@ -36,9 +36,6 @@ import autodagger.AutoInjector; import butterknife.BindView; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.nextcloud.talk.models.json.conversations.RoomsOverall; -import com.nextcloud.talk.models.json.generic.Status; -import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; @@ -48,6 +45,9 @@ import com.nextcloud.talk.jobs.CapabilitiesWorker; import com.nextcloud.talk.jobs.PushRegistrationWorker; import com.nextcloud.talk.jobs.SignalingSettingsWorker; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.conversations.RoomsOverall; +import com.nextcloud.talk.models.json.generic.Status; +import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ClosedInterfaceImpl; @@ -67,426 +67,435 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; - @AutoInjector(NextcloudTalkApplication.class) public class AccountVerificationController extends BaseController { - public static final String TAG = "AccountVerificationController"; + public static final String TAG = "AccountVerificationController"; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - CookieManager cookieManager; + @Inject + CookieManager cookieManager; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - @BindView(R.id.progress_text) - TextView progressText; + @BindView(R.id.progress_text) + TextView progressText; - private long internalAccountId = -1; + private long internalAccountId = -1; - private String baseUrl; - private String username; - private String token; - private boolean isAccountImport; - private String originalProtocol; + private String baseUrl; + private String username; + private String token; + private boolean isAccountImport; + private String originalProtocol; - public AccountVerificationController(Bundle args) { - super(); - if (args != null) { - baseUrl = args.getString(BundleKeys.INSTANCE.getKEY_BASE_URL()); - username = args.getString(BundleKeys.INSTANCE.getKEY_USERNAME()); - token = args.getString(BundleKeys.INSTANCE.getKEY_TOKEN()); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT())) { - isAccountImport = true; - } - if (args.containsKey(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL())) { - originalProtocol = args.getString(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL()); - } - } + public AccountVerificationController(Bundle args) { + super(); + if (args != null) { + baseUrl = args.getString(BundleKeys.INSTANCE.getKEY_BASE_URL()); + username = args.getString(BundleKeys.INSTANCE.getKEY_USERNAME()); + token = args.getString(BundleKeys.INSTANCE.getKEY_TOKEN()); + if (args.containsKey(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT())) { + isAccountImport = true; + } + if (args.containsKey(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL())) { + originalProtocol = args.getString(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL()); + } + } + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_account_verification, container, false); + } + + @Override + protected void onDetach(@NonNull View view) { + eventBus.unregister(this); + super.onDetach(view); + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + eventBus.register(this); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + if (getActivity() != null) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_account_verification, container, false); + if (getActionBar() != null) { + getActionBar().hide(); } - @Override - protected void onDetach(@NonNull View view) { - eventBus.unregister(this); - super.onDetach(view); + if (isAccountImport && !baseUrl.startsWith("http://") && !baseUrl.startsWith("https://") + || (!TextUtils + .isEmpty(originalProtocol) && !baseUrl.startsWith(originalProtocol))) { + determineBaseUrlProtocol(true); + } else { + checkEverything(); + } + } + + private void checkEverything() { + String credentials = ApiUtils.getCredentials(username, token); + cookieManager.getCookieStore().removeAll(); + + findServerTalkApp(credentials); + } + + private void determineBaseUrlProtocol(boolean checkForcedHttps) { + cookieManager.getCookieStore().removeAll(); + + String queryUrl; + + baseUrl = baseUrl.replace("http://", "").replace("https://", ""); + + if (checkForcedHttps) { + queryUrl = "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus(); + } else { + queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus(); } - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - eventBus.register(this); - } + ncApi.getServerStatus(queryUrl) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } - - if (getActionBar() != null) { - getActionBar().hide(); - } - - if (isAccountImport && !baseUrl.startsWith("http://") && !baseUrl.startsWith("https://") || (!TextUtils - .isEmpty(originalProtocol) && !baseUrl.startsWith(originalProtocol))) { - determineBaseUrlProtocol(true); - } else { - checkEverything(); - } - } - - private void checkEverything() { - String credentials = ApiUtils.getCredentials(username, token); - cookieManager.getCookieStore().removeAll(); - - findServerTalkApp(credentials); - } - - private void determineBaseUrlProtocol(boolean checkForcedHttps) { - cookieManager.getCookieStore().removeAll(); - - String queryUrl; - - baseUrl = baseUrl.replace("http://", "").replace("https://", ""); - - if (checkForcedHttps) { - queryUrl = "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus(); - } else { - queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus(); - } - - ncApi.getServerStatus(queryUrl) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(Status status) { - if (checkForcedHttps) { - baseUrl = "https://" + baseUrl; - } else { - baseUrl = "http://" + baseUrl; - } - - if (isAccountImport) { - getRouter().replaceTopController(RouterTransaction.with(new WebViewLoginController(baseUrl, - false, username, "")) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - checkEverything(); - } - } - - @Override - public void onError(Throwable e) { - if (checkForcedHttps) { - determineBaseUrlProtocol(false); - } else { - abortVerification(); - } - } - - @Override - public void onComplete() { - - } - }); - } - - private void findServerTalkApp(String credentials) { - ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) - .subscribeOn(Schedulers.io()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(RoomsOverall roomsOverall) { - fetchProfile(credentials); - } - - @Override - public void onError(Throwable e) { - if (getActivity() != null && getResources() != null) { - getActivity().runOnUiThread(() -> progressText.setText(String.format(getResources().getString( - R.string.nc_nextcloud_talk_app_not_installed), getResources().getString(R.string.nc_app_name)))); - } - - ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK); - - abortVerification(); - } - - @Override - public void onComplete() { - - } - }); - } - - private void storeProfile(String displayName, String userId) { - userUtils.createOrUpdateUser(username, token, - baseUrl, displayName, null, true, - userId, null, null, - appPreferences.getTemporaryClientCertAlias(), null) - .subscribeOn(Schedulers.io()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(UserEntity userEntity) { - internalAccountId = userEntity.getId(); - - if (new ClosedInterfaceImpl().isGooglePlayServicesAvailable()) { - registerForPush(); - } else { - getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_push_disabled))); - fetchAndStoreCapabilities(); - } - } - - @Override - public void onError(Throwable e) { - progressText.setText(progressText.getText().toString() + - "\n" + - getResources().getString( - R.string.nc_display_name_not_stored)); - abortVerification(); - - } - - @Override - public void onComplete() { - - } - }); - } - - private void fetchProfile(String credentials) { - ncApi.getUserProfile(credentials, - ApiUtils.getUrlForUserProfile(baseUrl)) - .subscribeOn(Schedulers.io()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(UserProfileOverall userProfileOverall) { - String displayName = null; - if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() - .getDisplayName())) { - displayName = userProfileOverall.getOcs().getData() - .getDisplayName(); - } else if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() - .getDisplayNameAlt())) { - displayName = userProfileOverall.getOcs().getData() - .getDisplayNameAlt(); - } - - if (!TextUtils.isEmpty(displayName)) { - storeProfile(displayName, userProfileOverall.getOcs().getData().getUserId()); - } else { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_display_name_not_fetched))); - } - abortVerification(); - } - } - - @Override - public void onError(Throwable e) { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_display_name_not_fetched))); - } - abortVerification(); - - } - - @Override - public void onComplete() { - - } - }); - } - - private void registerForPush() { - OneTimeWorkRequest pushRegistrationWork = new OneTimeWorkRequest.Builder(PushRegistrationWorker.class).build(); - WorkManager.getInstance().enqueue(pushRegistrationWork); - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(EventStatus eventStatus) { - if (eventStatus.getEventType().equals(EventStatus.EventType.PUSH_REGISTRATION)) { - if (internalAccountId == eventStatus.getUserId() && !eventStatus.isAllGood() && getActivity() != null) { - getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_push_disabled))); - } - fetchAndStoreCapabilities(); - } else if (eventStatus.getEventType().equals(EventStatus.EventType.CAPABILITIES_FETCH)) { - if (internalAccountId == eventStatus.getUserId() && !eventStatus.isAllGood()) { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_capabilities_failed))); - } - abortVerification(); - } else if (internalAccountId == eventStatus.getUserId() && eventStatus.isAllGood()) { - fetchAndStoreExternalSignalingSettings(); - } - } else if (eventStatus.getEventType().equals(EventStatus.EventType.SIGNALING_SETTINGS)) { - if (internalAccountId == eventStatus.getUserId() && !eventStatus.isAllGood()) { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_external_server_failed))); - } - } - - proceedWithLogin(); - } - - } - - private void fetchAndStoreCapabilities() { - Data userData = new Data.Builder() - .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), internalAccountId) - .build(); - - OneTimeWorkRequest pushNotificationWork = new OneTimeWorkRequest.Builder(CapabilitiesWorker.class) - .setInputData(userData) - .build(); - WorkManager.getInstance().enqueue(pushNotificationWork); - } - - private void fetchAndStoreExternalSignalingSettings() { - Data userData = new Data.Builder() - .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), internalAccountId) - .build(); - - OneTimeWorkRequest signalingSettings = new OneTimeWorkRequest.Builder(SignalingSettingsWorker.class) - .setInputData(userData) - .build(); - WorkManager.getInstance().enqueue(signalingSettings); - } - - private void proceedWithLogin() { - cookieManager.getCookieStore().removeAll(); - userUtils.disableAllUsersWithoutId(internalAccountId); - - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - if (userUtils.getUsers().size() == 1) { - getRouter().setRoot(RouterTransaction.with(new - ConversationsListView()) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - if (isAccountImport) { - ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED); - } - getRouter().popToRoot(); - } - }); - } - } - - - @Override - protected void onDestroyView(@NonNull View view) { - super.onDestroyView(view); - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); - } - } - - private void abortVerification() { - - if (!isAccountImport) { - if (internalAccountId != -1) { - userUtils.deleteUserWithId(internalAccountId).subscribe(new CompletableObserver() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onComplete() { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - new Handler().postDelayed(() -> getRouter().popToRoot(), 7500); - }); - } - } - - @Override - public void onError(Throwable e) { - - } - }); + @Override + public void onNext(Status status) { + if (checkForcedHttps) { + baseUrl = "https://" + baseUrl; } else { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - new Handler().postDelayed(() -> getRouter().popToRoot(), 7500); - }); - } + baseUrl = "http://" + baseUrl; } - } else { + + if (isAccountImport) { + getRouter().replaceTopController( + RouterTransaction.with(new WebViewLoginController(baseUrl, + false, username, "")) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else { + checkEverything(); + } + } + + @Override + public void onError(Throwable e) { + if (checkForcedHttps) { + determineBaseUrlProtocol(false); + } else { + abortVerification(); + } + } + + @Override + public void onComplete() { + + } + }); + } + + private void findServerTalkApp(String credentials) { + ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) + .subscribeOn(Schedulers.io()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(RoomsOverall roomsOverall) { + fetchProfile(credentials); + } + + @Override + public void onError(Throwable e) { + if (getActivity() != null && getResources() != null) { + getActivity().runOnUiThread( + () -> progressText.setText(String.format(getResources().getString( + R.string.nc_nextcloud_talk_app_not_installed), + getResources().getString(R.string.nc_app_name)))); + } + ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT); - if (getActivity() != null) { - getActivity().runOnUiThread(() -> new Handler().postDelayed(() -> { - if (getRouter().hasRootController()) { - if (getActivity() != null) { - getRouter().popToRoot(); - } + ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK); + abortVerification(); + } - } else { - if (userUtils.anyUserExists()) { - getRouter().setRoot(RouterTransaction.with(new ConversationsListView()) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - getRouter().setRoot(RouterTransaction.with(new ServerSelectionController()) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } - } - }, 7500)); + @Override + public void onComplete() { + + } + }); + } + + private void storeProfile(String displayName, String userId) { + userUtils.createOrUpdateUser(username, token, + baseUrl, displayName, null, true, + userId, null, null, + appPreferences.getTemporaryClientCertAlias(), null) + .subscribeOn(Schedulers.io()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(UserEntity userEntity) { + internalAccountId = userEntity.getId(); + + if (new ClosedInterfaceImpl().isGooglePlayServicesAvailable()) { + registerForPush(); + } else { + getActivity().runOnUiThread( + () -> progressText.setText(progressText.getText().toString() + "\n" + + getResources().getString(R.string.nc_push_disabled))); + fetchAndStoreCapabilities(); } - } - } + } + @Override + public void onError(Throwable e) { + progressText.setText(progressText.getText().toString() + + "\n" + + getResources().getString( + R.string.nc_display_name_not_stored)); + abortVerification(); + } + + @Override + public void onComplete() { + + } + }); + } + + private void fetchProfile(String credentials) { + ncApi.getUserProfile(credentials, + ApiUtils.getUrlForUserProfile(baseUrl)) + .subscribeOn(Schedulers.io()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(UserProfileOverall userProfileOverall) { + String displayName = null; + if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() + .getDisplayName())) { + displayName = userProfileOverall.getOcs().getData() + .getDisplayName(); + } else if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() + .getDisplayNameAlt())) { + displayName = userProfileOverall.getOcs().getData() + .getDisplayNameAlt(); + } + + if (!TextUtils.isEmpty(displayName)) { + storeProfile(displayName, userProfileOverall.getOcs().getData().getUserId()); + } else { + if (getActivity() != null) { + getActivity().runOnUiThread( + () -> progressText.setText(progressText.getText().toString() + "\n" + + getResources().getString(R.string.nc_display_name_not_fetched))); + } + abortVerification(); + } + } + + @Override + public void onError(Throwable e) { + if (getActivity() != null) { + getActivity().runOnUiThread( + () -> progressText.setText(progressText.getText().toString() + "\n" + + getResources().getString(R.string.nc_display_name_not_fetched))); + } + abortVerification(); + } + + @Override + public void onComplete() { + + } + }); + } + + private void registerForPush() { + OneTimeWorkRequest pushRegistrationWork = + new OneTimeWorkRequest.Builder(PushRegistrationWorker.class).build(); + WorkManager.getInstance().enqueue(pushRegistrationWork); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(EventStatus eventStatus) { + if (eventStatus.getEventType().equals(EventStatus.EventType.PUSH_REGISTRATION)) { + if (internalAccountId == eventStatus.getUserId() + && !eventStatus.isAllGood() + && getActivity() != null) { + getActivity().runOnUiThread( + () -> progressText.setText(progressText.getText().toString() + "\n" + + getResources().getString(R.string.nc_push_disabled))); + } + fetchAndStoreCapabilities(); + } else if (eventStatus.getEventType().equals(EventStatus.EventType.CAPABILITIES_FETCH)) { + if (internalAccountId == eventStatus.getUserId() && !eventStatus.isAllGood()) { + if (getActivity() != null) { + getActivity().runOnUiThread( + () -> progressText.setText(progressText.getText().toString() + "\n" + + getResources().getString(R.string.nc_capabilities_failed))); + } + abortVerification(); + } else if (internalAccountId == eventStatus.getUserId() && eventStatus.isAllGood()) { + fetchAndStoreExternalSignalingSettings(); + } + } else if (eventStatus.getEventType().equals(EventStatus.EventType.SIGNALING_SETTINGS)) { + if (internalAccountId == eventStatus.getUserId() && !eventStatus.isAllGood()) { + if (getActivity() != null) { + getActivity().runOnUiThread( + () -> progressText.setText(progressText.getText().toString() + "\n" + + getResources().getString(R.string.nc_external_server_failed))); + } + } + + proceedWithLogin(); + } + } + + private void fetchAndStoreCapabilities() { + Data userData = new Data.Builder() + .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), internalAccountId) + .build(); + + OneTimeWorkRequest pushNotificationWork = + new OneTimeWorkRequest.Builder(CapabilitiesWorker.class) + .setInputData(userData) + .build(); + WorkManager.getInstance().enqueue(pushNotificationWork); + } + + private void fetchAndStoreExternalSignalingSettings() { + Data userData = new Data.Builder() + .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), internalAccountId) + .build(); + + OneTimeWorkRequest signalingSettings = + new OneTimeWorkRequest.Builder(SignalingSettingsWorker.class) + .setInputData(userData) + .build(); + WorkManager.getInstance().enqueue(signalingSettings); + } + + private void proceedWithLogin() { + cookieManager.getCookieStore().removeAll(); + userUtils.disableAllUsersWithoutId(internalAccountId); + + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + if (userUtils.getUsers().size() == 1) { + getRouter().setRoot(RouterTransaction.with(new + ConversationsListView()) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else { + if (isAccountImport) { + ApplicationWideMessageHolder.getInstance().setMessageType( + ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED); + } + getRouter().popToRoot(); + } + }); + } + } + + @Override + protected void onDestroyView(@NonNull View view) { + super.onDestroyView(view); + if (getActivity() != null) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + } + } + + private void abortVerification() { + + if (!isAccountImport) { + if (internalAccountId != -1) { + userUtils.deleteUserWithId(internalAccountId).subscribe(new CompletableObserver() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onComplete() { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + new Handler().postDelayed(() -> getRouter().popToRoot(), 7500); + }); + } + } + + @Override + public void onError(Throwable e) { + + } + }); + } else { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + new Handler().postDelayed(() -> getRouter().popToRoot(), 7500); + }); + } + } + } else { + ApplicationWideMessageHolder.getInstance().setMessageType( + ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT); + if (getActivity() != null) { + getActivity().runOnUiThread(() -> new Handler().postDelayed(() -> { + if (getRouter().hasRootController()) { + if (getActivity() != null) { + getRouter().popToRoot(); + } + } else { + if (userUtils.anyUserExists()) { + getRouter().setRoot(RouterTransaction.with(new ConversationsListView()) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else { + getRouter().setRoot(RouterTransaction.with(new ServerSelectionController()) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } + } + }, 7500)); + } + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java index 20c67a0a8..23dd3836a 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -56,17 +56,6 @@ import com.bluelinelabs.logansquare.LoganSquare; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; -import com.nextcloud.talk.models.ExternalSignalingServer; -import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.conversations.RoomOverall; -import com.nextcloud.talk.models.json.conversations.RoomsOverall; -import com.nextcloud.talk.models.json.generic.GenericOverall; -import com.nextcloud.talk.models.json.participants.Participant; -import com.nextcloud.talk.models.json.participants.ParticipantsOverall; -import com.nextcloud.talk.models.json.signaling.*; -import com.nextcloud.talk.models.json.signaling.settings.IceServer; -import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; @@ -77,7 +66,25 @@ import com.nextcloud.talk.events.NetworkEvent; import com.nextcloud.talk.events.PeerConnectionEvent; import com.nextcloud.talk.events.SessionDescriptionSendEvent; import com.nextcloud.talk.events.WebSocketCommunicationEvent; +import com.nextcloud.talk.models.ExternalSignalingServer; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; +import com.nextcloud.talk.models.json.conversations.Conversation; +import com.nextcloud.talk.models.json.conversations.RoomOverall; +import com.nextcloud.talk.models.json.conversations.RoomsOverall; +import com.nextcloud.talk.models.json.generic.GenericOverall; +import com.nextcloud.talk.models.json.participants.Participant; +import com.nextcloud.talk.models.json.participants.ParticipantsOverall; +import com.nextcloud.talk.models.json.signaling.DataChannelMessage; +import com.nextcloud.talk.models.json.signaling.DataChannelMessageNick; +import com.nextcloud.talk.models.json.signaling.NCIceCandidate; +import com.nextcloud.talk.models.json.signaling.NCMessagePayload; +import com.nextcloud.talk.models.json.signaling.NCMessageWrapper; +import com.nextcloud.talk.models.json.signaling.NCSignalingMessage; +import com.nextcloud.talk.models.json.signaling.Signaling; +import com.nextcloud.talk.models.json.signaling.SignalingOverall; +import com.nextcloud.talk.models.json.signaling.settings.IceServer; +import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.NotificationUtils; @@ -141,2182 +148,2257 @@ import pub.devrel.easypermissions.AfterPermissionGranted; @AutoInjector(NextcloudTalkApplication.class) public class CallController extends BaseController { - private static final String TAG = "CallController"; + private static final String TAG = "CallController"; - private static final String[] PERMISSIONS_CALL = { - android.Manifest.permission.CAMERA, - android.Manifest.permission.RECORD_AUDIO, - }; + private static final String[] PERMISSIONS_CALL = { + android.Manifest.permission.CAMERA, + android.Manifest.permission.RECORD_AUDIO, + }; - private static final String[] PERMISSIONS_CAMERA = { - Manifest.permission.CAMERA - }; + private static final String[] PERMISSIONS_CAMERA = { + Manifest.permission.CAMERA + }; - private static final String[] PERMISSIONS_MICROPHONE = { - Manifest.permission.RECORD_AUDIO - }; + private static final String[] PERMISSIONS_MICROPHONE = { + Manifest.permission.RECORD_AUDIO + }; - @BindView(R.id.callControlEnableSpeaker) - SimpleDraweeView callControlEnableSpeaker; + @BindView(R.id.callControlEnableSpeaker) + SimpleDraweeView callControlEnableSpeaker; - @BindView(R.id.pip_video_view) - SurfaceViewRenderer pipVideoView; - @BindView(R.id.relative_layout) - RelativeLayout relativeLayout; - @BindView(R.id.remote_renderers_layout) - LinearLayout remoteRenderersLayout; + @BindView(R.id.pip_video_view) + SurfaceViewRenderer pipVideoView; + @BindView(R.id.relative_layout) + RelativeLayout relativeLayout; + @BindView(R.id.remote_renderers_layout) + LinearLayout remoteRenderersLayout; - @BindView(R.id.callControlsRelativeLayout) - RelativeLayout callControls; - @BindView(R.id.call_control_microphone) - SimpleDraweeView microphoneControlButton; - @BindView(R.id.call_control_camera) - SimpleDraweeView cameraControlButton; - @BindView(R.id.call_control_switch_camera) - SimpleDraweeView cameraSwitchButton; - @BindView(R.id.connectingTextView) - TextView connectingTextView; + @BindView(R.id.callControlsRelativeLayout) + RelativeLayout callControls; + @BindView(R.id.call_control_microphone) + SimpleDraweeView microphoneControlButton; + @BindView(R.id.call_control_camera) + SimpleDraweeView cameraControlButton; + @BindView(R.id.call_control_switch_camera) + SimpleDraweeView cameraSwitchButton; + @BindView(R.id.connectingTextView) + TextView connectingTextView; - @BindView(R.id.connectingRelativeLayoutView) - RelativeLayout connectingView; + @BindView(R.id.connectingRelativeLayoutView) + RelativeLayout connectingView; - @BindView(R.id.conversationRelativeLayoutView) - RelativeLayout conversationView; + @BindView(R.id.conversationRelativeLayoutView) + RelativeLayout conversationView; - @BindView(R.id.errorImageView) - ImageView errorImageView; + @BindView(R.id.errorImageView) + ImageView errorImageView; - @BindView(R.id.progress_bar) - ProgressBar progressBar; + @BindView(R.id.progress_bar) + ProgressBar progressBar; - @Inject - NcApi ncApi; - @Inject - UserUtils userUtils; - @Inject - AppPreferences appPreferences; - @Inject - Cache cache; - @Inject - EventBus eventBus; + @Inject + NcApi ncApi; + @Inject + UserUtils userUtils; + @Inject + AppPreferences appPreferences; + @Inject + Cache cache; + @Inject + EventBus eventBus; - private PeerConnectionFactory peerConnectionFactory; - private MediaConstraints audioConstraints; - private MediaConstraints videoConstraints; - private MediaConstraints sdpConstraints; - private MediaConstraints sdpConstraintsForMCU; - private MagicAudioManager audioManager; - private VideoSource videoSource; - private VideoTrack localVideoTrack; - private AudioSource audioSource; - private AudioTrack localAudioTrack; - private VideoCapturer videoCapturer; - private EglBase rootEglBase; - private Disposable signalingDisposable; - private Disposable pingDisposable; - private List iceServers; - private CameraEnumerator cameraEnumerator; - private String roomToken; - private UserEntity conversationUser; - private String callSession; - private MediaStream localMediaStream; - private String credentials; - private List magicPeerConnectionWrapperList = new ArrayList<>(); - private Map participantMap = new HashMap<>(); + private PeerConnectionFactory peerConnectionFactory; + private MediaConstraints audioConstraints; + private MediaConstraints videoConstraints; + private MediaConstraints sdpConstraints; + private MediaConstraints sdpConstraintsForMCU; + private MagicAudioManager audioManager; + private VideoSource videoSource; + private VideoTrack localVideoTrack; + private AudioSource audioSource; + private AudioTrack localAudioTrack; + private VideoCapturer videoCapturer; + private EglBase rootEglBase; + private Disposable signalingDisposable; + private Disposable pingDisposable; + private List iceServers; + private CameraEnumerator cameraEnumerator; + private String roomToken; + private UserEntity conversationUser; + private String callSession; + private MediaStream localMediaStream; + private String credentials; + private List magicPeerConnectionWrapperList = new ArrayList<>(); + private Map participantMap = new HashMap<>(); - private boolean videoOn = false; - private boolean audioOn = false; + private boolean videoOn = false; + private boolean audioOn = false; - private boolean isMultiSession = false; - private boolean needsPing = true; + private boolean isMultiSession = false; + private boolean needsPing = true; - private boolean isVoiceOnlyCall; - private Handler callControlHandler = new Handler(); - private Handler cameraSwitchHandler = new Handler(); + private boolean isVoiceOnlyCall; + private Handler callControlHandler = new Handler(); + private Handler cameraSwitchHandler = new Handler(); - private boolean isPTTActive = false; - private PulseAnimation pulseAnimation; - private View.OnClickListener videoOnClickListener; + private boolean isPTTActive = false; + private PulseAnimation pulseAnimation; + private View.OnClickListener videoOnClickListener; - private String baseUrl; - private String roomId; + private String baseUrl; + private String roomId; - private SpotlightView spotlightView; + private SpotlightView spotlightView; - private ExternalSignalingServer externalSignalingServer; - private MagicWebSocketInstance webSocketClient; - private WebSocketConnectionHelper webSocketConnectionHelper; - private boolean hasMCU; - private boolean hasExternalSignalingServer; - private String conversationPassword; + private ExternalSignalingServer externalSignalingServer; + private MagicWebSocketInstance webSocketClient; + private WebSocketConnectionHelper webSocketConnectionHelper; + private boolean hasMCU; + private boolean hasExternalSignalingServer; + private String conversationPassword; - private PowerManagerUtils powerManagerUtils; + private PowerManagerUtils powerManagerUtils; - private Handler handler; + private Handler handler; - private CallStatus currentCallStatus; + private CallStatus currentCallStatus; - private MediaPlayer mediaPlayer; + private MediaPlayer mediaPlayer; - @Parcel - public enum CallStatus { - CALLING, CALLING_TIMEOUT, ESTABLISHED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED + public CallController(Bundle args) { + super(); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + roomId = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), ""); + roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), ""); + conversationUser = args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY()); + conversationPassword = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), ""); + isVoiceOnlyCall = args.getBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), false); + + credentials = + ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken()); + + baseUrl = args.getString(BundleKeys.INSTANCE.getKEY_MODIFIED_BASE_URL(), ""); + + if (TextUtils.isEmpty(baseUrl)) { + baseUrl = conversationUser.getBaseUrl(); } - public CallController(Bundle args) { - super(); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + powerManagerUtils = new PowerManagerUtils(); + setCallState(CallStatus.CALLING); + } - roomId = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), ""); - roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), ""); - conversationUser = args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY()); - conversationPassword = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), ""); - isVoiceOnlyCall = args.getBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), false); + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_call, container, false); + } - credentials = ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken()); + private void createCameraEnumerator() { + if (getActivity() != null) { + boolean camera2EnumeratorIsSupported = false; + try { + camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(getActivity()); + } catch (final Throwable throwable) { + Log.w(TAG, "Camera2Enumator threw an error"); + } - baseUrl = args.getString(BundleKeys.INSTANCE.getKEY_MODIFIED_BASE_URL(), ""); + if (camera2EnumeratorIsSupported) { + cameraEnumerator = new Camera2Enumerator(getActivity()); + } else { + cameraEnumerator = + new Camera1Enumerator(MagicWebRTCUtils.shouldEnableVideoHardwareAcceleration()); + } + } + } - if (TextUtils.isEmpty(baseUrl)) { - baseUrl = conversationUser.getBaseUrl(); - } + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); - powerManagerUtils = new PowerManagerUtils(); - setCallState(CallStatus.CALLING); + microphoneControlButton.setOnTouchListener(new MicrophoneButtonTouchListener()); + videoOnClickListener = new VideoClickListener(); + + pulseAnimation = PulseAnimation.create().with(microphoneControlButton) + .setDuration(310) + .setRepeatCount(PulseAnimation.INFINITE) + .setRepeatMode(PulseAnimation.REVERSE); + + setPipVideoViewDimensions(); + + try { + cache.evictAll(); + } catch (IOException e) { + Log.e(TAG, "Failed to evict cache"); } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_call, container, false); + callControls.setZ(100.0f); + basicInitialization(); + initViews(); + + initiateCall(); + } + + private void basicInitialization() { + rootEglBase = EglBase.create(); + createCameraEnumerator(); + + //Create a new PeerConnectionFactory instance. + PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); + peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory(); + + peerConnectionFactory.setVideoHwAccelerationOptions(rootEglBase.getEglBaseContext(), + rootEglBase.getEglBaseContext()); + + //Create MediaConstraints - Will be useful for specifying video and audio constraints. + audioConstraints = new MediaConstraints(); + videoConstraints = new MediaConstraints(); + + localMediaStream = peerConnectionFactory.createLocalMediaStream("NCMS"); + + // Create and audio manager that will take care of audio routing, + // audio modes, audio device enumeration etc. + audioManager = MagicAudioManager.create(getApplicationContext(), !isVoiceOnlyCall); + // Store existing audio settings and change audio mode to + // MODE_IN_COMMUNICATION for best possible VoIP performance. + Log.d(TAG, "Starting the audio manager..."); + audioManager.start(this::onAudioManagerDevicesChanged); + + iceServers = new ArrayList<>(); + + //create sdpConstraints + sdpConstraints = new MediaConstraints(); + sdpConstraintsForMCU = new MediaConstraints(); + sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true")); + String offerToReceiveVideoString = "true"; + + if (isVoiceOnlyCall) { + offerToReceiveVideoString = "false"; } - private void createCameraEnumerator() { - if (getActivity() != null) { - boolean camera2EnumeratorIsSupported = false; - try { - camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(getActivity()); - } catch (final Throwable throwable) { - Log.w(TAG, "Camera2Enumator threw an error"); - } + sdpConstraints.mandatory.add( + new MediaConstraints.KeyValuePair("OfferToReceiveVideo", offerToReceiveVideoString)); - if (camera2EnumeratorIsSupported) { - cameraEnumerator = new Camera2Enumerator(getActivity()); - } else { - cameraEnumerator = new Camera1Enumerator(MagicWebRTCUtils.shouldEnableVideoHardwareAcceleration()); - } - } + sdpConstraintsForMCU.mandatory.add( + new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "false")); + sdpConstraintsForMCU.mandatory.add( + new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false")); + + sdpConstraintsForMCU.optional.add( + new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true")); + sdpConstraintsForMCU.optional.add( + new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); + + sdpConstraints.optional.add( + new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true")); + sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); + + if (!isVoiceOnlyCall) { + cameraInitialization(); } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); + microphoneInitialization(); + } - microphoneControlButton.setOnTouchListener(new MicrophoneButtonTouchListener()); - videoOnClickListener = new VideoClickListener(); + private void handleFromNotification() { + ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) + .retry(3) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - pulseAnimation = PulseAnimation.create().with(microphoneControlButton) - .setDuration(310) - .setRepeatCount(PulseAnimation.INFINITE) - .setRepeatMode(PulseAnimation.REVERSE); + } - setPipVideoViewDimensions(); - - try { - cache.evictAll(); - } catch (IOException e) { - Log.e(TAG, "Failed to evict cache"); - } - - callControls.setZ(100.0f); - basicInitialization(); - initViews(); - - initiateCall(); - } - - private void basicInitialization() { - rootEglBase = EglBase.create(); - createCameraEnumerator(); - - //Create a new PeerConnectionFactory instance. - PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); - peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory(); - - peerConnectionFactory.setVideoHwAccelerationOptions(rootEglBase.getEglBaseContext(), - rootEglBase.getEglBaseContext()); - - //Create MediaConstraints - Will be useful for specifying video and audio constraints. - audioConstraints = new MediaConstraints(); - videoConstraints = new MediaConstraints(); - - localMediaStream = peerConnectionFactory.createLocalMediaStream("NCMS"); - - // Create and audio manager that will take care of audio routing, - // audio modes, audio device enumeration etc. - audioManager = MagicAudioManager.create(getApplicationContext(), !isVoiceOnlyCall); - // Store existing audio settings and change audio mode to - // MODE_IN_COMMUNICATION for best possible VoIP performance. - Log.d(TAG, "Starting the audio manager..."); - audioManager.start(this::onAudioManagerDevicesChanged); - - iceServers = new ArrayList<>(); - - //create sdpConstraints - sdpConstraints = new MediaConstraints(); - sdpConstraintsForMCU = new MediaConstraints(); - sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true")); - String offerToReceiveVideoString = "true"; - - if (isVoiceOnlyCall) { - offerToReceiveVideoString = "false"; - } - - sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", offerToReceiveVideoString)); - - sdpConstraintsForMCU.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "false")); - sdpConstraintsForMCU.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false")); - - sdpConstraintsForMCU.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true")); - sdpConstraintsForMCU.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); - - sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true")); - sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); - - if (!isVoiceOnlyCall) { - cameraInitialization(); - } - - microphoneInitialization(); - } - - private void handleFromNotification() { - ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) - .retry(3) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomsOverall roomsOverall) { - for (Conversation conversation : roomsOverall.getOcs().getData()) { - if (roomId.equals(conversation.getRoomId())) { - roomToken = conversation.getToken(); - break; - } - } - - checkPermissions(); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - private void initViews() { - if (isVoiceOnlyCall) { - callControlEnableSpeaker.setVisibility(View.VISIBLE); - cameraSwitchButton.setVisibility(View.GONE); - cameraControlButton.setVisibility(View.GONE); - pipVideoView.setVisibility(View.GONE); - } else { - if (cameraEnumerator.getDeviceNames().length < 2) { - cameraSwitchButton.setVisibility(View.GONE); + @Override + public void onNext(RoomsOverall roomsOverall) { + for (Conversation conversation : roomsOverall.getOcs().getData()) { + if (roomId.equals(conversation.getRoomId())) { + roomToken = conversation.getToken(); + break; + } } - pipVideoView.init(rootEglBase.getEglBaseContext(), null); - pipVideoView.setZOrderMediaOverlay(true); - // disabled because it causes some devices to crash - pipVideoView.setEnableHardwareScaler(false); - pipVideoView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); - } - } - - - private void checkPermissions() { - if (isVoiceOnlyCall) { - onMicrophoneClick(); - } else if (getActivity() != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - requestPermissions(PERMISSIONS_CALL, 100); - } else { - onRequestPermissionsResult(100, PERMISSIONS_CALL, new int[]{1, 1}); - } - } - - } - - private boolean isConnectionEstablished() { - return (currentCallStatus.equals(CallStatus.ESTABLISHED) || currentCallStatus.equals(CallStatus.IN_CONVERSATION)); - } - - @AfterPermissionGranted(100) - private void onPermissionsGranted() { - if (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CALL)) { - if (!videoOn && !isVoiceOnlyCall) { - onCameraClick(); - } - - if (!audioOn) { - onMicrophoneClick(); - } - - if (!isVoiceOnlyCall) { - if (cameraEnumerator.getDeviceNames().length == 0) { - cameraControlButton.setVisibility(View.GONE); - } - - if (cameraEnumerator.getDeviceNames().length > 1) { - cameraSwitchButton.setVisibility(View.VISIBLE); - } - } - - if (!isConnectionEstablished()) { - fetchSignalingSettings(); - } - } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(), - PERMISSIONS_CALL)) { - checkIfSomeAreApproved(); - } - - } - - private void checkIfSomeAreApproved() { - if (!isVoiceOnlyCall) { - if (cameraEnumerator.getDeviceNames().length == 0) { - cameraControlButton.setVisibility(View.GONE); - } - - if (cameraEnumerator.getDeviceNames().length > 1) { - cameraSwitchButton.setVisibility(View.VISIBLE); - } - - if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA)) { - if (!videoOn) { - onCameraClick(); - } - } else { - cameraControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_off_white_24px); - cameraControlButton.setAlpha(0.7f); - cameraSwitchButton.setVisibility(View.GONE); - } - } - - if (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_MICROPHONE)) { - if (!audioOn) { - onMicrophoneClick(); - } - } else { - microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px); - } - - if (!isConnectionEstablished()) { - fetchSignalingSettings(); - } - } - - @AfterPermissionDenied(100) - private void onPermissionsDenied() { - if (!isVoiceOnlyCall) { - if (cameraEnumerator.getDeviceNames().length == 0) { - cameraControlButton.setVisibility(View.GONE); - } else if (cameraEnumerator.getDeviceNames().length == 1) { - cameraSwitchButton.setVisibility(View.GONE); - } - } - - if (getActivity() != null && (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA) || - EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_MICROPHONE))) { - checkIfSomeAreApproved(); - } else if (!isConnectionEstablished()) { - fetchSignalingSettings(); - } - } - - private void onAudioManagerDevicesChanged( - final MagicAudioManager.AudioDevice device, final Set availableDevices) { - Log.d(TAG, "onAudioManagerDevicesChanged: " + availableDevices + ", " - + "selected: " + device); - - final boolean shouldDisableProximityLock = (device.equals(MagicAudioManager.AudioDevice.WIRED_HEADSET) - || device.equals(MagicAudioManager.AudioDevice.SPEAKER_PHONE) - || device.equals(MagicAudioManager.AudioDevice.BLUETOOTH)); - - if (shouldDisableProximityLock) { - powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITHOUT_PROXIMITY_SENSOR_LOCK); - } else { - powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITH_PROXIMITY_SENSOR_LOCK); - } - } - - - private void cameraInitialization() { - videoCapturer = createCameraCapturer(cameraEnumerator); - - //Create a VideoSource instance - if (videoCapturer != null) { - videoSource = peerConnectionFactory.createVideoSource(videoCapturer); - localVideoTrack = peerConnectionFactory.createVideoTrack("NCv0", videoSource); - localMediaStream.addTrack(localVideoTrack); - localVideoTrack.setEnabled(false); - localVideoTrack.addSink(pipVideoView); - } - - } - - private void microphoneInitialization() { - //create an AudioSource instance - audioSource = peerConnectionFactory.createAudioSource(audioConstraints); - localAudioTrack = peerConnectionFactory.createAudioTrack("NCa0", audioSource); - localAudioTrack.setEnabled(false); - localMediaStream.addTrack(localAudioTrack); - } - - private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) { - final String[] deviceNames = enumerator.getDeviceNames(); - - // First, try to find front facing camera - Logging.d(TAG, "Looking for front facing cameras."); - for (String deviceName : deviceNames) { - if (enumerator.isFrontFacing(deviceName)) { - Logging.d(TAG, "Creating front facing camera capturer."); - VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null); - if (videoCapturer != null) { - pipVideoView.setMirror(true); - return videoCapturer; - } - } - } - - - // Front facing camera not found, try something else - Logging.d(TAG, "Looking for other cameras."); - for (String deviceName : deviceNames) { - if (!enumerator.isFrontFacing(deviceName)) { - Logging.d(TAG, "Creating other camera capturer."); - VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null); - - if (videoCapturer != null) { - pipVideoView.setMirror(false); - return videoCapturer; - } - } - } - - return null; - } - - @OnLongClick(R.id.call_control_microphone) - boolean onMicrophoneLongClick() { - if (!audioOn) { - callControlHandler.removeCallbacksAndMessages(null); - cameraSwitchHandler.removeCallbacksAndMessages(null); - isPTTActive = true; - callControls.setVisibility(View.VISIBLE); - if (!isVoiceOnlyCall) { - cameraSwitchButton.setVisibility(View.VISIBLE); - } - } - - onMicrophoneClick(); - return true; - } - - @OnClick(R.id.callControlEnableSpeaker) - public void onEnableSpeakerphoneClick() { - if (audioManager != null) { - audioManager.toggleUseSpeakerphone(); - if (audioManager.isSpeakerphoneAutoOn()) { - callControlEnableSpeaker.getHierarchy().setPlaceholderImage(R.drawable.ic_volume_up_white_24dp); - } else { - callControlEnableSpeaker.getHierarchy().setPlaceholderImage(R.drawable.ic_volume_mute_white_24dp); - } - } - } - - @OnClick(R.id.call_control_microphone) - public void onMicrophoneClick() { - if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_MICROPHONE)) { - - if (getActivity() != null && !appPreferences.getPushToTalkIntroShown()) { - spotlightView = new SpotlightView.Builder(getActivity()) - .introAnimationDuration(300) - .enableRevealAnimation(true) - .performClick(false) - .fadeinTextDuration(400) - .headingTvColor(getResources().getColor(R.color.colorPrimary)) - .headingTvSize(20) - .headingTvText(getResources().getString(R.string.nc_push_to_talk)) - .subHeadingTvColor(getResources().getColor(R.color.bg_default)) - .subHeadingTvSize(16) - .subHeadingTvText(getResources().getString(R.string.nc_push_to_talk_desc)) - .maskColor(Color.parseColor("#dc000000")) - .target(microphoneControlButton) - .lineAnimDuration(400) - .lineAndArcColor(getResources().getColor(R.color.colorPrimary)) - .enableDismissAfterShown(true) - .dismissOnBackPress(true) - .usageId("pushToTalk") - .show(); - - appPreferences.setPushToTalkIntroShown(true); - } - - if (!isPTTActive) { - audioOn = !audioOn; - - if (audioOn) { - microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_white_24px); - } else { - microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px); - } - - toggleMedia(audioOn, false); - } else { - microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_white_24px); - pulseAnimation.start(); - toggleMedia(true, false); - } - - if (isVoiceOnlyCall && !isConnectionEstablished()) { - fetchSignalingSettings(); - } - - } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(), - PERMISSIONS_MICROPHONE)) { - // Microphone permission is permanently denied so we cannot request it normally. - - OpenAppDetailsDialogFragment.show( - R.string.nc_microphone_permission_permanently_denied, - R.string.nc_permissions_settings, (AppCompatActivity) getActivity()); - } else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - requestPermissions(PERMISSIONS_MICROPHONE, 100); - } else { - onRequestPermissionsResult(100, PERMISSIONS_MICROPHONE, new int[]{1}); - } - } - } - - @OnClick(R.id.callControlHangupView) - void onHangupClick() { - setCallState(CallStatus.LEAVING); - hangup(true); - } - - @OnClick(R.id.call_control_camera) - public void onCameraClick() { - if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA)) { - videoOn = !videoOn; - - if (videoOn) { - cameraControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_white_24px); - if (cameraEnumerator.getDeviceNames().length > 1) { - cameraSwitchButton.setVisibility(View.VISIBLE); - } - } else { - cameraControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_off_white_24px); - cameraSwitchButton.setVisibility(View.GONE); - } - - toggleMedia(videoOn, true); - } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(), - PERMISSIONS_CAMERA)) { - // Camera permission is permanently denied so we cannot request it normally. - OpenAppDetailsDialogFragment.show( - R.string.nc_camera_permission_permanently_denied, - R.string.nc_permissions_settings, (AppCompatActivity) getActivity()); - } else { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - requestPermissions(PERMISSIONS_CAMERA, 100); - } else { - onRequestPermissionsResult(100, PERMISSIONS_CAMERA, new int[]{1}); - } - } - - } - - @OnClick({R.id.call_control_switch_camera, R.id.pip_video_view}) - public void switchCamera() { - CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer; - if (cameraVideoCapturer != null) { - cameraVideoCapturer.switchCamera(new CameraVideoCapturer.CameraSwitchHandler() { - @Override - public void onCameraSwitchDone(boolean currentCameraIsFront) { - pipVideoView.setMirror(currentCameraIsFront); - } - - @Override - public void onCameraSwitchError(String s) { - - } - }); - } - } - - private void toggleMedia(boolean enable, boolean video) { - String message; - if (video) { - message = "videoOff"; - if (enable) { - cameraControlButton.setAlpha(1.0f); - message = "videoOn"; - startVideoCapture(); - } else { - cameraControlButton.setAlpha(0.7f); - if (videoCapturer != null) { - try { - videoCapturer.stopCapture(); - } catch (InterruptedException e) { - Log.d(TAG, "Failed to stop capturing video while sensor is near the ear"); - } - } - } - - if (localMediaStream != null && localMediaStream.videoTracks.size() > 0) { - localMediaStream.videoTracks.get(0).setEnabled(enable); - } - if (enable) { - pipVideoView.setVisibility(View.VISIBLE); - } else { - pipVideoView.setVisibility(View.INVISIBLE); - } - } else { - message = "audioOff"; - if (enable) { - message = "audioOn"; - microphoneControlButton.setAlpha(1.0f); - } else { - microphoneControlButton.setAlpha(0.7f); - } - - if (localMediaStream != null && localMediaStream.audioTracks.size() > 0) { - localMediaStream.audioTracks.get(0).setEnabled(enable); - } - } - - if (isConnectionEstablished()) { - if (!hasMCU) { - for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { - magicPeerConnectionWrapperList.get(i).sendChannelData(new DataChannelMessage(message)); - } - } else { - for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { - if (magicPeerConnectionWrapperList.get(i).getSessionId().equals(webSocketClient.getSessionId())) { - magicPeerConnectionWrapperList.get(i).sendChannelData(new DataChannelMessage(message)); - break; - - } - } - } - } - } - - - private void animateCallControls(boolean show, long startDelay) { - if (isVoiceOnlyCall) { - if (spotlightView != null && spotlightView.getVisibility() != View.GONE) { - spotlightView.setVisibility(View.GONE); - } - } else if (!isPTTActive) { - float alpha; - long duration; - - if (show) { - callControlHandler.removeCallbacksAndMessages(null); - cameraSwitchHandler.removeCallbacksAndMessages(null); - alpha = 1.0f; - duration = 1000; - if (callControls.getVisibility() != View.VISIBLE) { - callControls.setAlpha(0.0f); - callControls.setVisibility(View.VISIBLE); - - cameraSwitchButton.setAlpha(0.0f); - cameraSwitchButton.setVisibility(View.VISIBLE); - } else { - callControlHandler.postDelayed(() -> animateCallControls(false, 0), 5000); - return; - } - } else { - alpha = 0.0f; - duration = 1000; - } - - if (callControls != null) { - callControls.setEnabled(false); - callControls.animate() - .translationY(0) - .alpha(alpha) - .setDuration(duration) - .setStartDelay(startDelay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - if (callControls != null) { - if (!show) { - callControls.setVisibility(View.GONE); - if (spotlightView != null && spotlightView.getVisibility() != View.GONE) { - spotlightView.setVisibility(View.GONE); - } - } else { - callControlHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (!isPTTActive) { - animateCallControls(false, 0); - } - } - }, 7500); - } - - callControls.setEnabled(true); - } - } - }); - } - - if (cameraSwitchButton != null) { - cameraSwitchButton.setEnabled(false); - cameraSwitchButton.animate() - .translationY(0) - .alpha(alpha) - .setDuration(duration) - .setStartDelay(startDelay) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - if (cameraSwitchButton != null) { - if (!show) { - cameraSwitchButton.setVisibility(View.GONE); - } - - cameraSwitchButton.setEnabled(true); - } - } - }); - } - - } - } - - @Override - public void onDestroy() { - if (!currentCallStatus.equals(CallStatus.LEAVING)) { - onHangupClick(); - } - powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.IDLE); - super.onDestroy(); - } - - private void fetchSignalingSettings() { - ncApi.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(baseUrl)) - .subscribeOn(Schedulers.io()) - .retry(3) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(SignalingSettingsOverall signalingSettingsOverall) { - IceServer iceServer; - if (signalingSettingsOverall != null && signalingSettingsOverall.getOcs() != null && - signalingSettingsOverall.getOcs().getSettings() != null) { - - externalSignalingServer = new ExternalSignalingServer(); - - if (!TextUtils.isEmpty(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()) && - !TextUtils.isEmpty(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket())) { - externalSignalingServer = new ExternalSignalingServer(); - externalSignalingServer.setExternalSignalingServer(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()); - externalSignalingServer.setExternalSignalingTicket(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket()); - hasExternalSignalingServer = true; - } else { - hasExternalSignalingServer = false; - } - - if (!conversationUser.getUserId().equals("?")) { - try { - userUtils.createOrUpdateUser(null, null, null, null, null, null, null, - conversationUser.getId(), null, null, LoganSquare.serialize(externalSignalingServer)) - .subscribeOn(Schedulers.io()) - .subscribe(); - } catch (IOException exception) { - Log.e(TAG, "Failed to serialize external signaling server"); - } - } - - if (signalingSettingsOverall.getOcs().getSettings().getStunServers() != null) { - for (int i = 0; i < signalingSettingsOverall.getOcs().getSettings().getStunServers().size(); - i++) { - iceServer = signalingSettingsOverall.getOcs().getSettings().getStunServers().get(i); - if (TextUtils.isEmpty(iceServer.getUsername()) || TextUtils.isEmpty(iceServer - .getCredential())) { - iceServers.add(new PeerConnection.IceServer(iceServer.getUrl())); - } else { - iceServers.add(new PeerConnection.IceServer(iceServer.getUrl(), - iceServer.getUsername(), iceServer.getCredential())); - } - } - } - - if (signalingSettingsOverall.getOcs().getSettings().getTurnServers() != null) { - for (int i = 0; i < signalingSettingsOverall.getOcs().getSettings().getTurnServers().size(); - i++) { - iceServer = signalingSettingsOverall.getOcs().getSettings().getTurnServers().get(i); - for (int j = 0; j < iceServer.getUrls().size(); j++) { - if (TextUtils.isEmpty(iceServer.getUsername()) || TextUtils.isEmpty(iceServer - .getCredential())) { - iceServers.add(new PeerConnection.IceServer(iceServer.getUrls().get(j))); - } else { - iceServers.add(new PeerConnection.IceServer(iceServer.getUrls().get(j), - iceServer.getUsername(), iceServer.getCredential())); - } - } - } - } - } - - checkCapabilities(); - } - - @Override - public void onError(Throwable e) { - } - - @Override - public void onComplete() { - - } - }); - } - - private void checkCapabilities() { - ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) - .retry(3) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(CapabilitiesOverall capabilitiesOverall) { - isMultiSession = capabilitiesOverall.getOcs().getData() - .getCapabilities() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() != null && - capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures().contains("multi-room-users"); - - needsPing = !(capabilitiesOverall.getOcs().getData() - .getCapabilities() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() != null && - capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures().contains("no-ping")); - - if (!hasExternalSignalingServer) { - joinRoomAndCall(); - } else { - setupAndInitiateWebSocketsConnection(); - } - } - - @Override - public void onError(Throwable e) { - isMultiSession = false; - } - - @Override - public void onComplete() { - - } - }); - } - - private void joinRoomAndCall() { - ncApi.joinRoom(credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, - roomToken), conversationPassword) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(3) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - callSession = roomOverall.getOcs().getData().getSessionId(); - ApplicationWideCurrentRoomHolder.getInstance().setSession(callSession); - ApplicationWideCurrentRoomHolder.getInstance().setCurrentRoomId(roomId); - ApplicationWideCurrentRoomHolder.getInstance().setCurrentRoomToken(roomToken); - ApplicationWideCurrentRoomHolder.getInstance().setUserInRoom(conversationUser); - callOrJoinRoomViaWebSocket(); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - private void callOrJoinRoomViaWebSocket() { - if (!hasExternalSignalingServer) { - performCall(); - } else { - webSocketClient.joinRoomWithRoomTokenAndSession(roomToken, callSession); - } - } - - private void performCall() { - ncApi.joinCall(credentials, - ApiUtils.getUrlForCall(baseUrl, roomToken)) - .subscribeOn(Schedulers.io()) - .retry(3) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(GenericOverall genericOverall) { - if (!currentCallStatus.equals(CallStatus.LEAVING)) { - setCallState(CallStatus.ESTABLISHED); - - ApplicationWideCurrentRoomHolder.getInstance().setInCall(true); - - if (needsPing) { - ncApi.pingCall(credentials, ApiUtils.getUrlForCallPing(baseUrl, roomToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .repeatWhen(observable -> observable.delay(5000, TimeUnit.MILLISECONDS)) - .takeWhile(observable -> isConnectionEstablished()) - .retry(3, observable -> isConnectionEstablished()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - pingDisposable = d; - } - - @Override - public void onNext(GenericOverall genericOverall) { - - } - - @Override - public void onError(Throwable e) { - dispose(pingDisposable); - } - - @Override - public void onComplete() { - dispose(pingDisposable); - } - }); - } - - // Start pulling signaling messages - String urlToken = null; - if (isMultiSession) { - urlToken = roomToken; - } - - if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) { - NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomId); - } else if (!TextUtils.isEmpty(roomToken)) { - NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomToken); - } - - if (!hasExternalSignalingServer) { - ncApi.pullSignalingMessages(credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .repeatWhen(observable -> observable) - .takeWhile(observable -> isConnectionEstablished()) - .retry(3, observable -> isConnectionEstablished()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - signalingDisposable = d; - } - - @Override - public void onNext(SignalingOverall signalingOverall) { - if (signalingOverall.getOcs().getSignalings() != null) { - for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); i++) { - try { - receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i)); - } catch (IOException e) { - Log.e(TAG, "Failed to process received signaling" + - " message"); - } - } - } - } - - @Override - public void onError(Throwable e) { - dispose(signalingDisposable); - } - - @Override - public void onComplete() { - dispose(signalingDisposable); - } - }); - - - } - } - } - - @Override - public void onError(Throwable e) { - } - - @Override - public void onComplete() { - - } - }); - } - - private void setupAndInitiateWebSocketsConnection() { - if (webSocketConnectionHelper == null) { - webSocketConnectionHelper = new WebSocketConnectionHelper(); - } - - if (webSocketClient == null) { - webSocketClient = WebSocketConnectionHelper.getExternalSignalingInstanceForServer( - externalSignalingServer.getExternalSignalingServer(), - conversationUser, externalSignalingServer.getExternalSignalingTicket(), - TextUtils.isEmpty(credentials)); - } else { - if (webSocketClient.isConnected() && currentCallStatus.equals(CallStatus.PUBLISHER_FAILED)) { - webSocketClient.restartWebSocket(); - } - } - - joinRoomAndCall(); - } - - private void initiateCall() { - if (!TextUtils.isEmpty(roomToken)) { checkPermissions(); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + private void initViews() { + if (isVoiceOnlyCall) { + callControlEnableSpeaker.setVisibility(View.VISIBLE); + cameraSwitchButton.setVisibility(View.GONE); + cameraControlButton.setVisibility(View.GONE); + pipVideoView.setVisibility(View.GONE); + } else { + if (cameraEnumerator.getDeviceNames().length < 2) { + cameraSwitchButton.setVisibility(View.GONE); + } + + pipVideoView.init(rootEglBase.getEglBaseContext(), null); + pipVideoView.setZOrderMediaOverlay(true); + // disabled because it causes some devices to crash + pipVideoView.setEnableHardwareScaler(false); + pipVideoView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); + } + } + + private void checkPermissions() { + if (isVoiceOnlyCall) { + onMicrophoneClick(); + } else if (getActivity() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(PERMISSIONS_CALL, 100); + } else { + onRequestPermissionsResult(100, PERMISSIONS_CALL, new int[] { 1, 1 }); + } + } + } + + private boolean isConnectionEstablished() { + return (currentCallStatus.equals(CallStatus.ESTABLISHED) || currentCallStatus.equals( + CallStatus.IN_CONVERSATION)); + } + + @AfterPermissionGranted(100) + private void onPermissionsGranted() { + if (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CALL)) { + if (!videoOn && !isVoiceOnlyCall) { + onCameraClick(); + } + + if (!audioOn) { + onMicrophoneClick(); + } + + if (!isVoiceOnlyCall) { + if (cameraEnumerator.getDeviceNames().length == 0) { + cameraControlButton.setVisibility(View.GONE); + } + + if (cameraEnumerator.getDeviceNames().length > 1) { + cameraSwitchButton.setVisibility(View.VISIBLE); + } + } + + if (!isConnectionEstablished()) { + fetchSignalingSettings(); + } + } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied( + getActivity(), + PERMISSIONS_CALL)) { + checkIfSomeAreApproved(); + } + } + + private void checkIfSomeAreApproved() { + if (!isVoiceOnlyCall) { + if (cameraEnumerator.getDeviceNames().length == 0) { + cameraControlButton.setVisibility(View.GONE); + } + + if (cameraEnumerator.getDeviceNames().length > 1) { + cameraSwitchButton.setVisibility(View.VISIBLE); + } + + if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), + PERMISSIONS_CAMERA)) { + if (!videoOn) { + onCameraClick(); + } + } else { + cameraControlButton.getHierarchy() + .setPlaceholderImage(R.drawable.ic_videocam_off_white_24px); + cameraControlButton.setAlpha(0.7f); + cameraSwitchButton.setVisibility(View.GONE); + } + } + + if (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_MICROPHONE)) { + if (!audioOn) { + onMicrophoneClick(); + } + } else { + microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px); + } + + if (!isConnectionEstablished()) { + fetchSignalingSettings(); + } + } + + @AfterPermissionDenied(100) + private void onPermissionsDenied() { + if (!isVoiceOnlyCall) { + if (cameraEnumerator.getDeviceNames().length == 0) { + cameraControlButton.setVisibility(View.GONE); + } else if (cameraEnumerator.getDeviceNames().length == 1) { + cameraSwitchButton.setVisibility(View.GONE); + } + } + + if (getActivity() != null && (EffortlessPermissions.hasPermissions(getActivity(), + PERMISSIONS_CAMERA) || + EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_MICROPHONE))) { + checkIfSomeAreApproved(); + } else if (!isConnectionEstablished()) { + fetchSignalingSettings(); + } + } + + private void onAudioManagerDevicesChanged( + final MagicAudioManager.AudioDevice device, + final Set availableDevices) { + Log.d(TAG, "onAudioManagerDevicesChanged: " + availableDevices + ", " + + "selected: " + device); + + final boolean shouldDisableProximityLock = + (device.equals(MagicAudioManager.AudioDevice.WIRED_HEADSET) + || device.equals(MagicAudioManager.AudioDevice.SPEAKER_PHONE) + || device.equals(MagicAudioManager.AudioDevice.BLUETOOTH)); + + if (shouldDisableProximityLock) { + powerManagerUtils.updatePhoneState( + PowerManagerUtils.PhoneState.WITHOUT_PROXIMITY_SENSOR_LOCK); + } else { + powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITH_PROXIMITY_SENSOR_LOCK); + } + } + + private void cameraInitialization() { + videoCapturer = createCameraCapturer(cameraEnumerator); + + //Create a VideoSource instance + if (videoCapturer != null) { + videoSource = peerConnectionFactory.createVideoSource(videoCapturer); + localVideoTrack = peerConnectionFactory.createVideoTrack("NCv0", videoSource); + localMediaStream.addTrack(localVideoTrack); + localVideoTrack.setEnabled(false); + localVideoTrack.addSink(pipVideoView); + } + } + + private void microphoneInitialization() { + //create an AudioSource instance + audioSource = peerConnectionFactory.createAudioSource(audioConstraints); + localAudioTrack = peerConnectionFactory.createAudioTrack("NCa0", audioSource); + localAudioTrack.setEnabled(false); + localMediaStream.addTrack(localAudioTrack); + } + + private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) { + final String[] deviceNames = enumerator.getDeviceNames(); + + // First, try to find front facing camera + Logging.d(TAG, "Looking for front facing cameras."); + for (String deviceName : deviceNames) { + if (enumerator.isFrontFacing(deviceName)) { + Logging.d(TAG, "Creating front facing camera capturer."); + VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null); + if (videoCapturer != null) { + pipVideoView.setMirror(true); + return videoCapturer; + } + } + } + + // Front facing camera not found, try something else + Logging.d(TAG, "Looking for other cameras."); + for (String deviceName : deviceNames) { + if (!enumerator.isFrontFacing(deviceName)) { + Logging.d(TAG, "Creating other camera capturer."); + VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null); + + if (videoCapturer != null) { + pipVideoView.setMirror(false); + return videoCapturer; + } + } + } + + return null; + } + + @OnLongClick(R.id.call_control_microphone) + boolean onMicrophoneLongClick() { + if (!audioOn) { + callControlHandler.removeCallbacksAndMessages(null); + cameraSwitchHandler.removeCallbacksAndMessages(null); + isPTTActive = true; + callControls.setVisibility(View.VISIBLE); + if (!isVoiceOnlyCall) { + cameraSwitchButton.setVisibility(View.VISIBLE); + } + } + + onMicrophoneClick(); + return true; + } + + @OnClick(R.id.callControlEnableSpeaker) + public void onEnableSpeakerphoneClick() { + if (audioManager != null) { + audioManager.toggleUseSpeakerphone(); + if (audioManager.isSpeakerphoneAutoOn()) { + callControlEnableSpeaker.getHierarchy() + .setPlaceholderImage(R.drawable.ic_volume_up_white_24dp); + } else { + callControlEnableSpeaker.getHierarchy() + .setPlaceholderImage(R.drawable.ic_volume_mute_white_24dp); + } + } + } + + @OnClick(R.id.call_control_microphone) + public void onMicrophoneClick() { + if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), + PERMISSIONS_MICROPHONE)) { + + if (getActivity() != null && !appPreferences.getPushToTalkIntroShown()) { + spotlightView = new SpotlightView.Builder(getActivity()) + .introAnimationDuration(300) + .enableRevealAnimation(true) + .performClick(false) + .fadeinTextDuration(400) + .headingTvColor(getResources().getColor(R.color.colorPrimary)) + .headingTvSize(20) + .headingTvText(getResources().getString(R.string.nc_push_to_talk)) + .subHeadingTvColor(getResources().getColor(R.color.bg_default)) + .subHeadingTvSize(16) + .subHeadingTvText(getResources().getString(R.string.nc_push_to_talk_desc)) + .maskColor(Color.parseColor("#dc000000")) + .target(microphoneControlButton) + .lineAnimDuration(400) + .lineAndArcColor(getResources().getColor(R.color.colorPrimary)) + .enableDismissAfterShown(true) + .dismissOnBackPress(true) + .usageId("pushToTalk") + .show(); + + appPreferences.setPushToTalkIntroShown(true); + } + + if (!isPTTActive) { + audioOn = !audioOn; + + if (audioOn) { + microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_white_24px); } else { - handleFromNotification(); - } - } - - @Override - protected void onDetach(@NonNull View view) { - eventBus.unregister(this); - super.onDetach(view); - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - eventBus.register(this); - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(WebSocketCommunicationEvent webSocketCommunicationEvent) { - switch (webSocketCommunicationEvent.getType()) { - case "hello": - if (!webSocketCommunicationEvent.getHashMap().containsKey("oldResumeId")) { - if (currentCallStatus.equals(CallStatus.RECONNECTING)) { - hangup(false); - } else { - initiateCall(); - } - } else { - } - break; - case "roomJoined": - startSendingNick(); - - if (webSocketCommunicationEvent.getHashMap().get("roomToken").equals(roomToken)) { - performCall(); - } - break; - case "participantsUpdate": - if (webSocketCommunicationEvent.getHashMap().get("roomToken").equals(roomToken)) { - processUsersInRoom((List>) webSocketClient.getJobWithId(Integer.valueOf(webSocketCommunicationEvent.getHashMap().get("jobId")))); - } - break; - case "signalingMessage": - processMessage((NCSignalingMessage) webSocketClient.getJobWithId(Integer.valueOf(webSocketCommunicationEvent.getHashMap().get("jobId")))); - break; - case "peerReadyForRequestingOffer": - webSocketClient.requestOfferForSessionIdWithType(webSocketCommunicationEvent.getHashMap().get("sessionId"), "video"); - break; - } - } - - @OnClick({R.id.pip_video_view, R.id.remote_renderers_layout}) - public void showCallControls() { - animateCallControls(true, 0); - } - - private void dispose(@Nullable Disposable disposable) { - if (disposable != null && !disposable.isDisposed()) { - disposable.dispose(); - } else if (disposable == null) { - - if (pingDisposable != null && !pingDisposable.isDisposed()) { - pingDisposable.dispose(); - pingDisposable = null; - } - - if (signalingDisposable != null && !signalingDisposable.isDisposed()) { - signalingDisposable.dispose(); - signalingDisposable = null; - } - } - } - - private void receivedSignalingMessage(Signaling signaling) throws IOException { - String messageType = signaling.getType(); - - if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CALLING)) { - return; + microphoneControlButton.getHierarchy() + .setPlaceholderImage(R.drawable.ic_mic_off_white_24px); } - if ("usersInRoom".equals(messageType)) { - processUsersInRoom((List>) signaling.getMessageWrapper()); - } else if ("message".equals(messageType)) { - NCSignalingMessage ncSignalingMessage = LoganSquare.parse(signaling.getMessageWrapper().toString(), - NCSignalingMessage.class); - processMessage(ncSignalingMessage); - } else { - Log.d(TAG, "Something went very very wrong"); + toggleMedia(audioOn, false); + } else { + microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_white_24px); + pulseAnimation.start(); + toggleMedia(true, false); + } + + if (isVoiceOnlyCall && !isConnectionEstablished()) { + fetchSignalingSettings(); + } + } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied( + getActivity(), + PERMISSIONS_MICROPHONE)) { + // Microphone permission is permanently denied so we cannot request it normally. + + OpenAppDetailsDialogFragment.show( + R.string.nc_microphone_permission_permanently_denied, + R.string.nc_permissions_settings, (AppCompatActivity) getActivity()); + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(PERMISSIONS_MICROPHONE, 100); + } else { + onRequestPermissionsResult(100, PERMISSIONS_MICROPHONE, new int[] { 1 }); + } + } + } + + @OnClick(R.id.callControlHangupView) + void onHangupClick() { + setCallState(CallStatus.LEAVING); + hangup(true); + } + + @OnClick(R.id.call_control_camera) + public void onCameraClick() { + if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), + PERMISSIONS_CAMERA)) { + videoOn = !videoOn; + + if (videoOn) { + cameraControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_white_24px); + if (cameraEnumerator.getDeviceNames().length > 1) { + cameraSwitchButton.setVisibility(View.VISIBLE); } + } else { + cameraControlButton.getHierarchy() + .setPlaceholderImage(R.drawable.ic_videocam_off_white_24px); + cameraSwitchButton.setVisibility(View.GONE); + } + + toggleMedia(videoOn, true); + } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied( + getActivity(), + PERMISSIONS_CAMERA)) { + // Camera permission is permanently denied so we cannot request it normally. + OpenAppDetailsDialogFragment.show( + R.string.nc_camera_permission_permanently_denied, + R.string.nc_permissions_settings, (AppCompatActivity) getActivity()); + } else { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(PERMISSIONS_CAMERA, 100); + } else { + onRequestPermissionsResult(100, PERMISSIONS_CAMERA, new int[] { 1 }); + } + } + } + + @OnClick({ R.id.call_control_switch_camera, R.id.pip_video_view }) + public void switchCamera() { + CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer; + if (cameraVideoCapturer != null) { + cameraVideoCapturer.switchCamera(new CameraVideoCapturer.CameraSwitchHandler() { + @Override + public void onCameraSwitchDone(boolean currentCameraIsFront) { + pipVideoView.setMirror(currentCameraIsFront); + } + + @Override + public void onCameraSwitchError(String s) { + + } + }); + } + } + + private void toggleMedia(boolean enable, boolean video) { + String message; + if (video) { + message = "videoOff"; + if (enable) { + cameraControlButton.setAlpha(1.0f); + message = "videoOn"; + startVideoCapture(); + } else { + cameraControlButton.setAlpha(0.7f); + if (videoCapturer != null) { + try { + videoCapturer.stopCapture(); + } catch (InterruptedException e) { + Log.d(TAG, "Failed to stop capturing video while sensor is near the ear"); + } + } + } + + if (localMediaStream != null && localMediaStream.videoTracks.size() > 0) { + localMediaStream.videoTracks.get(0).setEnabled(enable); + } + if (enable) { + pipVideoView.setVisibility(View.VISIBLE); + } else { + pipVideoView.setVisibility(View.INVISIBLE); + } + } else { + message = "audioOff"; + if (enable) { + message = "audioOn"; + microphoneControlButton.setAlpha(1.0f); + } else { + microphoneControlButton.setAlpha(0.7f); + } + + if (localMediaStream != null && localMediaStream.audioTracks.size() > 0) { + localMediaStream.audioTracks.get(0).setEnabled(enable); + } } - private void processMessage(NCSignalingMessage ncSignalingMessage) { - if (ncSignalingMessage.getRoomType().equals("video") || ncSignalingMessage.getRoomType().equals("screen")) { - MagicPeerConnectionWrapper magicPeerConnectionWrapper = - getPeerConnectionWrapperForSessionIdAndType(ncSignalingMessage.getFrom(), - ncSignalingMessage.getRoomType(), false); - - String type = null; - if (ncSignalingMessage.getPayload() != null && ncSignalingMessage.getPayload().getType() != null) { - type = ncSignalingMessage.getPayload().getType(); - } else if (ncSignalingMessage.getType() != null) { - type = ncSignalingMessage.getType(); - } - - if (type != null) { - switch (type) { - case "unshareScreen": - endPeerConnection(ncSignalingMessage.getFrom(), true); - break; - case "offer": - case "answer": - magicPeerConnectionWrapper.setNick(ncSignalingMessage.getPayload().getNick()); - SessionDescription sessionDescriptionWithPreferredCodec; - - String sessionDescriptionStringWithPreferredCodec = MagicWebRTCUtils.preferCodec - (ncSignalingMessage.getPayload().getSdp(), - "H264", false); - - sessionDescriptionWithPreferredCodec = new SessionDescription( - SessionDescription.Type.fromCanonicalForm(type), - sessionDescriptionStringWithPreferredCodec); - - if (magicPeerConnectionWrapper.getPeerConnection() != null) { - magicPeerConnectionWrapper.getPeerConnection().setRemoteDescription(magicPeerConnectionWrapper - .getMagicSdpObserver(), sessionDescriptionWithPreferredCodec); - } - break; - case "candidate": - NCIceCandidate ncIceCandidate = ncSignalingMessage.getPayload().getIceCandidate(); - IceCandidate iceCandidate = new IceCandidate(ncIceCandidate.getSdpMid(), - ncIceCandidate.getSdpMLineIndex(), ncIceCandidate.getCandidate()); - magicPeerConnectionWrapper.addCandidate(iceCandidate); - break; - case "endOfCandidates": - magicPeerConnectionWrapper.drainIceCandidates(); - break; - default: - break; - } - } - } else { - Log.d(TAG, "Something went very very wrong"); - } - } - - private void hangup(boolean shutDownView) { - stopCallingSound(); - dispose(null); - - if (shutDownView) { - - if (videoCapturer != null) { - try { - videoCapturer.stopCapture(); - } catch (InterruptedException e) { - Log.e(TAG, "Failed to stop capturing while hanging up"); - } - videoCapturer.dispose(); - videoCapturer = null; - } - - if (pipVideoView != null) { - pipVideoView.release(); - } - - if (audioSource != null) { - audioSource.dispose(); - audioSource = null; - } - - if (audioManager != null) { - audioManager.stop(); - audioManager = null; - } - - if (videoSource != null) { - videoSource = null; - } - - if (peerConnectionFactory != null) { - peerConnectionFactory = null; - } - - localMediaStream = null; - localAudioTrack = null; - localVideoTrack = null; - - - if (TextUtils.isEmpty(credentials) && hasExternalSignalingServer) { - WebSocketConnectionHelper.deleteExternalSignalingInstanceForUserEntity(-1); - } - } - + if (isConnectionEstablished()) { + if (!hasMCU) { for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { - endPeerConnection(magicPeerConnectionWrapperList.get(i).getSessionId(), false); + magicPeerConnectionWrapperList.get(i).sendChannelData(new DataChannelMessage(message)); } - - hangupNetworkCalls(shutDownView); + } else { + for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { + if (magicPeerConnectionWrapperList.get(i) + .getSessionId() + .equals(webSocketClient.getSessionId())) { + magicPeerConnectionWrapperList.get(i).sendChannelData(new DataChannelMessage(message)); + break; + } + } + } } + } - private void hangupNetworkCalls(boolean shutDownView) { - ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) + private void animateCallControls(boolean show, long startDelay) { + if (isVoiceOnlyCall) { + if (spotlightView != null && spotlightView.getVisibility() != View.GONE) { + spotlightView.setVisibility(View.GONE); + } + } else if (!isPTTActive) { + float alpha; + long duration; + + if (show) { + callControlHandler.removeCallbacksAndMessages(null); + cameraSwitchHandler.removeCallbacksAndMessages(null); + alpha = 1.0f; + duration = 1000; + if (callControls.getVisibility() != View.VISIBLE) { + callControls.setAlpha(0.0f); + callControls.setVisibility(View.VISIBLE); + + cameraSwitchButton.setAlpha(0.0f); + cameraSwitchButton.setVisibility(View.VISIBLE); + } else { + callControlHandler.postDelayed(() -> animateCallControls(false, 0), 5000); + return; + } + } else { + alpha = 0.0f; + duration = 1000; + } + + if (callControls != null) { + callControls.setEnabled(false); + callControls.animate() + .translationY(0) + .alpha(alpha) + .setDuration(duration) + .setStartDelay(startDelay) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (callControls != null) { + if (!show) { + callControls.setVisibility(View.GONE); + if (spotlightView != null && spotlightView.getVisibility() != View.GONE) { + spotlightView.setVisibility(View.GONE); + } + } else { + callControlHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (!isPTTActive) { + animateCallControls(false, 0); + } + } + }, 7500); + } + + callControls.setEnabled(true); + } + } + }); + } + + if (cameraSwitchButton != null) { + cameraSwitchButton.setEnabled(false); + cameraSwitchButton.animate() + .translationY(0) + .alpha(alpha) + .setDuration(duration) + .setStartDelay(startDelay) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (cameraSwitchButton != null) { + if (!show) { + cameraSwitchButton.setVisibility(View.GONE); + } + + cameraSwitchButton.setEnabled(true); + } + } + }); + } + } + } + + @Override + public void onDestroy() { + if (!currentCallStatus.equals(CallStatus.LEAVING)) { + onHangupClick(); + } + powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.IDLE); + super.onDestroy(); + } + + private void fetchSignalingSettings() { + ncApi.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(baseUrl)) + .subscribeOn(Schedulers.io()) + .retry(3) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(SignalingSettingsOverall signalingSettingsOverall) { + IceServer iceServer; + if (signalingSettingsOverall != null && signalingSettingsOverall.getOcs() != null && + signalingSettingsOverall.getOcs().getSettings() != null) { + + externalSignalingServer = new ExternalSignalingServer(); + + if (!TextUtils.isEmpty( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()) && + !TextUtils.isEmpty(signalingSettingsOverall.getOcs() + .getSettings() + .getExternalSignalingTicket())) { + externalSignalingServer = new ExternalSignalingServer(); + externalSignalingServer.setExternalSignalingServer( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()); + externalSignalingServer.setExternalSignalingTicket( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket()); + hasExternalSignalingServer = true; + } else { + hasExternalSignalingServer = false; + } + + if (!conversationUser.getUserId().equals("?")) { + try { + userUtils.createOrUpdateUser(null, null, null, null, null, null, null, + conversationUser.getId(), null, null, + LoganSquare.serialize(externalSignalingServer)) + .subscribeOn(Schedulers.io()) + .subscribe(); + } catch (IOException exception) { + Log.e(TAG, "Failed to serialize external signaling server"); + } + } + + if (signalingSettingsOverall.getOcs().getSettings().getStunServers() != null) { + for (int i = 0; + i < signalingSettingsOverall.getOcs().getSettings().getStunServers().size(); + i++) { + iceServer = + signalingSettingsOverall.getOcs().getSettings().getStunServers().get(i); + if (TextUtils.isEmpty(iceServer.getUsername()) || TextUtils.isEmpty(iceServer + .getCredential())) { + iceServers.add(new PeerConnection.IceServer(iceServer.getUrl())); + } else { + iceServers.add(new PeerConnection.IceServer(iceServer.getUrl(), + iceServer.getUsername(), iceServer.getCredential())); + } + } + } + + if (signalingSettingsOverall.getOcs().getSettings().getTurnServers() != null) { + for (int i = 0; + i < signalingSettingsOverall.getOcs().getSettings().getTurnServers().size(); + i++) { + iceServer = + signalingSettingsOverall.getOcs().getSettings().getTurnServers().get(i); + for (int j = 0; j < iceServer.getUrls().size(); j++) { + if (TextUtils.isEmpty(iceServer.getUsername()) || TextUtils.isEmpty(iceServer + .getCredential())) { + iceServers.add(new PeerConnection.IceServer(iceServer.getUrls().get(j))); + } else { + iceServers.add(new PeerConnection.IceServer(iceServer.getUrls().get(j), + iceServer.getUsername(), iceServer.getCredential())); + } + } + } + } + } + + checkCapabilities(); + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onComplete() { + + } + }); + } + + private void checkCapabilities() { + ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) + .retry(3) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(CapabilitiesOverall capabilitiesOverall) { + isMultiSession = capabilitiesOverall.getOcs().getData() + .getCapabilities() != null && capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() != null && + capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures() != null && capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures().contains("multi-room-users"); + + needsPing = !(capabilitiesOverall.getOcs().getData() + .getCapabilities() != null && capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() != null && + capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures() != null && capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures().contains("no-ping")); + + if (!hasExternalSignalingServer) { + joinRoomAndCall(); + } else { + setupAndInitiateWebSocketsConnection(); + } + } + + @Override + public void onError(Throwable e) { + isMultiSession = false; + } + + @Override + public void onComplete() { + + } + }); + } + + private void joinRoomAndCall() { + ncApi.joinRoom(credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, + roomToken), conversationPassword) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(3) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(RoomOverall roomOverall) { + callSession = roomOverall.getOcs().getData().getSessionId(); + ApplicationWideCurrentRoomHolder.getInstance().setSession(callSession); + ApplicationWideCurrentRoomHolder.getInstance().setCurrentRoomId(roomId); + ApplicationWideCurrentRoomHolder.getInstance().setCurrentRoomToken(roomToken); + ApplicationWideCurrentRoomHolder.getInstance().setUserInRoom(conversationUser); + callOrJoinRoomViaWebSocket(); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + private void callOrJoinRoomViaWebSocket() { + if (!hasExternalSignalingServer) { + performCall(); + } else { + webSocketClient.joinRoomWithRoomTokenAndSession(roomToken, callSession); + } + } + + private void performCall() { + ncApi.joinCall(credentials, + ApiUtils.getUrlForCall(baseUrl, roomToken)) + .subscribeOn(Schedulers.io()) + .retry(3) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(GenericOverall genericOverall) { + if (!currentCallStatus.equals(CallStatus.LEAVING)) { + setCallState(CallStatus.ESTABLISHED); + + ApplicationWideCurrentRoomHolder.getInstance().setInCall(true); + + if (needsPing) { + ncApi.pingCall(credentials, ApiUtils.getUrlForCallPing(baseUrl, roomToken)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) + .repeatWhen(observable -> observable.delay(5000, TimeUnit.MILLISECONDS)) + .takeWhile(observable -> isConnectionEstablished()) + .retry(3, observable -> isConnectionEstablished()) .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void onSubscribe(Disposable d) { + pingDisposable = d; + } - } + @Override + public void onNext(GenericOverall genericOverall) { - @Override - public void onNext(GenericOverall genericOverall) { - if (!TextUtils.isEmpty(credentials) && hasExternalSignalingServer) { - webSocketClient.joinRoomWithRoomTokenAndSession("", callSession); - } + } - if (isMultiSession) { - if (shutDownView && getActivity() != null) { - getActivity().finish(); - } else if (!shutDownView && (currentCallStatus.equals(CallStatus.RECONNECTING) || currentCallStatus.equals(CallStatus.PUBLISHER_FAILED))) { - initiateCall(); - } - } else { - leaveRoom(shutDownView); - } - } + @Override + public void onError(Throwable e) { + dispose(pingDisposable); + } - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } + @Override + public void onComplete() { + dispose(pingDisposable); + } }); - } + } - private void leaveRoom(boolean shutDownView) { - ncApi.leaveRoom(credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, roomToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(GenericOverall genericOverall) { - if (shutDownView && getActivity() != null) { - getActivity().finish(); - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - private void startVideoCapture() { - if (videoCapturer != null) { - videoCapturer.startCapture(1280, 720, 30); - } - } - - private void processUsersInRoom(List> users) { - List newSessions = new ArrayList<>(); - Set oldSesssions = new HashSet<>(); - - for (HashMap participant : users) { - if (!participant.get("sessionId").equals(callSession)) { - Object inCallObject = participant.get("inCall"); - boolean isNewSession; - if (inCallObject instanceof Boolean) { - isNewSession = (boolean) inCallObject; - } else { - isNewSession = ((long) inCallObject) != 0; - } - - if (isNewSession) { - newSessions.add(participant.get("sessionId").toString()); - } else { - oldSesssions.add(participant.get("sessionId").toString()); - } - } - } - - for (MagicPeerConnectionWrapper magicPeerConnectionWrapper : magicPeerConnectionWrapperList) { - if (!magicPeerConnectionWrapper.isMCUPublisher()) { - oldSesssions.add(magicPeerConnectionWrapper.getSessionId()); - } - } - - // Calculate sessions that left the call - oldSesssions.removeAll(newSessions); - - // Calculate sessions that join the call - newSessions.removeAll(oldSesssions); - - if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CALLING)) { - return; - } - - if (newSessions.size() > 0 && !hasMCU) { - getPeersForCall(); - } - - hasMCU = hasExternalSignalingServer && webSocketClient != null && webSocketClient.hasMCU(); - - for (String sessionId : newSessions) { - getPeerConnectionWrapperForSessionIdAndType(sessionId, "video", hasMCU && sessionId.equals(webSocketClient.getSessionId())); - } - - if (newSessions.size() > 0 && !currentCallStatus.equals(CallStatus.IN_CONVERSATION)) { - setCallState(CallStatus.IN_CONVERSATION); - } - - for (String sessionId : oldSesssions) { - endPeerConnection(sessionId, false); - } - } - - private void getPeersForCall() { - ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) - .subscribeOn(Schedulers.io()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(ParticipantsOverall participantsOverall) { - participantMap = new HashMap<>(); - for (Participant participant : participantsOverall.getOcs().getData()) { - participantMap.put(participant.getSessionId(), participant); - if (getActivity() != null) { - getActivity().runOnUiThread(() -> setupAvatarForSession(participant.getSessionId())); - } - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - private void deleteMagicPeerConnection(MagicPeerConnectionWrapper magicPeerConnectionWrapper) { - magicPeerConnectionWrapper.removePeerConnection(); - magicPeerConnectionWrapperList.remove(magicPeerConnectionWrapper); - } - - private MagicPeerConnectionWrapper getPeerConnectionWrapperForSessionId(String sessionId, String type) { - for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { - if (magicPeerConnectionWrapperList.get(i).getSessionId().equals(sessionId) && magicPeerConnectionWrapperList.get(i).getVideoStreamType().equals(type)) { - return magicPeerConnectionWrapperList.get(i); - } - } - - return null; - } - - private MagicPeerConnectionWrapper getPeerConnectionWrapperForSessionIdAndType(String sessionId, String type, boolean publisher) { - MagicPeerConnectionWrapper magicPeerConnectionWrapper; - if ((magicPeerConnectionWrapper = getPeerConnectionWrapperForSessionId(sessionId, type)) != null) { - return magicPeerConnectionWrapper; - } else { - if (hasMCU && publisher) { - magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraintsForMCU, sessionId, callSession, localMediaStream, true, true, type); - - } else if (hasMCU) { - magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraints, sessionId, callSession, null, false, true, type); - } else { - if (!"screen".equals(type)) { - magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraints, sessionId, callSession, localMediaStream, false, false, type); - } else { - magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraints, sessionId, callSession, null, false, false, type); - } - } - - magicPeerConnectionWrapperList.add(magicPeerConnectionWrapper); - - if (publisher) { - startSendingNick(); - } - - return magicPeerConnectionWrapper; - } - } - - private List getPeerConnectionWrapperListForSessionId(String sessionId) { - List internalList = new ArrayList<>(); - for (MagicPeerConnectionWrapper magicPeerConnectionWrapper : magicPeerConnectionWrapperList) { - if (magicPeerConnectionWrapper.getSessionId().equals(sessionId)) { - internalList.add(magicPeerConnectionWrapper); - } - } - - return internalList; - } - - private void endPeerConnection(String sessionId, boolean justScreen) { - List magicPeerConnectionWrappers; - MagicPeerConnectionWrapper magicPeerConnectionWrapper; - if (!(magicPeerConnectionWrappers = getPeerConnectionWrapperListForSessionId(sessionId)).isEmpty() - && getActivity() != null) { - for (int i = 0; i < magicPeerConnectionWrappers.size(); i++) { - magicPeerConnectionWrapper = magicPeerConnectionWrappers.get(i); - if (magicPeerConnectionWrapper.getSessionId().equals(sessionId)) { - if (magicPeerConnectionWrapper.getVideoStreamType().equals("screen") || !justScreen) { - MagicPeerConnectionWrapper finalMagicPeerConnectionWrapper = magicPeerConnectionWrapper; - getActivity().runOnUiThread(() -> removeMediaStream(sessionId + "+" + - finalMagicPeerConnectionWrapper.getVideoStreamType())); - deleteMagicPeerConnection(magicPeerConnectionWrapper); - } - } - } - } - } - - private void removeMediaStream(String sessionId) { - if (remoteRenderersLayout != null && remoteRenderersLayout.getChildCount() > 0) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); - if (relativeLayout != null) { - SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); - surfaceViewRenderer.release(); - remoteRenderersLayout.removeView(relativeLayout); - remoteRenderersLayout.invalidate(); - } - } - - if (callControls != null) { - callControls.setZ(100.0f); - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) { - powerManagerUtils.setOrientation(Objects.requireNonNull(getResources()).getConfiguration().orientation); - - - if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL); - } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL); - } - - setPipVideoViewDimensions(); - } - - private void setPipVideoViewDimensions() { - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) pipVideoView.getLayoutParams(); - - if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL); - layoutParams.height = (int) getResources().getDimension(R.dimen.large_preview_dimension); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - pipVideoView.setLayoutParams(layoutParams); - } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL); - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.width = (int) getResources().getDimension(R.dimen.large_preview_dimension); - pipVideoView.setLayoutParams(layoutParams); - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) { - if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType - .PEER_CLOSED)) { - endPeerConnection(peerConnectionEvent.getSessionId(), peerConnectionEvent.getVideoStreamType().equals("screen")); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_FAR) || - peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_NEAR)) { - - if (!isVoiceOnlyCall) { - boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_FAR) && videoOn; - if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA) && - (currentCallStatus.equals(CallStatus.CALLING) || isConnectionEstablished()) && videoOn - && enableVideo != localVideoTrack.enabled()) { - toggleMedia(enableVideo, true); - } - } - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.NICK_CHANGE)) { - gotNick(peerConnectionEvent.getSessionId(), peerConnectionEvent.getNick(), true, peerConnectionEvent.getVideoStreamType()); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) { - gotAudioOrVideoChange(true, peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), - peerConnectionEvent.getChangeValue()); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.AUDIO_CHANGE)) { - gotAudioOrVideoChange(false, peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), - peerConnectionEvent.getChangeValue()); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED)) { - currentCallStatus = CallStatus.PUBLISHER_FAILED; - webSocketClient.clearResumeId(); - hangup(false); - } - } - - private void startSendingNick() { - DataChannelMessageNick dataChannelMessage = new DataChannelMessageNick(); - dataChannelMessage.setType("nickChanged"); - HashMap nickChangedPayload = new HashMap<>(); - nickChangedPayload.put("userid", conversationUser.getUserId()); - nickChangedPayload.put("name", conversationUser.getDisplayName()); - dataChannelMessage.setPayload(nickChangedPayload); - final MagicPeerConnectionWrapper magicPeerConnectionWrapper; - for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { - if (magicPeerConnectionWrapperList.get(i).isMCUPublisher()) { - magicPeerConnectionWrapper = magicPeerConnectionWrapperList.get(i); - Observable - .interval(1, TimeUnit.SECONDS) - .repeatUntil(() -> (!isConnectionEstablished() || isBeingDestroyed() || isDestroyed())) - .observeOn(Schedulers.io()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(Long aLong) { - magicPeerConnectionWrapper.sendNickChannelData(dataChannelMessage); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - break; - } - - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(MediaStreamEvent mediaStreamEvent) { - if (mediaStreamEvent.getMediaStream() != null) { - setupVideoStreamForLayout(mediaStreamEvent.getMediaStream(), mediaStreamEvent.getSession(), - mediaStreamEvent.getMediaStream().videoTracks != null - && mediaStreamEvent.getMediaStream().videoTracks.size() > 0, mediaStreamEvent.getVideoStreamType()); - } else { - setupVideoStreamForLayout(null, mediaStreamEvent.getSession(), false, mediaStreamEvent.getVideoStreamType()); - } - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(SessionDescriptionSendEvent sessionDescriptionSend) throws IOException { - NCMessageWrapper ncMessageWrapper = new NCMessageWrapper(); - ncMessageWrapper.setEv("message"); - ncMessageWrapper.setSessionId(callSession); - NCSignalingMessage ncSignalingMessage = new NCSignalingMessage(); - ncSignalingMessage.setTo(sessionDescriptionSend.getPeerId()); - ncSignalingMessage.setRoomType(sessionDescriptionSend.getVideoStreamType()); - ncSignalingMessage.setType(sessionDescriptionSend.getType()); - NCMessagePayload ncMessagePayload = new NCMessagePayload(); - ncMessagePayload.setType(sessionDescriptionSend.getType()); - - if (!"candidate".equals(sessionDescriptionSend.getType())) { - ncMessagePayload.setSdp(sessionDescriptionSend.getSessionDescription().description); - ncMessagePayload.setNick(conversationUser.getDisplayName()); - } else { - ncMessagePayload.setIceCandidate(sessionDescriptionSend.getNcIceCandidate()); - } - - - // Set all we need - ncSignalingMessage.setPayload(ncMessagePayload); - ncMessageWrapper.setSignalingMessage(ncSignalingMessage); - - - if (!hasExternalSignalingServer) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("{") - .append("\"fn\":\"") - .append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncMessageWrapper.getSignalingMessage()))).append("\"") - .append(",") - .append("\"sessionId\":") - .append("\"").append(StringEscapeUtils.escapeJson(callSession)).append("\"") - .append(",") - .append("\"ev\":\"message\"") - .append("}"); - - List strings = new ArrayList<>(); - String stringToSend = stringBuilder.toString(); - strings.add(stringToSend); - - String urlToken = null; - if (isMultiSession) { + // Start pulling signaling messages + String urlToken = null; + if (isMultiSession) { urlToken = roomToken; - } + } - ncApi.sendSignalingMessages(credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken), - strings.toString()) - .retry(3) + if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty( + roomId)) { + NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom( + getApplicationContext(), conversationUser, roomId); + } else if (!TextUtils.isEmpty(roomToken)) { + NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom( + getApplicationContext(), conversationUser, roomToken); + } + + if (!hasExternalSignalingServer) { + ncApi.pullSignalingMessages(credentials, + ApiUtils.getUrlForSignaling(baseUrl, urlToken)) .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .repeatWhen(observable -> observable) + .takeWhile(observable -> isConnectionEstablished()) + .retry(3, observable -> isConnectionEstablished()) .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void onSubscribe(Disposable d) { + signalingDisposable = d; + } - } - - @Override - public void onNext(SignalingOverall signalingOverall) { - if (signalingOverall.getOcs().getSignalings() != null) { - for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); i++) { - try { - receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i)); - } catch (IOException e) { - e.printStackTrace(); - } - } + @Override + public void onNext(SignalingOverall signalingOverall) { + if (signalingOverall.getOcs().getSignalings() != null) { + for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); + i++) { + try { + receivedSignalingMessage( + signalingOverall.getOcs().getSignalings().get(i)); + } catch (IOException e) { + Log.e(TAG, "Failed to process received signaling" + + " message"); } + } } + } - @Override - public void onError(Throwable e) { - } + @Override + public void onError(Throwable e) { + dispose(signalingDisposable); + } - @Override - public void onComplete() { - - } + @Override + public void onComplete() { + dispose(signalingDisposable); + } }); - } else { - webSocketClient.sendCallMessage(ncMessageWrapper); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - - EffortlessPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, - this); - } - - private void setupAvatarForSession(String session) { - if (remoteRenderersLayout != null) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+video"); - if (relativeLayout != null) { - SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); - - String userId; - - if (hasMCU) { - userId = webSocketClient.getUserIdForSession(session); - } else { - userId = participantMap.get(session).getUserId(); - } - - if (!TextUtils.isEmpty(userId)) { - - if (getActivity() != null) { - avatarImageView.setController(null); - - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(avatarImageView.getController()) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(baseUrl, - userId, - R.dimen.avatar_size_big), null)) - .build(); - avatarImageView.setController(draweeController); - } - } + } } - } + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onComplete() { + + } + }); + } + + private void setupAndInitiateWebSocketsConnection() { + if (webSocketConnectionHelper == null) { + webSocketConnectionHelper = new WebSocketConnectionHelper(); } - private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, String session, boolean enable, String videoStreamType) { - boolean isInitialLayoutSetupForPeer = false; - if (remoteRenderersLayout.findViewWithTag(session) == null) { - setupNewPeerLayout(session, videoStreamType); - isInitialLayoutSetupForPeer = true; + if (webSocketClient == null) { + webSocketClient = WebSocketConnectionHelper.getExternalSignalingInstanceForServer( + externalSignalingServer.getExternalSignalingServer(), + conversationUser, externalSignalingServer.getExternalSignalingTicket(), + TextUtils.isEmpty(credentials)); + } else { + if (webSocketClient.isConnected() && currentCallStatus.equals(CallStatus.PUBLISHER_FAILED)) { + webSocketClient.restartWebSocket(); + } + } + + joinRoomAndCall(); + } + + private void initiateCall() { + if (!TextUtils.isEmpty(roomToken)) { + checkPermissions(); + } else { + handleFromNotification(); + } + } + + @Override + protected void onDetach(@NonNull View view) { + eventBus.unregister(this); + super.onDetach(view); + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + eventBus.register(this); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(WebSocketCommunicationEvent webSocketCommunicationEvent) { + switch (webSocketCommunicationEvent.getType()) { + case "hello": + if (!webSocketCommunicationEvent.getHashMap().containsKey("oldResumeId")) { + if (currentCallStatus.equals(CallStatus.RECONNECTING)) { + hangup(false); + } else { + initiateCall(); + } + } else { + } + break; + case "roomJoined": + startSendingNick(); + + if (webSocketCommunicationEvent.getHashMap().get("roomToken").equals(roomToken)) { + performCall(); + } + break; + case "participantsUpdate": + if (webSocketCommunicationEvent.getHashMap().get("roomToken").equals(roomToken)) { + processUsersInRoom((List>) webSocketClient.getJobWithId( + Integer.valueOf(webSocketCommunicationEvent.getHashMap().get("jobId")))); + } + break; + case "signalingMessage": + processMessage((NCSignalingMessage) webSocketClient.getJobWithId( + Integer.valueOf(webSocketCommunicationEvent.getHashMap().get("jobId")))); + break; + case "peerReadyForRequestingOffer": + webSocketClient.requestOfferForSessionIdWithType( + webSocketCommunicationEvent.getHashMap().get("sessionId"), "video"); + break; + } + } + + @OnClick({ R.id.pip_video_view, R.id.remote_renderers_layout }) + public void showCallControls() { + animateCallControls(true, 0); + } + + private void dispose(@Nullable Disposable disposable) { + if (disposable != null && !disposable.isDisposed()) { + disposable.dispose(); + } else if (disposable == null) { + + if (pingDisposable != null && !pingDisposable.isDisposed()) { + pingDisposable.dispose(); + pingDisposable = null; + } + + if (signalingDisposable != null && !signalingDisposable.isDisposed()) { + signalingDisposable.dispose(); + signalingDisposable = null; + } + } + } + + private void receivedSignalingMessage(Signaling signaling) throws IOException { + String messageType = signaling.getType(); + + if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CALLING)) { + return; + } + + if ("usersInRoom".equals(messageType)) { + processUsersInRoom((List>) signaling.getMessageWrapper()); + } else if ("message".equals(messageType)) { + NCSignalingMessage ncSignalingMessage = + LoganSquare.parse(signaling.getMessageWrapper().toString(), + NCSignalingMessage.class); + processMessage(ncSignalingMessage); + } else { + Log.d(TAG, "Something went very very wrong"); + } + } + + private void processMessage(NCSignalingMessage ncSignalingMessage) { + if (ncSignalingMessage.getRoomType().equals("video") || ncSignalingMessage.getRoomType() + .equals("screen")) { + MagicPeerConnectionWrapper magicPeerConnectionWrapper = + getPeerConnectionWrapperForSessionIdAndType(ncSignalingMessage.getFrom(), + ncSignalingMessage.getRoomType(), false); + + String type = null; + if (ncSignalingMessage.getPayload() != null + && ncSignalingMessage.getPayload().getType() != null) { + type = ncSignalingMessage.getPayload().getType(); + } else if (ncSignalingMessage.getType() != null) { + type = ncSignalingMessage.getType(); + } + + if (type != null) { + switch (type) { + case "unshareScreen": + endPeerConnection(ncSignalingMessage.getFrom(), true); + break; + case "offer": + case "answer": + magicPeerConnectionWrapper.setNick(ncSignalingMessage.getPayload().getNick()); + SessionDescription sessionDescriptionWithPreferredCodec; + + String sessionDescriptionStringWithPreferredCodec = MagicWebRTCUtils.preferCodec + (ncSignalingMessage.getPayload().getSdp(), + "H264", false); + + sessionDescriptionWithPreferredCodec = new SessionDescription( + SessionDescription.Type.fromCanonicalForm(type), + sessionDescriptionStringWithPreferredCodec); + + if (magicPeerConnectionWrapper.getPeerConnection() != null) { + magicPeerConnectionWrapper.getPeerConnection() + .setRemoteDescription(magicPeerConnectionWrapper + .getMagicSdpObserver(), sessionDescriptionWithPreferredCodec); + } + break; + case "candidate": + NCIceCandidate ncIceCandidate = ncSignalingMessage.getPayload().getIceCandidate(); + IceCandidate iceCandidate = new IceCandidate(ncIceCandidate.getSdpMid(), + ncIceCandidate.getSdpMLineIndex(), ncIceCandidate.getCandidate()); + magicPeerConnectionWrapper.addCandidate(iceCandidate); + break; + case "endOfCandidates": + magicPeerConnectionWrapper.drainIceCandidates(); + break; + default: + break; + } + } + } else { + Log.d(TAG, "Something went very very wrong"); + } + } + + private void hangup(boolean shutDownView) { + stopCallingSound(); + dispose(null); + + if (shutDownView) { + + if (videoCapturer != null) { + try { + videoCapturer.stopCapture(); + } catch (InterruptedException e) { + Log.e(TAG, "Failed to stop capturing while hanging up"); + } + videoCapturer.dispose(); + videoCapturer = null; + } + + if (pipVideoView != null) { + pipVideoView.release(); + } + + if (audioSource != null) { + audioSource.dispose(); + audioSource = null; + } + + if (audioManager != null) { + audioManager.stop(); + audioManager = null; + } + + if (videoSource != null) { + videoSource = null; + } + + if (peerConnectionFactory != null) { + peerConnectionFactory = null; + } + + localMediaStream = null; + localAudioTrack = null; + localVideoTrack = null; + + if (TextUtils.isEmpty(credentials) && hasExternalSignalingServer) { + WebSocketConnectionHelper.deleteExternalSignalingInstanceForUserEntity(-1); + } + } + + for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { + endPeerConnection(magicPeerConnectionWrapperList.get(i).getSessionId(), false); + } + + hangupNetworkCalls(shutDownView); + } + + private void hangupNetworkCalls(boolean shutDownView) { + ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(GenericOverall genericOverall) { + if (!TextUtils.isEmpty(credentials) && hasExternalSignalingServer) { + webSocketClient.joinRoomWithRoomTokenAndSession("", callSession); + } + + if (isMultiSession) { + if (shutDownView && getActivity() != null) { + getActivity().finish(); + } else if (!shutDownView && (currentCallStatus.equals(CallStatus.RECONNECTING) + || currentCallStatus.equals(CallStatus.PUBLISHER_FAILED))) { + initiateCall(); + } + } else { + leaveRoom(shutDownView); + } + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + private void leaveRoom(boolean shutDownView) { + ncApi.leaveRoom(credentials, + ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, roomToken)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(GenericOverall genericOverall) { + if (shutDownView && getActivity() != null) { + getActivity().finish(); + } + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + private void startVideoCapture() { + if (videoCapturer != null) { + videoCapturer.startCapture(1280, 720, 30); + } + } + + private void processUsersInRoom(List> users) { + List newSessions = new ArrayList<>(); + Set oldSesssions = new HashSet<>(); + + for (HashMap participant : users) { + if (!participant.get("sessionId").equals(callSession)) { + Object inCallObject = participant.get("inCall"); + boolean isNewSession; + if (inCallObject instanceof Boolean) { + isNewSession = (boolean) inCallObject; + } else { + isNewSession = ((long) inCallObject) != 0; } - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+" + videoStreamType); + if (isNewSession) { + newSessions.add(participant.get("sessionId").toString()); + } else { + oldSesssions.add(participant.get("sessionId").toString()); + } + } + } + + for (MagicPeerConnectionWrapper magicPeerConnectionWrapper : magicPeerConnectionWrapperList) { + if (!magicPeerConnectionWrapper.isMCUPublisher()) { + oldSesssions.add(magicPeerConnectionWrapper.getSessionId()); + } + } + + // Calculate sessions that left the call + oldSesssions.removeAll(newSessions); + + // Calculate sessions that join the call + newSessions.removeAll(oldSesssions); + + if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CALLING)) { + return; + } + + if (newSessions.size() > 0 && !hasMCU) { + getPeersForCall(); + } + + hasMCU = hasExternalSignalingServer && webSocketClient != null && webSocketClient.hasMCU(); + + for (String sessionId : newSessions) { + getPeerConnectionWrapperForSessionIdAndType(sessionId, "video", + hasMCU && sessionId.equals(webSocketClient.getSessionId())); + } + + if (newSessions.size() > 0 && !currentCallStatus.equals(CallStatus.IN_CONVERSATION)) { + setCallState(CallStatus.IN_CONVERSATION); + } + + for (String sessionId : oldSesssions) { + endPeerConnection(sessionId, false); + } + } + + private void getPeersForCall() { + ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) + .subscribeOn(Schedulers.io()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(ParticipantsOverall participantsOverall) { + participantMap = new HashMap<>(); + for (Participant participant : participantsOverall.getOcs().getData()) { + participantMap.put(participant.getSessionId(), participant); + if (getActivity() != null) { + getActivity().runOnUiThread( + () -> setupAvatarForSession(participant.getSessionId())); + } + } + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + private void deleteMagicPeerConnection(MagicPeerConnectionWrapper magicPeerConnectionWrapper) { + magicPeerConnectionWrapper.removePeerConnection(); + magicPeerConnectionWrapperList.remove(magicPeerConnectionWrapper); + } + + private MagicPeerConnectionWrapper getPeerConnectionWrapperForSessionId(String sessionId, + String type) { + for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { + if (magicPeerConnectionWrapperList.get(i).getSessionId().equals(sessionId) + && magicPeerConnectionWrapperList.get(i).getVideoStreamType().equals(type)) { + return magicPeerConnectionWrapperList.get(i); + } + } + + return null; + } + + private MagicPeerConnectionWrapper getPeerConnectionWrapperForSessionIdAndType(String sessionId, + String type, boolean publisher) { + MagicPeerConnectionWrapper magicPeerConnectionWrapper; + if ((magicPeerConnectionWrapper = getPeerConnectionWrapperForSessionId(sessionId, type)) + != null) { + return magicPeerConnectionWrapper; + } else { + if (hasMCU && publisher) { + magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, + iceServers, sdpConstraintsForMCU, sessionId, callSession, localMediaStream, true, true, + type); + } else if (hasMCU) { + magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, + iceServers, sdpConstraints, sessionId, callSession, null, false, true, type); + } else { + if (!"screen".equals(type)) { + magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, + iceServers, sdpConstraints, sessionId, callSession, localMediaStream, false, false, + type); + } else { + magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, + iceServers, sdpConstraints, sessionId, callSession, null, false, false, type); + } + } + + magicPeerConnectionWrapperList.add(magicPeerConnectionWrapper); + + if (publisher) { + startSendingNick(); + } + + return magicPeerConnectionWrapper; + } + } + + private List getPeerConnectionWrapperListForSessionId( + String sessionId) { + List internalList = new ArrayList<>(); + for (MagicPeerConnectionWrapper magicPeerConnectionWrapper : magicPeerConnectionWrapperList) { + if (magicPeerConnectionWrapper.getSessionId().equals(sessionId)) { + internalList.add(magicPeerConnectionWrapper); + } + } + + return internalList; + } + + private void endPeerConnection(String sessionId, boolean justScreen) { + List magicPeerConnectionWrappers; + MagicPeerConnectionWrapper magicPeerConnectionWrapper; + if (!(magicPeerConnectionWrappers = + getPeerConnectionWrapperListForSessionId(sessionId)).isEmpty() + && getActivity() != null) { + for (int i = 0; i < magicPeerConnectionWrappers.size(); i++) { + magicPeerConnectionWrapper = magicPeerConnectionWrappers.get(i); + if (magicPeerConnectionWrapper.getSessionId().equals(sessionId)) { + if (magicPeerConnectionWrapper.getVideoStreamType().equals("screen") || !justScreen) { + MagicPeerConnectionWrapper finalMagicPeerConnectionWrapper = magicPeerConnectionWrapper; + getActivity().runOnUiThread(() -> removeMediaStream(sessionId + "+" + + finalMagicPeerConnectionWrapper.getVideoStreamType())); + deleteMagicPeerConnection(magicPeerConnectionWrapper); + } + } + } + } + } + + private void removeMediaStream(String sessionId) { + if (remoteRenderersLayout != null && remoteRenderersLayout.getChildCount() > 0) { + RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); + if (relativeLayout != null) { SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); - SimpleDraweeView imageView = relativeLayout.findViewById(R.id.avatarImageView); + surfaceViewRenderer.release(); + remoteRenderersLayout.removeView(relativeLayout); + remoteRenderersLayout.invalidate(); + } + } - if (mediaStream != null && mediaStream.videoTracks != null && mediaStream.videoTracks.size() > 0 && enable) { - VideoTrack videoTrack = mediaStream.videoTracks.get(0); + if (callControls != null) { + callControls.setZ(100.0f); + } + } - videoTrack.addSink(surfaceViewRenderer); + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) { + powerManagerUtils.setOrientation( + Objects.requireNonNull(getResources()).getConfiguration().orientation); - imageView.setVisibility(View.INVISIBLE); - surfaceViewRenderer.setVisibility(View.VISIBLE); - } else { - imageView.setVisibility(View.VISIBLE); - surfaceViewRenderer.setVisibility(View.INVISIBLE); + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL); + } else if (getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT) { + remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL); + } + + setPipVideoViewDimensions(); + } + + private void setPipVideoViewDimensions() { + FrameLayout.LayoutParams layoutParams = + (FrameLayout.LayoutParams) pipVideoView.getLayoutParams(); + + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL); + layoutParams.height = (int) getResources().getDimension(R.dimen.large_preview_dimension); + layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; + pipVideoView.setLayoutParams(layoutParams); + } else if (getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT) { + remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL); + layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = (int) getResources().getDimension(R.dimen.large_preview_dimension); + pipVideoView.setLayoutParams(layoutParams); + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) { + if (peerConnectionEvent.getPeerConnectionEventType() + .equals(PeerConnectionEvent.PeerConnectionEventType + .PEER_CLOSED)) { + endPeerConnection(peerConnectionEvent.getSessionId(), + peerConnectionEvent.getVideoStreamType().equals("screen")); + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent + .PeerConnectionEventType.SENSOR_FAR) || + peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent + .PeerConnectionEventType.SENSOR_NEAR)) { + + if (!isVoiceOnlyCall) { + boolean enableVideo = + peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent + .PeerConnectionEventType.SENSOR_FAR) && videoOn; + if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), + PERMISSIONS_CAMERA) && + (currentCallStatus.equals(CallStatus.CALLING) || isConnectionEstablished()) && videoOn + && enableVideo != localVideoTrack.enabled()) { + toggleMedia(enableVideo, true); + } + } + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent + .PeerConnectionEventType.NICK_CHANGE)) { + gotNick(peerConnectionEvent.getSessionId(), peerConnectionEvent.getNick(), true, + peerConnectionEvent.getVideoStreamType()); + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent + .PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) { + gotAudioOrVideoChange(true, + peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), + peerConnectionEvent.getChangeValue()); + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent + .PeerConnectionEventType.AUDIO_CHANGE)) { + gotAudioOrVideoChange(false, + peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), + peerConnectionEvent.getChangeValue()); + } else if (peerConnectionEvent.getPeerConnectionEventType() + .equals(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED)) { + currentCallStatus = CallStatus.PUBLISHER_FAILED; + webSocketClient.clearResumeId(); + hangup(false); + } + } + + private void startSendingNick() { + DataChannelMessageNick dataChannelMessage = new DataChannelMessageNick(); + dataChannelMessage.setType("nickChanged"); + HashMap nickChangedPayload = new HashMap<>(); + nickChangedPayload.put("userid", conversationUser.getUserId()); + nickChangedPayload.put("name", conversationUser.getDisplayName()); + dataChannelMessage.setPayload(nickChangedPayload); + final MagicPeerConnectionWrapper magicPeerConnectionWrapper; + for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) { + if (magicPeerConnectionWrapperList.get(i).isMCUPublisher()) { + magicPeerConnectionWrapper = magicPeerConnectionWrapperList.get(i); + Observable + .interval(1, TimeUnit.SECONDS) + .repeatUntil(() -> (!isConnectionEstablished() || isBeingDestroyed() || isDestroyed())) + .observeOn(Schedulers.io()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(Long aLong) { + magicPeerConnectionWrapper.sendNickChannelData(dataChannelMessage); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + break; + } + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(MediaStreamEvent mediaStreamEvent) { + if (mediaStreamEvent.getMediaStream() != null) { + setupVideoStreamForLayout(mediaStreamEvent.getMediaStream(), mediaStreamEvent.getSession(), + mediaStreamEvent.getMediaStream().videoTracks != null + && mediaStreamEvent.getMediaStream().videoTracks.size() > 0, + mediaStreamEvent.getVideoStreamType()); + } else { + setupVideoStreamForLayout(null, mediaStreamEvent.getSession(), false, + mediaStreamEvent.getVideoStreamType()); + } + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(SessionDescriptionSendEvent sessionDescriptionSend) + throws IOException { + NCMessageWrapper ncMessageWrapper = new NCMessageWrapper(); + ncMessageWrapper.setEv("message"); + ncMessageWrapper.setSessionId(callSession); + NCSignalingMessage ncSignalingMessage = new NCSignalingMessage(); + ncSignalingMessage.setTo(sessionDescriptionSend.getPeerId()); + ncSignalingMessage.setRoomType(sessionDescriptionSend.getVideoStreamType()); + ncSignalingMessage.setType(sessionDescriptionSend.getType()); + NCMessagePayload ncMessagePayload = new NCMessagePayload(); + ncMessagePayload.setType(sessionDescriptionSend.getType()); + + if (!"candidate".equals(sessionDescriptionSend.getType())) { + ncMessagePayload.setSdp(sessionDescriptionSend.getSessionDescription().description); + ncMessagePayload.setNick(conversationUser.getDisplayName()); + } else { + ncMessagePayload.setIceCandidate(sessionDescriptionSend.getNcIceCandidate()); + } + + // Set all we need + ncSignalingMessage.setPayload(ncMessagePayload); + ncMessageWrapper.setSignalingMessage(ncSignalingMessage); + + if (!hasExternalSignalingServer) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("{") + .append("\"fn\":\"") + .append(StringEscapeUtils.escapeJson( + LoganSquare.serialize(ncMessageWrapper.getSignalingMessage()))).append("\"") + .append(",") + .append("\"sessionId\":") + .append("\"").append(StringEscapeUtils.escapeJson(callSession)).append("\"") + .append(",") + .append("\"ev\":\"message\"") + .append("}"); + + List strings = new ArrayList<>(); + String stringToSend = stringBuilder.toString(); + strings.add(stringToSend); + + String urlToken = null; + if (isMultiSession) { + urlToken = roomToken; + } + + ncApi.sendSignalingMessages(credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken), + strings.toString()) + .retry(3) + .subscribeOn(Schedulers.io()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - if (isInitialLayoutSetupForPeer && isVoiceOnlyCall) { - gotAudioOrVideoChange(true, session, false); } + + @Override + public void onNext(SignalingOverall signalingOverall) { + if (signalingOverall.getOcs().getSignalings() != null) { + for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); i++) { + try { + receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i)); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onComplete() { + + } + }); + } else { + webSocketClient.sendCallMessage(ncMessageWrapper); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + EffortlessPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, + this); + } + + private void setupAvatarForSession(String session) { + if (remoteRenderersLayout != null) { + RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+video"); + if (relativeLayout != null) { + SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); + + String userId; + + if (hasMCU) { + userId = webSocketClient.getUserIdForSession(session); + } else { + userId = participantMap.get(session).getUserId(); + } + + if (!TextUtils.isEmpty(userId)) { + + if (getActivity() != null) { + avatarImageView.setController(null); + + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(avatarImageView.getController()) + .setImageRequest( + DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(baseUrl, + userId, + R.dimen.avatar_size_big), null)) + .build(); + avatarImageView.setController(draweeController); + } + } + } + } + } + + private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, String session, + boolean enable, String videoStreamType) { + boolean isInitialLayoutSetupForPeer = false; + if (remoteRenderersLayout.findViewWithTag(session) == null) { + setupNewPeerLayout(session, videoStreamType); + isInitialLayoutSetupForPeer = true; + } + + RelativeLayout relativeLayout = + remoteRenderersLayout.findViewWithTag(session + "+" + videoStreamType); + SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); + SimpleDraweeView imageView = relativeLayout.findViewById(R.id.avatarImageView); + + if (mediaStream != null + && mediaStream.videoTracks != null + && mediaStream.videoTracks.size() > 0 + && enable) { + VideoTrack videoTrack = mediaStream.videoTracks.get(0); + + videoTrack.addSink(surfaceViewRenderer); + + imageView.setVisibility(View.INVISIBLE); + surfaceViewRenderer.setVisibility(View.VISIBLE); + } else { + imageView.setVisibility(View.VISIBLE); + surfaceViewRenderer.setVisibility(View.INVISIBLE); + + if (isInitialLayoutSetupForPeer && isVoiceOnlyCall) { + gotAudioOrVideoChange(true, session, false); + } + } + + callControls.setZ(100.0f); + } + + private void gotAudioOrVideoChange(boolean video, String sessionId, boolean change) { + RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); + if (relativeLayout != null) { + ImageView imageView; + SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); + SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); + + if (video) { + imageView = relativeLayout.findViewById(R.id.remote_video_off); + + if (change) { + avatarImageView.setVisibility(View.INVISIBLE); + surfaceViewRenderer.setVisibility(View.VISIBLE); + } else { + avatarImageView.setVisibility(View.VISIBLE); + surfaceViewRenderer.setVisibility(View.INVISIBLE); + } + } else { + imageView = relativeLayout.findViewById(R.id.remote_audio_off); + } + + if (change && imageView.getVisibility() != View.INVISIBLE) { + imageView.setVisibility(View.INVISIBLE); + } else if (!change && imageView.getVisibility() != View.VISIBLE) { + imageView.setVisibility(View.VISIBLE); + } + } + } + + private void setupNewPeerLayout(String session, String type) { + if (remoteRenderersLayout.findViewWithTag(session + "+" + type) == null + && getActivity() != null) { + getActivity().runOnUiThread(() -> { + RelativeLayout relativeLayout = (RelativeLayout) + getActivity().getLayoutInflater().inflate(R.layout.call_item, remoteRenderersLayout, + false); + relativeLayout.setTag(session + "+" + type); + SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id + .surface_view); + + surfaceViewRenderer.setMirror(false); + surfaceViewRenderer.init(rootEglBase.getEglBaseContext(), null); + surfaceViewRenderer.setZOrderMediaOverlay(false); + // disabled because it causes some devices to crash + surfaceViewRenderer.setEnableHardwareScaler(false); + surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); + surfaceViewRenderer.setOnClickListener(videoOnClickListener); + remoteRenderersLayout.addView(relativeLayout); + if (hasExternalSignalingServer) { + gotNick(session, webSocketClient.getDisplayNameForSession(session), false, type); + } else { + gotNick(session, + getPeerConnectionWrapperForSessionIdAndType(session, type, false).getNick(), false, + type); + } + + if ("video".equals(type)) { + setupAvatarForSession(session); } callControls.setZ(100.0f); + }); + } + } + + private void gotNick(String sessionOrUserId, String nick, boolean isFromAnEvent, String type) { + if (isFromAnEvent && hasExternalSignalingServer) { + // get session based on userId + sessionOrUserId = webSocketClient.getSessionForUserId(sessionOrUserId); } - private void gotAudioOrVideoChange(boolean video, String sessionId, boolean change) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); - if (relativeLayout != null) { - ImageView imageView; - SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); - SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); + sessionOrUserId += "+" + type; - if (video) { - imageView = relativeLayout.findViewById(R.id.remote_video_off); + if (relativeLayout != null) { + RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionOrUserId); + TextView textView = relativeLayout.findViewById(R.id.peer_nick_text_view); + if (!textView.getText().equals(nick)) { + textView.setText(nick); + } + } + } - if (change) { - avatarImageView.setVisibility(View.INVISIBLE); - surfaceViewRenderer.setVisibility(View.VISIBLE); - } else { - avatarImageView.setVisibility(View.VISIBLE); - surfaceViewRenderer.setVisibility(View.INVISIBLE); - } - } else { - imageView = relativeLayout.findViewById(R.id.remote_audio_off); + @OnClick(R.id.connectingRelativeLayoutView) + public void onConnectingViewClick() { + if (currentCallStatus.equals(CallStatus.CALLING_TIMEOUT)) { + setCallState(CallStatus.RECONNECTING); + hangupNetworkCalls(false); + } + } + + private void setCallState(CallStatus callState) { + if (currentCallStatus == null || !currentCallStatus.equals(callState)) { + currentCallStatus = callState; + if (handler == null) { + handler = new Handler(Looper.getMainLooper()); + } else { + handler.removeCallbacksAndMessages(null); + } + + switch (callState) { + case CALLING: + handler.post(() -> { + playCallingSound(); + connectingTextView.setText(R.string.nc_connecting_call); + if (connectingView.getVisibility() != View.VISIBLE) { + connectingView.setVisibility(View.VISIBLE); } - if (change && imageView.getVisibility() != View.INVISIBLE) { - imageView.setVisibility(View.INVISIBLE); - } else if (!change && imageView.getVisibility() != View.VISIBLE) { - imageView.setVisibility(View.VISIBLE); - } - } - } - - private void setupNewPeerLayout(String session, String type) { - if (remoteRenderersLayout.findViewWithTag(session + "+" + type) == null && getActivity() != null) { - getActivity().runOnUiThread(() -> { - RelativeLayout relativeLayout = (RelativeLayout) - getActivity().getLayoutInflater().inflate(R.layout.call_item, remoteRenderersLayout, - false); - relativeLayout.setTag(session + "+" + type); - SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id - .surface_view); - - surfaceViewRenderer.setMirror(false); - surfaceViewRenderer.init(rootEglBase.getEglBaseContext(), null); - surfaceViewRenderer.setZOrderMediaOverlay(false); - // disabled because it causes some devices to crash - surfaceViewRenderer.setEnableHardwareScaler(false); - surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); - surfaceViewRenderer.setOnClickListener(videoOnClickListener); - remoteRenderersLayout.addView(relativeLayout); - if (hasExternalSignalingServer) { - gotNick(session, webSocketClient.getDisplayNameForSession(session), false, type); - } else { - gotNick(session, getPeerConnectionWrapperForSessionIdAndType(session, type, false).getNick(), false, type); - } - - if ("video".equals(type)) { - setupAvatarForSession(session); - } - - callControls.setZ(100.0f); - }); - } - } - - private void gotNick(String sessionOrUserId, String nick, boolean isFromAnEvent, String type) { - if (isFromAnEvent && hasExternalSignalingServer) { - // get session based on userId - sessionOrUserId = webSocketClient.getSessionForUserId(sessionOrUserId); - } - - sessionOrUserId += "+" + type; - - if (relativeLayout != null) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionOrUserId); - TextView textView = relativeLayout.findViewById(R.id.peer_nick_text_view); - if (!textView.getText().equals(nick)) { - textView.setText(nick); - } - } - } - - @OnClick(R.id.connectingRelativeLayoutView) - public void onConnectingViewClick() { - if (currentCallStatus.equals(CallStatus.CALLING_TIMEOUT)) { - setCallState(CallStatus.RECONNECTING); - hangupNetworkCalls(false); - } - } - - private void setCallState(CallStatus callState) { - if (currentCallStatus == null || !currentCallStatus.equals(callState)) { - currentCallStatus = callState; - if (handler == null) { - handler = new Handler(Looper.getMainLooper()); - } else { - handler.removeCallbacksAndMessages(null); + if (conversationView.getVisibility() != View.INVISIBLE) { + conversationView.setVisibility(View.INVISIBLE); } - switch (callState) { - case CALLING: - handler.post(() -> { - playCallingSound(); - connectingTextView.setText(R.string.nc_connecting_call); - if (connectingView.getVisibility() != View.VISIBLE) { - connectingView.setVisibility(View.VISIBLE); - } - - if (conversationView.getVisibility() != View.INVISIBLE) { - conversationView.setVisibility(View.INVISIBLE); - } - - if (progressBar.getVisibility() != View.VISIBLE) { - progressBar.setVisibility(View.VISIBLE); - } - - if (errorImageView.getVisibility() != View.GONE) { - errorImageView.setVisibility(View.GONE); - } - }); - break; - case CALLING_TIMEOUT: - handler.post(() -> { - hangup(false); - connectingTextView.setText(R.string.nc_call_timeout); - if (connectingView.getVisibility() != View.VISIBLE) { - connectingView.setVisibility(View.VISIBLE); - } - - if (progressBar.getVisibility() != View.GONE) { - progressBar.setVisibility(View.GONE); - } - - if (conversationView.getVisibility() != View.INVISIBLE) { - conversationView.setVisibility(View.INVISIBLE); - } - - errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp); - - if (errorImageView.getVisibility() != View.VISIBLE) { - errorImageView.setVisibility(View.VISIBLE); - } - }); - break; - case RECONNECTING: - handler.post(() -> { - playCallingSound(); - connectingTextView.setText(R.string.nc_call_reconnecting); - if (connectingView.getVisibility() != View.VISIBLE) { - connectingView.setVisibility(View.VISIBLE); - } - if (conversationView.getVisibility() != View.INVISIBLE) { - conversationView.setVisibility(View.INVISIBLE); - } - if (progressBar.getVisibility() != View.VISIBLE) { - progressBar.setVisibility(View.VISIBLE); - } - - if (errorImageView.getVisibility() != View.GONE) { - errorImageView.setVisibility(View.GONE); - } - }); - break; - case ESTABLISHED: - handler.postDelayed(() -> setCallState(CallStatus.CALLING_TIMEOUT), 45000); - handler.post(() -> { - if (connectingView != null) { - connectingTextView.setText(R.string.nc_calling); - if (connectingTextView.getVisibility() != View.VISIBLE) { - connectingView.setVisibility(View.VISIBLE); - } - } - - if (progressBar != null) { - if (progressBar.getVisibility() != View.VISIBLE) { - progressBar.setVisibility(View.VISIBLE); - } - } - - if (conversationView != null) { - if (conversationView.getVisibility() != View.INVISIBLE) { - conversationView.setVisibility(View.INVISIBLE); - } - } - - if (errorImageView != null) { - if (errorImageView.getVisibility() != View.GONE) { - errorImageView.setVisibility(View.GONE); - } - } - }); - break; - case IN_CONVERSATION: - handler.post(() -> { - stopCallingSound(); - - if (!isPTTActive) { - animateCallControls(false, 5000); - } - - if (connectingView != null) { - if (connectingView.getVisibility() != View.INVISIBLE) { - connectingView.setVisibility(View.INVISIBLE); - } - } - - if (progressBar != null) { - if (progressBar.getVisibility() != View.GONE) { - progressBar.setVisibility(View.GONE); - } - } - - if (conversationView != null) { - if (conversationView.getVisibility() != View.VISIBLE) { - conversationView.setVisibility(View.VISIBLE); - } - } - - if (errorImageView != null) { - if (errorImageView.getVisibility() != View.GONE) { - errorImageView.setVisibility(View.GONE); - } - } - }); - break; - case OFFLINE: - handler.post(() -> { - stopCallingSound(); - - if (connectingTextView != null) { - connectingTextView.setText(R.string.nc_offline); - - if (connectingView.getVisibility() != View.VISIBLE) { - connectingView.setVisibility(View.VISIBLE); - } - } - - - if (conversationView != null) { - if (conversationView.getVisibility() != View.INVISIBLE) { - conversationView.setVisibility(View.INVISIBLE); - } - } - - if (progressBar != null) { - if (progressBar.getVisibility() != View.GONE) { - progressBar.setVisibility(View.GONE); - } - } - - if (errorImageView != null) { - errorImageView.setImageResource(R.drawable.ic_signal_wifi_off_white_24dp); - if (errorImageView.getVisibility() != View.VISIBLE) { - errorImageView.setVisibility(View.VISIBLE); - } - } - }); - break; - case LEAVING: - handler.post(() -> { - if (!isDestroyed() && !isBeingDestroyed()) { - stopCallingSound(); - connectingTextView.setText(R.string.nc_leaving_call); - connectingView.setVisibility(View.VISIBLE); - conversationView.setVisibility(View.INVISIBLE); - progressBar.setVisibility(View.VISIBLE); - errorImageView.setVisibility(View.GONE); - } - }); - break; - default: - } - } - } - - private void playCallingSound() { - stopCallingSound(); - Uri ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/raw/librem_by_feandesign_call"); - if (getActivity() != null) { - mediaPlayer = new MediaPlayer(); - try { - mediaPlayer.setDataSource(Objects.requireNonNull(getActivity()), ringtoneUri); - mediaPlayer.setLooping(true); - AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes - .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build(); - mediaPlayer.setAudioAttributes(audioAttributes); - - mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); - - mediaPlayer.prepareAsync(); - - } catch (IOException e) { - Log.e(TAG, "Failed to play sound"); - } - } - } - - private void stopCallingSound() { - if (mediaPlayer != null) { - if (mediaPlayer.isPlaying()) { - mediaPlayer.stop(); + if (progressBar.getVisibility() != View.VISIBLE) { + progressBar.setVisibility(View.VISIBLE); } - mediaPlayer.release(); - mediaPlayer = null; - } - } - - private class MicrophoneButtonTouchListener implements View.OnTouchListener { - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View v, MotionEvent event) { - v.onTouchEvent(event); - if (event.getAction() == MotionEvent.ACTION_UP && isPTTActive) { - isPTTActive = false; - microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px); - pulseAnimation.stop(); - toggleMedia(false, false); - animateCallControls(false, 5000); + if (errorImageView.getVisibility() != View.GONE) { + errorImageView.setVisibility(View.GONE); } - return true; - } - } - - private class VideoClickListener implements View.OnClickListener { - - @Override - public void onClick(View v) { - showCallControls(); - } - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(NetworkEvent networkEvent) { - if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) { - if (handler != null) { - handler.removeCallbacksAndMessages(null); + }); + break; + case CALLING_TIMEOUT: + handler.post(() -> { + hangup(false); + connectingTextView.setText(R.string.nc_call_timeout); + if (connectingView.getVisibility() != View.VISIBLE) { + connectingView.setVisibility(View.VISIBLE); } + if (progressBar.getVisibility() != View.GONE) { + progressBar.setVisibility(View.GONE); + } + + if (conversationView.getVisibility() != View.INVISIBLE) { + conversationView.setVisibility(View.INVISIBLE); + } + + errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp); + + if (errorImageView.getVisibility() != View.VISIBLE) { + errorImageView.setVisibility(View.VISIBLE); + } + }); + break; + case RECONNECTING: + handler.post(() -> { + playCallingSound(); + connectingTextView.setText(R.string.nc_call_reconnecting); + if (connectingView.getVisibility() != View.VISIBLE) { + connectingView.setVisibility(View.VISIBLE); + } + if (conversationView.getVisibility() != View.INVISIBLE) { + conversationView.setVisibility(View.INVISIBLE); + } + if (progressBar.getVisibility() != View.VISIBLE) { + progressBar.setVisibility(View.VISIBLE); + } + + if (errorImageView.getVisibility() != View.GONE) { + errorImageView.setVisibility(View.GONE); + } + }); + break; + case ESTABLISHED: + handler.postDelayed(() -> setCallState(CallStatus.CALLING_TIMEOUT), 45000); + handler.post(() -> { + if (connectingView != null) { + connectingTextView.setText(R.string.nc_calling); + if (connectingTextView.getVisibility() != View.VISIBLE) { + connectingView.setVisibility(View.VISIBLE); + } + } + + if (progressBar != null) { + if (progressBar.getVisibility() != View.VISIBLE) { + progressBar.setVisibility(View.VISIBLE); + } + } + + if (conversationView != null) { + if (conversationView.getVisibility() != View.INVISIBLE) { + conversationView.setVisibility(View.INVISIBLE); + } + } + + if (errorImageView != null) { + if (errorImageView.getVisibility() != View.GONE) { + errorImageView.setVisibility(View.GONE); + } + } + }); + break; + case IN_CONVERSATION: + handler.post(() -> { + stopCallingSound(); + + if (!isPTTActive) { + animateCallControls(false, 5000); + } + + if (connectingView != null) { + if (connectingView.getVisibility() != View.INVISIBLE) { + connectingView.setVisibility(View.INVISIBLE); + } + } + + if (progressBar != null) { + if (progressBar.getVisibility() != View.GONE) { + progressBar.setVisibility(View.GONE); + } + } + + if (conversationView != null) { + if (conversationView.getVisibility() != View.VISIBLE) { + conversationView.setVisibility(View.VISIBLE); + } + } + + if (errorImageView != null) { + if (errorImageView.getVisibility() != View.GONE) { + errorImageView.setVisibility(View.GONE); + } + } + }); + break; + case OFFLINE: + handler.post(() -> { + stopCallingSound(); + + if (connectingTextView != null) { + connectingTextView.setText(R.string.nc_offline); + + if (connectingView.getVisibility() != View.VISIBLE) { + connectingView.setVisibility(View.VISIBLE); + } + } + + if (conversationView != null) { + if (conversationView.getVisibility() != View.INVISIBLE) { + conversationView.setVisibility(View.INVISIBLE); + } + } + + if (progressBar != null) { + if (progressBar.getVisibility() != View.GONE) { + progressBar.setVisibility(View.GONE); + } + } + + if (errorImageView != null) { + errorImageView.setImageResource(R.drawable.ic_signal_wifi_off_white_24dp); + if (errorImageView.getVisibility() != View.VISIBLE) { + errorImageView.setVisibility(View.VISIBLE); + } + } + }); + break; + case LEAVING: + handler.post(() -> { + if (!isDestroyed() && !isBeingDestroyed()) { + stopCallingSound(); + connectingTextView.setText(R.string.nc_leaving_call); + connectingView.setVisibility(View.VISIBLE); + conversationView.setVisibility(View.INVISIBLE); + progressBar.setVisibility(View.VISIBLE); + errorImageView.setVisibility(View.GONE); + } + }); + break; + default: + } + } + } + + private void playCallingSound() { + stopCallingSound(); + Uri ringtoneUri = Uri.parse("android.resource://" + + getApplicationContext().getPackageName() + + "/raw/librem_by_feandesign_call"); + if (getActivity() != null) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(Objects.requireNonNull(getActivity()), ringtoneUri); + mediaPlayer.setLooping(true); + AudioAttributes audioAttributes = + new AudioAttributes.Builder().setContentType(AudioAttributes + .CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .build(); + mediaPlayer.setAudioAttributes(audioAttributes); + + mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); + + mediaPlayer.prepareAsync(); + } catch (IOException e) { + Log.e(TAG, "Failed to play sound"); + } + } + } + + private void stopCallingSound() { + if (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) { + mediaPlayer.stop(); + } + + mediaPlayer.release(); + mediaPlayer = null; + } + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(NetworkEvent networkEvent) { + if (networkEvent.getNetworkConnectionEvent() + .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) { + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + /*if (!hasMCU) { setCallState(CallStatus.RECONNECTING); hangupNetworkCalls(false); }*/ - } else if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) { - if (handler != null) { - handler.removeCallbacksAndMessages(null); - } + } else if (networkEvent.getNetworkConnectionEvent() + .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) { + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } /* if (!hasMCU) { setCallState(CallStatus.OFFLINE); hangup(false); }*/ - } } + } + + @Parcel + public enum CallStatus { + CALLING, CALLING_TIMEOUT, ESTABLISHED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED + } + + private class MicrophoneButtonTouchListener implements View.OnTouchListener { + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + v.onTouchEvent(event); + if (event.getAction() == MotionEvent.ACTION_UP && isPTTActive) { + isPTTActive = false; + microphoneControlButton.getHierarchy() + .setPlaceholderImage(R.drawable.ic_mic_off_white_24px); + pulseAnimation.stop(); + toggleMedia(false, false); + animateCallControls(false, 5000); + } + return true; + } + } + + private class VideoClickListener implements View.OnClickListener { + + @Override + public void onClick(View v) { + showCallControls(); + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java index 8dfa6254b..c37b342c4 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java @@ -61,17 +61,17 @@ import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.postprocessors.BlurPostProcessor; import com.facebook.imagepipeline.request.ImageRequest; -import com.nextcloud.talk.models.RingtoneSettings; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.conversations.RoomsOverall; -import com.nextcloud.talk.models.json.participants.Participant; -import com.nextcloud.talk.models.json.participants.ParticipantsOverall; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.ConfigurationChangeEvent; +import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.conversations.Conversation; +import com.nextcloud.talk.models.json.conversations.RoomsOverall; +import com.nextcloud.talk.models.json.participants.Participant; +import com.nextcloud.talk.models.json.participants.ParticipantsOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DoNotDisturbUtils; @@ -97,397 +97,411 @@ import org.parceler.Parcels; @AutoInjector(NextcloudTalkApplication.class) public class CallNotificationController extends BaseController { - private static final String TAG = "CallNotificationController"; + private static final String TAG = "CallNotificationController"; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @Inject - Cache cache; + @Inject + Cache cache; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - @Inject - Context context; + @Inject + Context context; - @BindView(R.id.conversationNameTextView) - TextView conversationNameTextView; + @BindView(R.id.conversationNameTextView) + TextView conversationNameTextView; - @BindView(R.id.avatarImageView) - SimpleDraweeView avatarImageView; + @BindView(R.id.avatarImageView) + SimpleDraweeView avatarImageView; - @BindView(R.id.callAnswerVoiceOnlyView) - SimpleDraweeView callAnswerVoiceOnlyView; + @BindView(R.id.callAnswerVoiceOnlyView) + SimpleDraweeView callAnswerVoiceOnlyView; - @BindView(R.id.callAnswerCameraView) - SimpleDraweeView callAnswerCameraView; + @BindView(R.id.callAnswerCameraView) + SimpleDraweeView callAnswerCameraView; - @BindView(R.id.backgroundImageView) - ImageView backgroundImageView; + @BindView(R.id.backgroundImageView) + ImageView backgroundImageView; - @BindView(R.id.incomingTextRelativeLayout) - RelativeLayout incomingTextRelativeLayout; + @BindView(R.id.incomingTextRelativeLayout) + RelativeLayout incomingTextRelativeLayout; - private Bundle originalBundle; - private String roomId; - private UserEntity userBeingCalled; - private String credentials; - private Conversation currentConversation; - private MediaPlayer mediaPlayer; - private boolean leavingScreen = false; - private RenderScript renderScript; - private Vibrator vibrator; - private Handler handler; + private Bundle originalBundle; + private String roomId; + private UserEntity userBeingCalled; + private String credentials; + private Conversation currentConversation; + private MediaPlayer mediaPlayer; + private boolean leavingScreen = false; + private RenderScript renderScript; + private Vibrator vibrator; + private Handler handler; - public CallNotificationController(Bundle args) { - super(); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public CallNotificationController(Bundle args) { + super(); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - this.roomId = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), ""); - this.currentConversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - this.userBeingCalled = args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY()); + this.roomId = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), ""); + this.currentConversation = + Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); + this.userBeingCalled = args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY()); - this.originalBundle = args; - credentials = ApiUtils.getCredentials(userBeingCalled.getUsername(), userBeingCalled.getToken()); + this.originalBundle = args; + credentials = + ApiUtils.getCredentials(userBeingCalled.getUsername(), userBeingCalled.getToken()); + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_call_notification, container, false); + } + + @Override + protected void onDetach(@NonNull View view) { + eventBus.unregister(this); + super.onDetach(view); + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + eventBus.register(this); + } + + private void showAnswerControls() { + callAnswerCameraView.setVisibility(View.VISIBLE); + callAnswerVoiceOnlyView.setVisibility(View.VISIBLE); + } + + @OnClick(R.id.callControlHangupView) + void hangup() { + leavingScreen = true; + + if (getActivity() != null) { + getActivity().finish(); } + } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_call_notification, container, false); - } + @OnClick(R.id.callAnswerCameraView) + void answerWithCamera() { + originalBundle.putBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), false); + proceedToCall(); + } - @Override - protected void onDetach(@NonNull View view) { - eventBus.unregister(this); - super.onDetach(view); - } + @OnClick(R.id.callAnswerVoiceOnlyView) + void answerVoiceOnly() { + originalBundle.putBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), true); + proceedToCall(); + } - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - eventBus.register(this); - } + private void proceedToCall() { + originalBundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), + currentConversation.getToken()); - private void showAnswerControls() { - callAnswerCameraView.setVisibility(View.VISIBLE); - callAnswerVoiceOnlyView.setVisibility(View.VISIBLE); - } + getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle)) + .popChangeHandler(new HorizontalChangeHandler()) + .pushChangeHandler(new HorizontalChangeHandler())); + } - @OnClick(R.id.callControlHangupView) - void hangup() { - leavingScreen = true; + private void checkIfAnyParticipantsRemainInRoom() { + ncApi.getPeersForCall(credentials, ApiUtils.getUrlForParticipants(userBeingCalled.getBaseUrl(), + currentConversation.getToken())) + .subscribeOn(Schedulers.io()) + .takeWhile(observable -> !leavingScreen) + .retry(3) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } - if (getActivity() != null) { - getActivity().finish(); - } - } + @Override + public void onNext(ParticipantsOverall participantsOverall) { + boolean hasParticipantsInCall = false; + boolean inCallOnDifferentDevice = false; + List participantList = participantsOverall.getOcs().getData(); + for (Participant participant : participantList) { + if (participant.getParticipantFlags() != Participant.ParticipantFlags.NOT_IN_CALL) { + hasParticipantsInCall = true; - @OnClick(R.id.callAnswerCameraView) - void answerWithCamera() { - originalBundle.putBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), false); - proceedToCall(); - } - - @OnClick(R.id.callAnswerVoiceOnlyView) - void answerVoiceOnly() { - originalBundle.putBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), true); - proceedToCall(); - } - - private void proceedToCall() { - originalBundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), currentConversation.getToken()); - - getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle)) - .popChangeHandler(new HorizontalChangeHandler()) - .pushChangeHandler(new HorizontalChangeHandler())); - } - - private void checkIfAnyParticipantsRemainInRoom() { - ncApi.getPeersForCall(credentials, ApiUtils.getUrlForParticipants(userBeingCalled.getBaseUrl(), - currentConversation.getToken())) - .subscribeOn(Schedulers.io()) - .takeWhile(observable -> !leavingScreen) - .retry(3) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(ParticipantsOverall participantsOverall) { - boolean hasParticipantsInCall = false; - boolean inCallOnDifferentDevice = false; - List participantList = participantsOverall.getOcs().getData(); - for (Participant participant : participantList) { - if (participant.getParticipantFlags() != Participant.ParticipantFlags.NOT_IN_CALL) { - hasParticipantsInCall = true; - - if (participant.getUserId().equals(userBeingCalled.getUserId())) { - inCallOnDifferentDevice = true; - break; - } - } - } - - if (!hasParticipantsInCall || inCallOnDifferentDevice) { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> hangup()); - } - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - if (!leavingScreen) { - checkIfAnyParticipantsRemainInRoom(); - } - } - }); - - } - - private void handleFromNotification() { - ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(userBeingCalled.getBaseUrl())) - .subscribeOn(Schedulers.io()) - .retry(3) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(RoomsOverall roomsOverall) { - for (Conversation conversation : roomsOverall.getOcs().getData()) { - if (roomId.equals(conversation.getRoomId())) { - currentConversation = conversation; - runAllThings(); - break; - } - } - - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - private void runAllThings() { - if (conversationNameTextView != null) { - conversationNameTextView.setText(currentConversation.getDisplayName()); - } - - loadAvatar(); - checkIfAnyParticipantsRemainInRoom(); - showAnswerControls(); - } - - @SuppressLint("LongLogTag") - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - - renderScript = RenderScript.create(getActivity()); - - if (handler == null) { - handler = new Handler(); - - try { - cache.evictAll(); - } catch (IOException e) { - Log.e(TAG, "Failed to evict cache"); + if (participant.getUserId().equals(userBeingCalled.getUserId())) { + inCallOnDifferentDevice = true; + break; + } + } } - } - if (currentConversation == null) { - handleFromNotification(); + if (!hasParticipantsInCall || inCallOnDifferentDevice) { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> hangup()); + } + } + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + if (!leavingScreen) { + checkIfAnyParticipantsRemainInRoom(); + } + } + }); + } + + private void handleFromNotification() { + ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(userBeingCalled.getBaseUrl())) + .subscribeOn(Schedulers.io()) + .retry(3) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(RoomsOverall roomsOverall) { + for (Conversation conversation : roomsOverall.getOcs().getData()) { + if (roomId.equals(conversation.getRoomId())) { + currentConversation = conversation; + runAllThings(); + break; + } + } + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + private void runAllThings() { + if (conversationNameTextView != null) { + conversationNameTextView.setText(currentConversation.getDisplayName()); + } + + loadAvatar(); + checkIfAnyParticipantsRemainInRoom(); + showAnswerControls(); + } + + @SuppressLint("LongLogTag") + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + + renderScript = RenderScript.create(getActivity()); + + if (handler == null) { + handler = new Handler(); + + try { + cache.evictAll(); + } catch (IOException e) { + Log.e(TAG, "Failed to evict cache"); + } + } + + if (currentConversation == null) { + handleFromNotification(); + } else { + runAllThings(); + } + + if (DoNotDisturbUtils.INSTANCE.shouldPlaySound()) { + String callRingtonePreferenceString = appPreferences.getCallRingtoneUri(); + Uri ringtoneUri; + + if (TextUtils.isEmpty(callRingtonePreferenceString)) { + // play default sound + ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + + "/raw/librem_by_feandesign_call"); + } else { + try { + RingtoneSettings ringtoneSettings = + LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class); + ringtoneUri = ringtoneSettings.getRingtoneUri(); + } catch (IOException e) { + Log.e(TAG, "Failed to parse ringtone settings"); + ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + + "/raw/librem_by_feandesign_call"); + } + } + + if (ringtoneUri != null && getActivity() != null) { + mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(getActivity(), ringtoneUri); + + mediaPlayer.setLooping(true); + AudioAttributes audioAttributes = + new AudioAttributes.Builder().setContentType(AudioAttributes + .CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build(); + mediaPlayer.setAudioAttributes(audioAttributes); + + mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); + + mediaPlayer.prepareAsync(); + } catch (IOException e) { + Log.e(TAG, "Failed to set data source"); + } + } + } + + if (DoNotDisturbUtils.INSTANCE.shouldVibrate(appPreferences.getShouldVibrateSetting())) { + vibrator = (Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE); + + if (vibrator != null) { + long[] vibratePattern = new long[] { 0, 400, 800, 600, 800, 800, 800, 1000 }; + int[] amplitudes = new int[] { 0, 255, 0, 255, 0, 255, 0, 255 }; + + VibrationEffect vibrationEffect; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (vibrator.hasAmplitudeControl()) { + vibrationEffect = VibrationEffect.createWaveform(vibratePattern, amplitudes, -1); + vibrator.vibrate(vibrationEffect); + } else { + vibrationEffect = VibrationEffect.createWaveform(vibratePattern, -1); + vibrator.vibrate(vibrationEffect); + } } else { - runAllThings(); - } - - if (DoNotDisturbUtils.INSTANCE.shouldPlaySound()) { - String callRingtonePreferenceString = appPreferences.getCallRingtoneUri(); - Uri ringtoneUri; - - if (TextUtils.isEmpty(callRingtonePreferenceString)) { - // play default sound - ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + - "/raw/librem_by_feandesign_call"); - } else { - try { - RingtoneSettings ringtoneSettings = LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class); - ringtoneUri = ringtoneSettings.getRingtoneUri(); - } catch (IOException e) { - Log.e(TAG, "Failed to parse ringtone settings"); - ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + - "/raw/librem_by_feandesign_call"); - } - } - - if (ringtoneUri != null && getActivity() != null) { - mediaPlayer = new MediaPlayer(); - try { - mediaPlayer.setDataSource(getActivity(), ringtoneUri); - - mediaPlayer.setLooping(true); - AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes - .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build(); - mediaPlayer.setAudioAttributes(audioAttributes); - - mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); - - mediaPlayer.prepareAsync(); - } catch (IOException e) { - Log.e(TAG, "Failed to set data source"); - } - } - } - - if (DoNotDisturbUtils.INSTANCE.shouldVibrate(appPreferences.getShouldVibrateSetting())) { - vibrator = (Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE); - - if (vibrator != null) { - long[] vibratePattern = new long[]{0, 400, 800, 600, 800, 800, 800, 1000}; - int[] amplitudes = new int[]{0, 255, 0, 255, 0, 255, 0, 255}; - - VibrationEffect vibrationEffect; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (vibrator.hasAmplitudeControl()) { - vibrationEffect = VibrationEffect.createWaveform(vibratePattern, amplitudes, -1); - vibrator.vibrate(vibrationEffect); - } else { - vibrationEffect = VibrationEffect.createWaveform(vibratePattern, -1); - vibrator.vibrate(vibrationEffect); - } - } else { - vibrator.vibrate(vibratePattern, -1); - } - } - - handler.postDelayed(() -> { - if (vibrator != null) { - vibrator.cancel(); - } - }, 10000); - } - } - - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) { - ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) avatarImageView.getLayoutParams(); - int dimen = (int) getResources().getDimension(R.dimen.avatar_size_very_big); - - layoutParams.width = dimen; - layoutParams.height = dimen; - avatarImageView.setLayoutParams(layoutParams); - } - - private void loadAvatar() { - switch (currentConversation.getType()) { - case ROOM_TYPE_ONE_TO_ONE_CALL: - avatarImageView.setVisibility(View.VISIBLE); - - ImageRequest imageRequest = - DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userBeingCalled.getBaseUrl(), - currentConversation.getName(), R.dimen.avatar_size_very_big), null); - - ImagePipeline imagePipeline = Fresco.getImagePipeline(); - DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null); - - dataSource.subscribe(new BaseBitmapDataSubscriber() { - @Override - protected void onNewResultImpl(@Nullable Bitmap bitmap) { - if (avatarImageView != null) { - avatarImageView.getHierarchy().setImage(new BitmapDrawable(bitmap), 100, - true); - - if (getResources() != null) { - incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable - .incoming_gradient)); - } - - if ((AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 || AvatarStatusCodeHolder.getInstance().getStatusCode() == 0) && - userBeingCalled.hasSpreedFeatureCapability("no-ping")) { - if (getActivity() != null) { - Bitmap backgroundBitmap = bitmap.copy(bitmap.getConfig(), true); - new BlurPostProcessor(5, getActivity()).process(backgroundBitmap); - backgroundImageView.setImageDrawable(new BitmapDrawable(backgroundBitmap)); - } - } else if (AvatarStatusCodeHolder.getInstance().getStatusCode() == 201) { - ColorArt colorArt = new ColorArt(bitmap); - int color = colorArt.getBackgroundColor(); - - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[2] *= 0.75f; - color = Color.HSVToColor(hsv); - - backgroundImageView.setImageDrawable(new ColorDrawable(color)); - } - } - } - - @Override - protected void onFailureImpl(DataSource> dataSource) { - } - }, UiThreadImmediateExecutorService.getInstance()); - - break; - case ROOM_GROUP_CALL: - avatarImageView.getHierarchy().setImage(DisplayUtils.getRoundedDrawable(context.getDrawable(R.drawable.ic_people_group_white_24px)) - , 100, true); - case ROOM_PUBLIC_CALL: - avatarImageView.getHierarchy().setImage(DisplayUtils.getRoundedDrawable(context.getDrawable(R.drawable.ic_people_group_white_24px)) - , 100, true); - break; - default: - } - } - - private void endMediaAndVibratorNotifications() { - if (mediaPlayer != null) { - if (mediaPlayer.isPlaying()) { - mediaPlayer.stop(); - } - - mediaPlayer.release(); - mediaPlayer = null; + vibrator.vibrate(vibratePattern, -1); } + } + handler.postDelayed(() -> { if (vibrator != null) { - vibrator.cancel(); + vibrator.cancel(); } + }, 10000); + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) { + ConstraintLayout.LayoutParams layoutParams = + (ConstraintLayout.LayoutParams) avatarImageView.getLayoutParams(); + int dimen = (int) getResources().getDimension(R.dimen.avatar_size_very_big); + + layoutParams.width = dimen; + layoutParams.height = dimen; + avatarImageView.setLayoutParams(layoutParams); + } + + private void loadAvatar() { + switch (currentConversation.getType()) { + case ROOM_TYPE_ONE_TO_ONE_CALL: + avatarImageView.setVisibility(View.VISIBLE); + + ImageRequest imageRequest = + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName(userBeingCalled.getBaseUrl(), + currentConversation.getName(), R.dimen.avatar_size_very_big), null); + + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + DataSource> dataSource = + imagePipeline.fetchDecodedImage(imageRequest, null); + + dataSource.subscribe(new BaseBitmapDataSubscriber() { + @Override + protected void onNewResultImpl(@Nullable Bitmap bitmap) { + if (avatarImageView != null) { + avatarImageView.getHierarchy().setImage(new BitmapDrawable(bitmap), 100, + true); + + if (getResources() != null) { + incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable + .incoming_gradient)); + } + + if ((AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 + || AvatarStatusCodeHolder.getInstance().getStatusCode() == 0) && + userBeingCalled.hasSpreedFeatureCapability("no-ping")) { + if (getActivity() != null) { + Bitmap backgroundBitmap = bitmap.copy(bitmap.getConfig(), true); + new BlurPostProcessor(5, getActivity()).process(backgroundBitmap); + backgroundImageView.setImageDrawable(new BitmapDrawable(backgroundBitmap)); + } + } else if (AvatarStatusCodeHolder.getInstance().getStatusCode() == 201) { + ColorArt colorArt = new ColorArt(bitmap); + int color = colorArt.getBackgroundColor(); + + float[] hsv = new float[3]; + Color.colorToHSV(color, hsv); + hsv[2] *= 0.75f; + color = Color.HSVToColor(hsv); + + backgroundImageView.setImageDrawable(new ColorDrawable(color)); + } + } + } + + @Override + protected void onFailureImpl(DataSource> dataSource) { + } + }, UiThreadImmediateExecutorService.getInstance()); + + break; + case ROOM_GROUP_CALL: + avatarImageView.getHierarchy() + .setImage(DisplayUtils.getRoundedDrawable( + context.getDrawable(R.drawable.ic_people_group_white_24px)) + , 100, true); + case ROOM_PUBLIC_CALL: + avatarImageView.getHierarchy() + .setImage(DisplayUtils.getRoundedDrawable( + context.getDrawable(R.drawable.ic_people_group_white_24px)) + , 100, true); + break; + default: + } + } + + private void endMediaAndVibratorNotifications() { + if (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) { + mediaPlayer.stop(); + } + + mediaPlayer.release(); + mediaPlayer = null; } - @Override - public void onDestroy() { - AvatarStatusCodeHolder.getInstance().setStatusCode(0); - leavingScreen = true; - if (handler != null) { - handler.removeCallbacksAndMessages(null); - handler = null; - } - endMediaAndVibratorNotifications(); - super.onDestroy(); + if (vibrator != null) { + vibrator.cancel(); } + } + + @Override + public void onDestroy() { + AvatarStatusCodeHolder.getInstance().setStatusCode(0); + leavingScreen = true; + if (handler != null) { + handler.removeCallbacksAndMessages(null); + handler = null; + } + endMediaAndVibratorNotifications(); + super.onDestroy(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java index c1dd50040..0a8562c19 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java @@ -56,14 +56,6 @@ import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler; import com.bluelinelabs.logansquare.LoganSquare; import com.kennyc.bottomsheet.BottomSheet; -import com.nextcloud.talk.models.RetrofitBucket; -import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall; -import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.conversations.RoomOverall; -import com.nextcloud.talk.models.json.participants.Participant; -import com.nextcloud.talk.models.json.sharees.Sharee; -import com.nextcloud.talk.models.json.sharees.ShareesOverall; import com.nextcloud.talk.R; import com.nextcloud.talk.activities.MagicCallActivity; import com.nextcloud.talk.adapters.items.GenericTextHeaderItem; @@ -76,7 +68,15 @@ import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController; import com.nextcloud.talk.controllers.bottomsheet.OperationsMenuController; import com.nextcloud.talk.events.BottomSheetLockEvent; import com.nextcloud.talk.jobs.AddParticipantsToConversation; +import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall; +import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser; +import com.nextcloud.talk.models.json.conversations.Conversation; +import com.nextcloud.talk.models.json.conversations.RoomOverall; +import com.nextcloud.talk.models.json.participants.Participant; +import com.nextcloud.talk.models.json.sharees.Sharee; +import com.nextcloud.talk.models.json.sharees.ShareesOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ConductorRemapping; import com.nextcloud.talk.utils.KeyboardUtils; @@ -110,928 +110,942 @@ import org.parceler.Parcels; @AutoInjector(NextcloudTalkApplication.class) public class ContactsController extends BaseController implements SearchView.OnQueryTextListener, - FlexibleAdapter.OnItemClickListener, FastScroller.OnScrollStateChangeListener, FlexibleAdapter.EndlessScrollListener { + FlexibleAdapter.OnItemClickListener, FastScroller.OnScrollStateChangeListener, + FlexibleAdapter.EndlessScrollListener { - public static final String TAG = "ContactsController"; + public static final String TAG = "ContactsController"; - @Nullable - @BindView(R.id.initial_relative_layout) - RelativeLayout initialRelativeLayout; - @Nullable - @BindView(R.id.secondary_relative_layout) - RelativeLayout secondaryRelativeLayout; - @Inject - UserUtils userUtils; - @Inject - EventBus eventBus; - @Inject - AppPreferences appPreferences; - @BindView(R.id.progressBar) - ProgressBar progressBar; - @BindView(R.id.recyclerView) - RecyclerView recyclerView; + @Nullable + @BindView(R.id.initial_relative_layout) + RelativeLayout initialRelativeLayout; + @Nullable + @BindView(R.id.secondary_relative_layout) + RelativeLayout secondaryRelativeLayout; + @Inject + UserUtils userUtils; + @Inject + EventBus eventBus; + @Inject + AppPreferences appPreferences; + @BindView(R.id.progressBar) + ProgressBar progressBar; + @BindView(R.id.recyclerView) + RecyclerView recyclerView; - @BindView(R.id.swipe_refresh_layout) - SwipeRefreshLayout swipeRefreshLayout; + @BindView(R.id.swipe_refresh_layout) + SwipeRefreshLayout swipeRefreshLayout; - @BindView(R.id.fast_scroller) - FastScroller fastScroller; + @BindView(R.id.fast_scroller) + FastScroller fastScroller; - @BindView(R.id.call_header_layout) - RelativeLayout conversationPrivacyToogleLayout; + @BindView(R.id.call_header_layout) + RelativeLayout conversationPrivacyToogleLayout; - @BindView(R.id.joinConversationViaLinkRelativeLayout) - RelativeLayout joinConversationViaLinkLayout; + @BindView(R.id.joinConversationViaLinkRelativeLayout) + RelativeLayout joinConversationViaLinkLayout; - @BindView(R.id.generic_rv_layout) - CoordinatorLayout genericRvLayout; + @BindView(R.id.generic_rv_layout) + CoordinatorLayout genericRvLayout; - @Inject - NcApi ncApi; - private String credentials; - private UserEntity currentUser; - private FlexibleAdapter adapter; - private List contactItems; - private BottomSheet bottomSheet; - private View view; - private int currentPage; - private int currentSearchPage; + @Inject + NcApi ncApi; + private String credentials; + private UserEntity currentUser; + private FlexibleAdapter adapter; + private List contactItems; + private BottomSheet bottomSheet; + private View view; + private int currentPage; + private int currentSearchPage; - private SmoothScrollLinearLayoutManager layoutManager; + private SmoothScrollLinearLayoutManager layoutManager; - private MenuItem searchItem; - private SearchView searchView; + private MenuItem searchItem; + private SearchView searchView; - private boolean isNewConversationView; - private boolean isPublicCall; + private boolean isNewConversationView; + private boolean isPublicCall; - private HashMap userHeaderItems = new HashMap<>(); + private HashMap userHeaderItems = new HashMap<>(); - private boolean alreadyFetching = false; - private boolean canFetchFurther = true; - private boolean canFetchSearchFurther = true; + private boolean alreadyFetching = false; + private boolean canFetchFurther = true; + private boolean canFetchSearchFurther = true; - private MenuItem doneMenuItem; + private MenuItem doneMenuItem; - private Set selectedUserIds; - private Set selectedGroupIds; - private List existingParticipants; - private boolean isAddingParticipantsView; - private String conversationToken; + private Set selectedUserIds; + private Set selectedGroupIds; + private List existingParticipants; + private boolean isAddingParticipantsView; + private String conversationToken; - public ContactsController() { - super(); - setHasOptionsMenu(true); + public ContactsController() { + super(); + setHasOptionsMenu(true); + } + + public ContactsController(Bundle args) { + super(); + setHasOptionsMenu(true); + if (args.containsKey(BundleKeys.INSTANCE.getKEY_NEW_CONVERSATION())) { + isNewConversationView = true; + existingParticipants = new ArrayList<>(); + } else if (args.containsKey(BundleKeys.INSTANCE.getKEY_ADD_PARTICIPANTS())) { + isAddingParticipantsView = true; + conversationToken = args.getString(BundleKeys.INSTANCE.getKEY_TOKEN()); + + existingParticipants = new ArrayList<>(); + + if (args.containsKey(BundleKeys.INSTANCE.getKEY_EXISTING_PARTICIPANTS())) { + existingParticipants = + args.getStringArrayList(BundleKeys.INSTANCE.getKEY_EXISTING_PARTICIPANTS()); + } } - public ContactsController(Bundle args) { - super(); - setHasOptionsMenu(true); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_NEW_CONVERSATION())) { - isNewConversationView = true; - existingParticipants = new ArrayList<>(); - } else if (args.containsKey(BundleKeys.INSTANCE.getKEY_ADD_PARTICIPANTS())) { - isAddingParticipantsView = true; - conversationToken = args.getString(BundleKeys.INSTANCE.getKEY_TOKEN()); + selectedGroupIds = new HashSet<>(); + selectedUserIds = new HashSet<>(); + } - existingParticipants = new ArrayList<>(); + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_contacts_rv, container, false); + } - if (args.containsKey(BundleKeys.INSTANCE.getKEY_EXISTING_PARTICIPANTS())) { - existingParticipants = args.getStringArrayList(BundleKeys.INSTANCE.getKEY_EXISTING_PARTICIPANTS()); - } + @Override + protected void onDetach(@NonNull View view) { + eventBus.unregister(this); + super.onDetach(view); + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + eventBus.register(this); + + if (isNewConversationView) { + toggleNewCallHeaderVisibility(!isPublicCall); + } + + if (isAddingParticipantsView) { + joinConversationViaLinkLayout.setVisibility(View.GONE); + conversationPrivacyToogleLayout.setVisibility(View.GONE); + } + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + currentUser = userUtils.getCurrentUser(); + + if (currentUser != null) { + credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); + } + + if (adapter == null) { + contactItems = new ArrayList<>(); + adapter = new FlexibleAdapter<>(contactItems, getActivity(), true); + + if (currentUser != null) { + fetchData(true); + } + } + + setupAdapter(); + prepareViews(); + } + + private void setupAdapter() { + adapter.setNotifyChangeOfUnfilteredItems(true) + .setMode(SelectableAdapter.Mode.MULTI); + + adapter.setEndlessScrollListener(this, new ProgressItem()); + + adapter.setStickyHeaderElevation(5) + .setUnlinkAllItemsOnRemoveHeaders(true) + .setDisplayHeadersAtStartUp(true) + .setStickyHeaders(true); + + adapter.addListener(this); + } + + private void selectionDone() { + if (!isAddingParticipantsView) { + if (!isPublicCall && (selectedGroupIds.size() + selectedUserIds.size() == 1)) { + String userId; + String roomType = "1"; + + if (selectedGroupIds.size() == 1) { + roomType = "2"; + userId = selectedGroupIds.iterator().next(); + } else { + userId = selectedUserIds.iterator().next(); } - selectedGroupIds = new HashSet<>(); - selectedUserIds = new HashSet<>(); - } + RetrofitBucket retrofitBucket = + ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), roomType, + userId, null); + ncApi.createRoom(credentials, + retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_contacts_rv, container, false); - } - - @Override - protected void onDetach(@NonNull View view) { - eventBus.unregister(this); - super.onDetach(view); - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - eventBus.register(this); - - if (isNewConversationView) { - toggleNewCallHeaderVisibility(!isPublicCall); - } - - if (isAddingParticipantsView) { - joinConversationViaLinkLayout.setVisibility(View.GONE); - conversationPrivacyToogleLayout.setVisibility(View.GONE); - } - - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - currentUser = userUtils.getCurrentUser(); - - if (currentUser != null) { - credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); - } - - if (adapter == null) { - contactItems = new ArrayList<>(); - adapter = new FlexibleAdapter<>(contactItems, getActivity(), true); - - if (currentUser != null) { - fetchData(true); - } - } - - setupAdapter(); - prepareViews(); - } - - private void setupAdapter() { - adapter.setNotifyChangeOfUnfilteredItems(true) - .setMode(SelectableAdapter.Mode.MULTI); - - adapter.setEndlessScrollListener(this, new ProgressItem()); - - adapter.setStickyHeaderElevation(5) - .setUnlinkAllItemsOnRemoveHeaders(true) - .setDisplayHeadersAtStartUp(true) - .setStickyHeaders(true); - - adapter.addListener(this); - } - - private void selectionDone() { - if (!isAddingParticipantsView) { - if (!isPublicCall && (selectedGroupIds.size() + selectedUserIds.size() == 1)) { - String userId; - String roomType = "1"; - - if (selectedGroupIds.size() == 1) { - roomType = "2"; - userId = selectedGroupIds.iterator().next(); - } else { - userId = selectedUserIds.iterator().next(); - } - - RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), roomType, - userId, null); - ncApi.createRoom(credentials, - retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), roomOverall.getOcs().getData().getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), roomOverall.getOcs().getData().getRoomId()); - - if (currentUser.hasSpreedFeatureCapability("chat-v2")) { - ncApi.getRoom(credentials, - ApiUtils.getRoom(currentUser.getBaseUrl(), - roomOverall.getOcs().getData().getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), - Parcels.wrap(roomOverall.getOcs().getData())); - - ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(), - roomOverall.getOcs().getData().getToken(), bundle, true); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } else { - conversationIntent.putExtras(bundle); - startActivity(conversationIntent); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - if (!isDestroyed() && !isBeingDestroyed()) { - getRouter().popCurrentController(); - } - } - }, 100); - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - } - }); - } else { + } + @Override + public void onNext(RoomOverall roomOverall) { + Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); Bundle bundle = new Bundle(); - Conversation.ConversationType roomType; - if (isPublicCall) { - roomType = Conversation.ConversationType.ROOM_PUBLIC_CALL; + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), + roomOverall.getOcs().getData().getToken()); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), + roomOverall.getOcs().getData().getRoomId()); + + if (currentUser.hasSpreedFeatureCapability("chat-v2")) { + ncApi.getRoom(credentials, + ApiUtils.getRoom(currentUser.getBaseUrl(), + roomOverall.getOcs().getData().getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(RoomOverall roomOverall) { + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), + Parcels.wrap(roomOverall.getOcs().getData())); + + ConductorRemapping.INSTANCE.remapChatController(getRouter(), + currentUser.getId(), + roomOverall.getOcs().getData().getToken(), bundle, true); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); } else { - roomType = Conversation.ConversationType.ROOM_GROUP_CALL; - } - - ArrayList userIdsArray = new ArrayList<>(selectedUserIds); - ArrayList groupIdsArray = new ArrayList<>(selectedGroupIds); - - - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE(), Parcels.wrap(roomType)); - bundle.putStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS(), userIdsArray); - bundle.putStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_GROUP(), groupIdsArray); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 11); - prepareAndShowBottomSheetWithBundle(bundle, true); - } - } else { - String[] userIdsArray = selectedUserIds.toArray(new String[selectedUserIds.size()]); - String[] groupIdsArray = selectedGroupIds.toArray(new String[selectedGroupIds.size()]); - - Data.Builder data = new Data.Builder(); - data.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), currentUser.getId()); - data.putString(BundleKeys.INSTANCE.getKEY_TOKEN(), conversationToken); - data.putStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_USERS(), userIdsArray); - data.putStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_GROUPS(), groupIdsArray); - - OneTimeWorkRequest addParticipantsToConversationWorker = - new OneTimeWorkRequest.Builder(AddParticipantsToConversation.class).setInputData(data.build()).build(); - WorkManager.getInstance().enqueue(addParticipantsToConversationWorker); - - getRouter().popCurrentController(); - } - } - - private void initSearchView() { - if (getActivity() != null) { - SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); - if (searchItem != null) { - searchView = (SearchView) MenuItemCompat.getActionView(searchItem); - searchView.setMaxWidth(Integer.MAX_VALUE); - searchView.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER); - int imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.getIsKeyboardIncognito()) { - imeOptions |= EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; - } - searchView.setImeOptions(imeOptions); - searchView.setQueryHint(getResources().getString(R.string.nc_search)); - if (searchManager != null) { - searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); - } - searchView.setOnQueryTextListener(this); - } - } - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - getRouter().popCurrentController(); - return true; - case R.id.contacts_selection_done: - selectionDone(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, @NonNull MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_contacts, menu); - searchItem = menu.findItem(R.id.action_search); - doneMenuItem = menu.findItem(R.id.contacts_selection_done); - - initSearchView(); - } - - @Override - public void onPrepareOptionsMenu(@NonNull Menu menu) { - super.onPrepareOptionsMenu(menu); - checkAndHandleDoneMenuItem(); - if (adapter.hasFilter()) { - searchItem.expandActionView(); - searchView.setQuery((CharSequence) adapter.getFilter(String.class), false); - } - } - - private void fetchData(boolean startFromScratch) { - alreadyFetching = true; - Set shareeHashSet = new HashSet<>(); - Set autocompleteUsersHashSet = new HashSet<>(); - - userHeaderItems = new HashMap<>(); - - String query = (String) adapter.getFilter(String.class); - - RetrofitBucket retrofitBucket; - boolean serverIs14OrUp = false; - if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { - // a hack to see if we're on 14 or not - retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser.getBaseUrl(), query); - serverIs14OrUp = true; - } else { - retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(currentUser.getBaseUrl(), query); - } - - int page = 1; - if (!startFromScratch) { - if (TextUtils.isEmpty(query)) { - page = currentPage + 1; - } else { - page = currentSearchPage + 1; - } - } - - Map modifiedQueryMap = new HashMap<>(retrofitBucket.getQueryMap()); - modifiedQueryMap.put("page", page); - modifiedQueryMap.put("perPage", 100); - - List shareTypesList = null; - - if (serverIs14OrUp) { - shareTypesList = new ArrayList<>(); - // users - shareTypesList.add("0"); - // groups - shareTypesList.add("1"); - // mails - //shareTypesList.add("4"); - - - modifiedQueryMap.put("shareTypes[]", shareTypesList); - } - - boolean finalServerIs14OrUp = serverIs14OrUp; - ncApi.getContactsWithSearchParam( - credentials, - retrofitBucket.getUrl(), shareTypesList, modifiedQueryMap) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(3) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { + conversationIntent.putExtras(bundle); + startActivity(conversationIntent); + new Handler().postDelayed(new Runnable() { @Override - public void onSubscribe(Disposable d) { + public void run() { + if (!isDestroyed() && !isBeingDestroyed()) { + getRouter().popCurrentController(); + } } - - @Override - public void onNext(ResponseBody responseBody) { - if (responseBody != null) { - Participant participant; - - List newUserItemList = new ArrayList<>(); - - try { - if (!finalServerIs14OrUp) { - ShareesOverall shareesOverall = LoganSquare.parse(responseBody.string(), ShareesOverall.class); - - if (shareesOverall.getOcs().getData().getUsers() != null) { - shareeHashSet.addAll(shareesOverall.getOcs().getData().getUsers()); - } - - if (shareesOverall.getOcs().getData().getExactUsers() != null && - shareesOverall.getOcs().getData().getExactUsers().getExactSharees() != null) { - shareeHashSet.addAll(shareesOverall.getOcs().getData(). - getExactUsers().getExactSharees()); - } - - for (Sharee sharee : shareeHashSet) { - if (!sharee.getValue().getShareWith().equals(currentUser.getUserId()) && !existingParticipants.contains(sharee.getValue().getShareWith())) { - participant = new Participant(); - participant.setDisplayName(sharee.getLabel()); - String headerTitle; - - headerTitle = sharee.getLabel().substring(0, 1).toUpperCase(); - - GenericTextHeaderItem genericTextHeaderItem; - if (!userHeaderItems.containsKey(headerTitle)) { - genericTextHeaderItem = new GenericTextHeaderItem(headerTitle); - userHeaderItems.put(headerTitle, genericTextHeaderItem); - } - - participant.setUserId(sharee.getValue().getShareWith()); - - UserItem newContactItem = new UserItem(participant, currentUser, - userHeaderItems.get(headerTitle), getActivity()); - - if (!contactItems.contains(newContactItem)) { - newUserItemList.add(newContactItem); - } - - } - - } - - } else { - AutocompleteOverall autocompleteOverall = LoganSquare.parse(responseBody.string(), AutocompleteOverall.class); - autocompleteUsersHashSet.addAll(autocompleteOverall.getOcs().getData()); - - for (AutocompleteUser autocompleteUser : autocompleteUsersHashSet) { - if (!autocompleteUser.getId().equals(currentUser.getUserId()) && !existingParticipants.contains(autocompleteUser.getId())) { - participant = new Participant(); - participant.setUserId(autocompleteUser.getId()); - participant.setDisplayName(autocompleteUser.getLabel()); - participant.setSource(autocompleteUser.getSource()); - - String headerTitle; - - if (!autocompleteUser.getSource().equals("groups")) { - headerTitle = participant.getDisplayName().substring(0, 1).toUpperCase(); - } else { - headerTitle = getResources().getString(R.string.nc_groups); - } - - GenericTextHeaderItem genericTextHeaderItem; - if (!userHeaderItems.containsKey(headerTitle)) { - genericTextHeaderItem = new GenericTextHeaderItem(headerTitle); - userHeaderItems.put(headerTitle, genericTextHeaderItem); - } - - - UserItem newContactItem = new UserItem(participant, currentUser, - userHeaderItems.get(headerTitle), getActivity()); - - if (!contactItems.contains(newContactItem)) { - newUserItemList.add(newContactItem); - } - - } - } - } - } catch (Exception exception) { - Log.e(TAG, "Parsing response body failed while getting contacts"); - } - - if (TextUtils.isEmpty((CharSequence) modifiedQueryMap.get("search"))) { - canFetchFurther = !shareeHashSet.isEmpty() || (finalServerIs14OrUp && autocompleteUsersHashSet.size() == 100); - currentPage = (int) modifiedQueryMap.get("page"); - } else { - canFetchSearchFurther = !shareeHashSet.isEmpty() || (finalServerIs14OrUp && autocompleteUsersHashSet.size() == 100); - currentSearchPage = (int) modifiedQueryMap.get("page"); - } - - userHeaderItems = new HashMap<>(); - contactItems.addAll(newUserItemList); - - Collections.sort(newUserItemList, (o1, o2) -> { - String firstName; - String secondName; - - - if (o1 instanceof UserItem) { - firstName = ((UserItem) o1).getModel().getDisplayName(); - } else { - firstName = ((GenericTextHeaderItem) o1).getModel(); - } - - if (o2 instanceof UserItem) { - secondName = ((UserItem) o2).getModel().getDisplayName(); - } else { - secondName = ((GenericTextHeaderItem) o2).getModel(); - } - - if (o1 instanceof UserItem && o2 instanceof UserItem) { - if ("groups".equals(((UserItem) o1).getModel().getSource()) && "groups".equals(((UserItem) o2).getModel().getSource())) { - return firstName.compareToIgnoreCase(secondName); - } else if ("groups".equals(((UserItem) o1).getModel().getSource())) { - return -1; - } else if ("groups".equals(((UserItem) o2).getModel().getSource())) { - return 1; - } - } - - return firstName.compareToIgnoreCase(secondName); - }); - - Collections.sort(contactItems, (o1, o2) -> { - String firstName; - String secondName; - - - if (o1 instanceof UserItem) { - firstName = ((UserItem) o1).getModel().getDisplayName(); - } else { - firstName = ((GenericTextHeaderItem) o1).getModel(); - } - - if (o2 instanceof UserItem) { - secondName = ((UserItem) o2).getModel().getDisplayName(); - } else { - secondName = ((GenericTextHeaderItem) o2).getModel(); - } - - if (o1 instanceof UserItem && o2 instanceof UserItem) { - if ("groups".equals(((UserItem) o1).getModel().getSource()) && "groups".equals(((UserItem) o2).getModel().getSource())) { - return firstName.compareToIgnoreCase(secondName); - } else if ("groups".equals(((UserItem) o1).getModel().getSource())) { - return -1; - } else if ("groups".equals(((UserItem) o2).getModel().getSource())) { - return 1; - } - } - - return firstName.compareToIgnoreCase(secondName); - }); - - - if (newUserItemList.size() > 0) { - adapter.updateDataSet(newUserItemList); - } else { - adapter.filterItems(); - } - - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } - } - - } - - @Override - public void onError(Throwable e) { - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } - - } - - @Override - public void onComplete() { - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } - alreadyFetching = false; - - disengageProgressBar(); - } - }); - - } - - private void prepareViews() { - layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - recyclerView.setAdapter(adapter); - - swipeRefreshLayout.setOnRefreshListener(() -> fetchData(true)); - swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); - - fastScroller.addOnScrollStateChangeListener(this); - adapter.setFastScroller(fastScroller); - fastScroller.setBubbleTextCreator(position -> { - IFlexible abstractFlexibleItem = adapter.getItem(position); - if (abstractFlexibleItem instanceof UserItem) { - return ((UserItem) adapter.getItem(position)).getHeader().getModel(); - } else if (abstractFlexibleItem instanceof GenericTextHeaderItem) { - return ((GenericTextHeaderItem) adapter.getItem(position)).getModel(); - } else { - return ""; - } - }); - - disengageProgressBar(); - } - - private void disengageProgressBar() { - if (!alreadyFetching) { - progressBar.setVisibility(View.GONE); - genericRvLayout.setVisibility(View.VISIBLE); - - if (isNewConversationView) { - conversationPrivacyToogleLayout.setVisibility(View.VISIBLE); - joinConversationViaLinkLayout.setVisibility(View.VISIBLE); - } - } - } - - @Override - public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { - adapter.onSaveInstanceState(outState); - super.onSaveViewState(view, outState); - } - - @Override - public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { - super.onRestoreViewState(view, savedViewState); - if (adapter != null) { - adapter.onRestoreInstanceState(savedViewState); - } - } - - @Override - public boolean onQueryTextChange(String newText) { - if (!newText.equals("") && adapter.hasNewFilter(newText)) { - adapter.setFilter(newText); - fetchData(true); - } else if (newText.equals("")) { - adapter.setFilter(""); - adapter.updateDataSet(contactItems); - } - - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setEnabled(!adapter.hasFilter()); - } - - return true; - } - - @Override - public boolean onQueryTextSubmit(String query) { - return onQueryTextChange(query); - } - - private void checkAndHandleDoneMenuItem() { - if (adapter != null && doneMenuItem != null) { - if ((selectedGroupIds.size() + selectedUserIds.size() > 0) || isPublicCall) { - doneMenuItem.setVisible(true); - } else { - doneMenuItem.setVisible(false); - } - } else if (doneMenuItem != null) { - doneMenuItem.setVisible(false); - } - } - - @Override - public String getTitle() { - if (!isNewConversationView && !isAddingParticipantsView) { - return getResources().getString(R.string.nc_app_name); - } else { - return getResources().getString(R.string.nc_select_contacts); - } - } - - @Override - public void onFastScrollerStateChange(boolean scrolling) { - swipeRefreshLayout.setEnabled(!scrolling); - } - - - private void prepareAndShowBottomSheetWithBundle(Bundle bundle, boolean showEntrySheet) { - if (view == null) { - view = getActivity().getLayoutInflater().inflate(R.layout.bottom_sheet, null, false); - } - - if (bottomSheet == null) { - bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create(); - } - - if (showEntrySheet) { - getChildRouter((ViewGroup) view).setRoot( - RouterTransaction.with(new EntryMenuController(bundle)) - .popChangeHandler(new VerticalChangeHandler()) - .pushChangeHandler(new VerticalChangeHandler())); - } else { - getChildRouter((ViewGroup) view).setRoot( - RouterTransaction.with(new OperationsMenuController(bundle)) - .popChangeHandler(new VerticalChangeHandler()) - .pushChangeHandler(new VerticalChangeHandler())); - } - - bottomSheet.setOnShowListener(dialog -> { - if (showEntrySheet) { - new KeyboardUtils(getActivity(), bottomSheet.getLayout(), true); - } else { - eventBus.post(new BottomSheetLockEvent(false, 0, - false, false)); - } - }); - - bottomSheet.setOnDismissListener(dialog -> getActionBar().setDisplayHomeAsUpEnabled(getRouter().getBackstackSize() > 1)); - - bottomSheet.show(); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(BottomSheetLockEvent bottomSheetLockEvent) { - - if (bottomSheet != null) { - if (!bottomSheetLockEvent.isCancelable()) { - bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); - } else { - bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); - if (bottomSheet.isShowing() && bottomSheetLockEvent.isCancel()) { - new Handler().postDelayed(() -> { - bottomSheet.setOnCancelListener(null); - bottomSheet.cancel(); - - }, bottomSheetLockEvent.getDelay()); + }, 100); } - } - } - } + } - @Override - public boolean onItemClick(View view, int position) { - if (adapter.getItem(position) instanceof UserItem) { - if (!isNewConversationView && !isAddingParticipantsView) { - UserItem userItem = (UserItem) adapter.getItem(position); - String roomType = "1"; + @Override + public void onError(Throwable e) { - if ("groups".equals(userItem.getModel().getSource())) { - roomType = "2"; - } + } - RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), roomType, userItem.getModel().getUserId(), null); + @Override + public void onComplete() { + } + }); + } else { - ncApi.createRoom(credentials, - retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - if (getActivity() != null) { - Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), roomOverall.getOcs().getData().getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), roomOverall.getOcs().getData().getRoomId()); - conversationIntent.putExtras(bundle); - - if (currentUser.hasSpreedFeatureCapability("chat-v2")) { - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), - Parcels.wrap(roomOverall.getOcs().getData())); - - ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(), - roomOverall.getOcs().getData().getToken(), bundle, true); - } else { - startActivity(conversationIntent); - new Handler().postDelayed(() -> getRouter().popCurrentController(), 100); - } - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } else { - Participant participant = ((UserItem) adapter.getItem(position)).getModel(); - participant.setSelected(!participant.isSelected()); - - if ("groups".equals(participant.getSource())) { - if (participant.isSelected()) { - selectedGroupIds.add(participant.getUserId()); - } else { - selectedGroupIds.remove(participant.getUserId()); - } - } else { - if (participant.isSelected()) { - selectedUserIds.add(participant.getUserId()); - } else { - selectedUserIds.remove(participant.getUserId()); - } - } - - if (currentUser.hasSpreedFeatureCapability("last-room-activity") - && !currentUser.hasSpreedFeatureCapability("invite-groups-and-mails") && - "groups".equals(((UserItem) adapter.getItem(position)).getModel().getSource()) && - participant.isSelected() && - adapter.getSelectedItemCount() > 1) { - List currentItems = adapter.getCurrentItems(); - Participant internalParticipant; - for (int i = 0; i < currentItems.size(); i++) { - internalParticipant = currentItems.get(i).getModel(); - if (internalParticipant.getUserId().equals(participant.getUserId()) && - "groups".equals(internalParticipant.getSource()) && internalParticipant.isSelected()) { - internalParticipant.setSelected(false); - selectedGroupIds.remove(internalParticipant.getUserId()); - } - } - - } - - adapter.notifyDataSetChanged(); - checkAndHandleDoneMenuItem(); - } - } - return true; - } - - @Optional - @OnClick(R.id.joinConversationViaLinkRelativeLayout) - void joinConversationViaLink() { Bundle bundle = new Bundle(); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 10); + Conversation.ConversationType roomType; + if (isPublicCall) { + roomType = Conversation.ConversationType.ROOM_PUBLIC_CALL; + } else { + roomType = Conversation.ConversationType.ROOM_GROUP_CALL; + } + ArrayList userIdsArray = new ArrayList<>(selectedUserIds); + ArrayList groupIdsArray = new ArrayList<>(selectedGroupIds); + + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE(), + Parcels.wrap(roomType)); + bundle.putStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS(), userIdsArray); + bundle.putStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_GROUP(), groupIdsArray); + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 11); prepareAndShowBottomSheetWithBundle(bundle, true); + } + } else { + String[] userIdsArray = selectedUserIds.toArray(new String[selectedUserIds.size()]); + String[] groupIdsArray = selectedGroupIds.toArray(new String[selectedGroupIds.size()]); + + Data.Builder data = new Data.Builder(); + data.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), currentUser.getId()); + data.putString(BundleKeys.INSTANCE.getKEY_TOKEN(), conversationToken); + data.putStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_USERS(), userIdsArray); + data.putStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_GROUPS(), groupIdsArray); + + OneTimeWorkRequest addParticipantsToConversationWorker = + new OneTimeWorkRequest.Builder(AddParticipantsToConversation.class).setInputData( + data.build()).build(); + WorkManager.getInstance().enqueue(addParticipantsToConversationWorker); + + getRouter().popCurrentController(); + } + } + + private void initSearchView() { + if (getActivity() != null) { + SearchManager searchManager = + (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); + if (searchItem != null) { + searchView = (SearchView) MenuItemCompat.getActionView(searchItem); + searchView.setMaxWidth(Integer.MAX_VALUE); + searchView.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER); + int imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && appPreferences.getIsKeyboardIncognito()) { + imeOptions |= EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; + } + searchView.setImeOptions(imeOptions); + searchView.setQueryHint(getResources().getString(R.string.nc_search)); + if (searchManager != null) { + searchView.setSearchableInfo( + searchManager.getSearchableInfo(getActivity().getComponentName())); + } + searchView.setOnQueryTextListener(this); + } + } + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + getRouter().popCurrentController(); + return true; + case R.id.contacts_selection_done: + selectionDone(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, @NonNull MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_contacts, menu); + searchItem = menu.findItem(R.id.action_search); + doneMenuItem = menu.findItem(R.id.contacts_selection_done); + + initSearchView(); + } + + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + super.onPrepareOptionsMenu(menu); + checkAndHandleDoneMenuItem(); + if (adapter.hasFilter()) { + searchItem.expandActionView(); + searchView.setQuery((CharSequence) adapter.getFilter(String.class), false); + } + } + + private void fetchData(boolean startFromScratch) { + alreadyFetching = true; + Set shareeHashSet = new HashSet<>(); + Set autocompleteUsersHashSet = new HashSet<>(); + + userHeaderItems = new HashMap<>(); + + String query = (String) adapter.getFilter(String.class); + + RetrofitBucket retrofitBucket; + boolean serverIs14OrUp = false; + if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { + // a hack to see if we're on 14 or not + retrofitBucket = + ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser.getBaseUrl(), query); + serverIs14OrUp = true; + } else { + retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(currentUser.getBaseUrl(), query); } - @Optional - @OnClick(R.id.call_header_layout) - void toggleCallHeader() { - toggleNewCallHeaderVisibility(isPublicCall); - isPublicCall = !isPublicCall; + int page = 1; + if (!startFromScratch) { + if (TextUtils.isEmpty(query)) { + page = currentPage + 1; + } else { + page = currentSearchPage + 1; + } + } - if (isPublicCall) { - joinConversationViaLinkLayout.setVisibility(View.GONE); - } else { - joinConversationViaLinkLayout.setVisibility(View.VISIBLE); - } + Map modifiedQueryMap = new HashMap<>(retrofitBucket.getQueryMap()); + modifiedQueryMap.put("page", page); + modifiedQueryMap.put("perPage", 100); - if (isPublicCall) { - List currentItems = adapter.getCurrentItems(); - Participant internalParticipant; - for (int i = 0; i < currentItems.size(); i++) { - if (currentItems.get(i) instanceof UserItem) { - internalParticipant = ((UserItem) currentItems.get(i)).getModel(); - if ("groups".equals(internalParticipant.getSource()) && internalParticipant.isSelected()) { - internalParticipant.setSelected(false); - selectedGroupIds.remove(internalParticipant.getUserId()); + List shareTypesList = null; + + if (serverIs14OrUp) { + shareTypesList = new ArrayList<>(); + // users + shareTypesList.add("0"); + // groups + shareTypesList.add("1"); + // mails + //shareTypesList.add("4"); + + modifiedQueryMap.put("shareTypes[]", shareTypesList); + } + + boolean finalServerIs14OrUp = serverIs14OrUp; + ncApi.getContactsWithSearchParam( + credentials, + retrofitBucket.getUrl(), shareTypesList, modifiedQueryMap) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(3) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(ResponseBody responseBody) { + if (responseBody != null) { + Participant participant; + + List newUserItemList = new ArrayList<>(); + + try { + if (!finalServerIs14OrUp) { + ShareesOverall shareesOverall = + LoganSquare.parse(responseBody.string(), ShareesOverall.class); + + if (shareesOverall.getOcs().getData().getUsers() != null) { + shareeHashSet.addAll(shareesOverall.getOcs().getData().getUsers()); + } + + if (shareesOverall.getOcs().getData().getExactUsers() != null && + shareesOverall.getOcs().getData().getExactUsers().getExactSharees() != null) { + shareeHashSet.addAll(shareesOverall.getOcs().getData(). + getExactUsers().getExactSharees()); + } + + for (Sharee sharee : shareeHashSet) { + if (!sharee.getValue().getShareWith().equals(currentUser.getUserId()) + && !existingParticipants.contains(sharee.getValue().getShareWith())) { + participant = new Participant(); + participant.setDisplayName(sharee.getLabel()); + String headerTitle; + + headerTitle = sharee.getLabel().substring(0, 1).toUpperCase(); + + GenericTextHeaderItem genericTextHeaderItem; + if (!userHeaderItems.containsKey(headerTitle)) { + genericTextHeaderItem = new GenericTextHeaderItem(headerTitle); + userHeaderItems.put(headerTitle, genericTextHeaderItem); + } + + participant.setUserId(sharee.getValue().getShareWith()); + + UserItem newContactItem = new UserItem(participant, currentUser, + userHeaderItems.get(headerTitle), getActivity()); + + if (!contactItems.contains(newContactItem)) { + newUserItemList.add(newContactItem); + } } + } + } else { + AutocompleteOverall autocompleteOverall = + LoganSquare.parse(responseBody.string(), AutocompleteOverall.class); + autocompleteUsersHashSet.addAll(autocompleteOverall.getOcs().getData()); + + for (AutocompleteUser autocompleteUser : autocompleteUsersHashSet) { + if (!autocompleteUser.getId().equals(currentUser.getUserId()) + && !existingParticipants.contains(autocompleteUser.getId())) { + participant = new Participant(); + participant.setUserId(autocompleteUser.getId()); + participant.setDisplayName(autocompleteUser.getLabel()); + participant.setSource(autocompleteUser.getSource()); + + String headerTitle; + + if (!autocompleteUser.getSource().equals("groups")) { + headerTitle = participant.getDisplayName().substring(0, 1).toUpperCase(); + } else { + headerTitle = getResources().getString(R.string.nc_groups); + } + + GenericTextHeaderItem genericTextHeaderItem; + if (!userHeaderItems.containsKey(headerTitle)) { + genericTextHeaderItem = new GenericTextHeaderItem(headerTitle); + userHeaderItems.put(headerTitle, genericTextHeaderItem); + } + + UserItem newContactItem = new UserItem(participant, currentUser, + userHeaderItems.get(headerTitle), getActivity()); + + if (!contactItems.contains(newContactItem)) { + newUserItemList.add(newContactItem); + } + } + } } + } catch (Exception exception) { + Log.e(TAG, "Parsing response body failed while getting contacts"); + } + + if (TextUtils.isEmpty((CharSequence) modifiedQueryMap.get("search"))) { + canFetchFurther = !shareeHashSet.isEmpty() || (finalServerIs14OrUp + && autocompleteUsersHashSet.size() == 100); + currentPage = (int) modifiedQueryMap.get("page"); + } else { + canFetchSearchFurther = !shareeHashSet.isEmpty() || (finalServerIs14OrUp + && autocompleteUsersHashSet.size() == 100); + currentSearchPage = (int) modifiedQueryMap.get("page"); + } + + userHeaderItems = new HashMap<>(); + contactItems.addAll(newUserItemList); + + Collections.sort(newUserItemList, (o1, o2) -> { + String firstName; + String secondName; + + if (o1 instanceof UserItem) { + firstName = ((UserItem) o1).getModel().getDisplayName(); + } else { + firstName = ((GenericTextHeaderItem) o1).getModel(); + } + + if (o2 instanceof UserItem) { + secondName = ((UserItem) o2).getModel().getDisplayName(); + } else { + secondName = ((GenericTextHeaderItem) o2).getModel(); + } + + if (o1 instanceof UserItem && o2 instanceof UserItem) { + if ("groups".equals(((UserItem) o1).getModel().getSource()) && "groups".equals( + ((UserItem) o2).getModel().getSource())) { + return firstName.compareToIgnoreCase(secondName); + } else if ("groups".equals(((UserItem) o1).getModel().getSource())) { + return -1; + } else if ("groups".equals(((UserItem) o2).getModel().getSource())) { + return 1; + } + } + + return firstName.compareToIgnoreCase(secondName); + }); + + Collections.sort(contactItems, (o1, o2) -> { + String firstName; + String secondName; + + if (o1 instanceof UserItem) { + firstName = ((UserItem) o1).getModel().getDisplayName(); + } else { + firstName = ((GenericTextHeaderItem) o1).getModel(); + } + + if (o2 instanceof UserItem) { + secondName = ((UserItem) o2).getModel().getDisplayName(); + } else { + secondName = ((GenericTextHeaderItem) o2).getModel(); + } + + if (o1 instanceof UserItem && o2 instanceof UserItem) { + if ("groups".equals(((UserItem) o1).getModel().getSource()) && "groups".equals( + ((UserItem) o2).getModel().getSource())) { + return firstName.compareToIgnoreCase(secondName); + } else if ("groups".equals(((UserItem) o1).getModel().getSource())) { + return -1; + } else if ("groups".equals(((UserItem) o2).getModel().getSource())) { + return 1; + } + } + + return firstName.compareToIgnoreCase(secondName); + }); + + if (newUserItemList.size() > 0) { + adapter.updateDataSet(newUserItemList); + } else { + adapter.filterItems(); + } + + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } } + } + + @Override + public void onError(Throwable e) { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } + } + + @Override + public void onComplete() { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } + alreadyFetching = false; + + disengageProgressBar(); + } + }); + } + + private void prepareViews() { + layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setHasFixedSize(true); + recyclerView.setAdapter(adapter); + + swipeRefreshLayout.setOnRefreshListener(() -> fetchData(true)); + swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); + + fastScroller.addOnScrollStateChangeListener(this); + adapter.setFastScroller(fastScroller); + fastScroller.setBubbleTextCreator(position -> { + IFlexible abstractFlexibleItem = adapter.getItem(position); + if (abstractFlexibleItem instanceof UserItem) { + return ((UserItem) adapter.getItem(position)).getHeader().getModel(); + } else if (abstractFlexibleItem instanceof GenericTextHeaderItem) { + return ((GenericTextHeaderItem) adapter.getItem(position)).getModel(); + } else { + return ""; + } + }); + + disengageProgressBar(); + } + + private void disengageProgressBar() { + if (!alreadyFetching) { + progressBar.setVisibility(View.GONE); + genericRvLayout.setVisibility(View.VISIBLE); + + if (isNewConversationView) { + conversationPrivacyToogleLayout.setVisibility(View.VISIBLE); + joinConversationViaLinkLayout.setVisibility(View.VISIBLE); + } + } + } + + @Override + public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { + adapter.onSaveInstanceState(outState); + super.onSaveViewState(view, outState); + } + + @Override + public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { + super.onRestoreViewState(view, savedViewState); + if (adapter != null) { + adapter.onRestoreInstanceState(savedViewState); + } + } + + @Override + public boolean onQueryTextChange(String newText) { + if (!newText.equals("") && adapter.hasNewFilter(newText)) { + adapter.setFilter(newText); + fetchData(true); + } else if (newText.equals("")) { + adapter.setFilter(""); + adapter.updateDataSet(contactItems); + } + + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setEnabled(!adapter.hasFilter()); + } + + return true; + } + + @Override + public boolean onQueryTextSubmit(String query) { + return onQueryTextChange(query); + } + + private void checkAndHandleDoneMenuItem() { + if (adapter != null && doneMenuItem != null) { + if ((selectedGroupIds.size() + selectedUserIds.size() > 0) || isPublicCall) { + doneMenuItem.setVisible(true); + } else { + doneMenuItem.setVisible(false); + } + } else if (doneMenuItem != null) { + doneMenuItem.setVisible(false); + } + } + + @Override + public String getTitle() { + if (!isNewConversationView && !isAddingParticipantsView) { + return getResources().getString(R.string.nc_app_name); + } else { + return getResources().getString(R.string.nc_select_contacts); + } + } + + @Override + public void onFastScrollerStateChange(boolean scrolling) { + swipeRefreshLayout.setEnabled(!scrolling); + } + + private void prepareAndShowBottomSheetWithBundle(Bundle bundle, boolean showEntrySheet) { + if (view == null) { + view = getActivity().getLayoutInflater().inflate(R.layout.bottom_sheet, null, false); + } + + if (bottomSheet == null) { + bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create(); + } + + if (showEntrySheet) { + getChildRouter((ViewGroup) view).setRoot( + RouterTransaction.with(new EntryMenuController(bundle)) + .popChangeHandler(new VerticalChangeHandler()) + .pushChangeHandler(new VerticalChangeHandler())); + } else { + getChildRouter((ViewGroup) view).setRoot( + RouterTransaction.with(new OperationsMenuController(bundle)) + .popChangeHandler(new VerticalChangeHandler()) + .pushChangeHandler(new VerticalChangeHandler())); + } + + bottomSheet.setOnShowListener(dialog -> { + if (showEntrySheet) { + new KeyboardUtils(getActivity(), bottomSheet.getLayout(), true); + } else { + eventBus.post(new BottomSheetLockEvent(false, 0, + false, false)); + } + }); + + bottomSheet.setOnDismissListener( + dialog -> getActionBar().setDisplayHomeAsUpEnabled(getRouter().getBackstackSize() > 1)); + + bottomSheet.show(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(BottomSheetLockEvent bottomSheetLockEvent) { + + if (bottomSheet != null) { + if (!bottomSheetLockEvent.isCancelable()) { + bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); + } else { + bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); + if (bottomSheet.isShowing() && bottomSheetLockEvent.isCancel()) { + new Handler().postDelayed(() -> { + bottomSheet.setOnCancelListener(null); + bottomSheet.cancel(); + }, bottomSheetLockEvent.getDelay()); + } + } + } + } + + @Override + public boolean onItemClick(View view, int position) { + if (adapter.getItem(position) instanceof UserItem) { + if (!isNewConversationView && !isAddingParticipantsView) { + UserItem userItem = (UserItem) adapter.getItem(position); + String roomType = "1"; + + if ("groups".equals(userItem.getModel().getSource())) { + roomType = "2"; } - for (int i = 0; i < adapter.getItemCount(); i++) { - if (adapter.getItem(i) instanceof UserItem) { - UserItem userItem = (UserItem) adapter.getItem(i); - if ("groups".equals(userItem.getModel().getSource())) { - userItem.setEnabled(!isPublicCall); + RetrofitBucket retrofitBucket = + ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), roomType, + userItem.getModel().getUserId(), null); + + ncApi.createRoom(credentials, + retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(RoomOverall roomOverall) { + if (getActivity() != null) { + Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); + Bundle bundle = new Bundle(); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), + roomOverall.getOcs().getData().getToken()); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), + roomOverall.getOcs().getData().getRoomId()); + conversationIntent.putExtras(bundle); + + if (currentUser.hasSpreedFeatureCapability("chat-v2")) { + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), + Parcels.wrap(roomOverall.getOcs().getData())); + + ConductorRemapping.INSTANCE.remapChatController(getRouter(), + currentUser.getId(), + roomOverall.getOcs().getData().getToken(), bundle, true); + } else { + startActivity(conversationIntent); + new Handler().postDelayed(() -> getRouter().popCurrentController(), 100); + } } - } + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } else { + Participant participant = ((UserItem) adapter.getItem(position)).getModel(); + participant.setSelected(!participant.isSelected()); + + if ("groups".equals(participant.getSource())) { + if (participant.isSelected()) { + selectedGroupIds.add(participant.getUserId()); + } else { + selectedGroupIds.remove(participant.getUserId()); + } + } else { + if (participant.isSelected()) { + selectedUserIds.add(participant.getUserId()); + } else { + selectedUserIds.remove(participant.getUserId()); + } + } + + if (currentUser.hasSpreedFeatureCapability("last-room-activity") + && !currentUser.hasSpreedFeatureCapability("invite-groups-and-mails") && + "groups".equals(((UserItem) adapter.getItem(position)).getModel().getSource()) && + participant.isSelected() && + adapter.getSelectedItemCount() > 1) { + List currentItems = adapter.getCurrentItems(); + Participant internalParticipant; + for (int i = 0; i < currentItems.size(); i++) { + internalParticipant = currentItems.get(i).getModel(); + if (internalParticipant.getUserId().equals(participant.getUserId()) + && + "groups".equals(internalParticipant.getSource()) + && internalParticipant.isSelected()) { + internalParticipant.setSelected(false); + selectedGroupIds.remove(internalParticipant.getUserId()); + } + } } - checkAndHandleDoneMenuItem(); adapter.notifyDataSetChanged(); + checkAndHandleDoneMenuItem(); + } + } + return true; + } + + @Optional + @OnClick(R.id.joinConversationViaLinkRelativeLayout) + void joinConversationViaLink() { + Bundle bundle = new Bundle(); + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 10); + + prepareAndShowBottomSheetWithBundle(bundle, true); + } + + @Optional + @OnClick(R.id.call_header_layout) + void toggleCallHeader() { + toggleNewCallHeaderVisibility(isPublicCall); + isPublicCall = !isPublicCall; + + if (isPublicCall) { + joinConversationViaLinkLayout.setVisibility(View.GONE); + } else { + joinConversationViaLinkLayout.setVisibility(View.VISIBLE); } - private void toggleNewCallHeaderVisibility(boolean showInitialLayout) { - if (showInitialLayout) { - initialRelativeLayout.setVisibility(View.VISIBLE); - secondaryRelativeLayout.setVisibility(View.GONE); - } else { - initialRelativeLayout.setVisibility(View.GONE); - secondaryRelativeLayout.setVisibility(View.VISIBLE); + if (isPublicCall) { + List currentItems = adapter.getCurrentItems(); + Participant internalParticipant; + for (int i = 0; i < currentItems.size(); i++) { + if (currentItems.get(i) instanceof UserItem) { + internalParticipant = ((UserItem) currentItems.get(i)).getModel(); + if ("groups".equals(internalParticipant.getSource()) + && internalParticipant.isSelected()) { + internalParticipant.setSelected(false); + selectedGroupIds.remove(internalParticipant.getUserId()); + } } + } } - @Override - public void noMoreLoad(int newItemsSize) { - } - - @Override - public void onLoadMore(int lastPosition, int currentPage) { - String query = (String) adapter.getFilter(String.class); - - if (!alreadyFetching && ((searchView != null && searchView.isIconified() && canFetchFurther) - || (!TextUtils.isEmpty(query) && canFetchSearchFurther))) { - fetchData(false); - } else { - adapter.onLoadMoreComplete(null); + for (int i = 0; i < adapter.getItemCount(); i++) { + if (adapter.getItem(i) instanceof UserItem) { + UserItem userItem = (UserItem) adapter.getItem(i); + if ("groups".equals(userItem.getModel().getSource())) { + userItem.setEnabled(!isPublicCall); } + } } + + checkAndHandleDoneMenuItem(); + adapter.notifyDataSetChanged(); + } + + private void toggleNewCallHeaderVisibility(boolean showInitialLayout) { + if (showInitialLayout) { + initialRelativeLayout.setVisibility(View.VISIBLE); + secondaryRelativeLayout.setVisibility(View.GONE); + } else { + initialRelativeLayout.setVisibility(View.GONE); + secondaryRelativeLayout.setVisibility(View.VISIBLE); + } + } + + @Override + public void noMoreLoad(int newItemsSize) { + } + + @Override + public void onLoadMore(int lastPosition, int currentPage) { + String query = (String) adapter.getFilter(String.class); + + if (!alreadyFetching && ((searchView != null && searchView.isIconified() && canFetchFurther) + || (!TextUtils.isEmpty(query) && canFetchSearchFurther))) { + fetchData(false); + } else { + adapter.onLoadMoreComplete(null); + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index afc1a8f0e..3b3ea5912 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -20,7 +20,6 @@ package com.nextcloud.talk.controllers -import android.content.res.Configuration import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Bundle @@ -48,12 +47,6 @@ import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.view.SimpleDraweeView -import com.nextcloud.talk.models.json.conversations.Conversation -import com.nextcloud.talk.models.json.conversations.RoomOverall -import com.nextcloud.talk.models.json.converters.EnumNotificationLevelConverter -import com.nextcloud.talk.models.json.generic.GenericOverall -import com.nextcloud.talk.models.json.participants.Participant -import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.R import com.nextcloud.talk.adapters.items.UserItem import com.nextcloud.talk.api.NcApi @@ -65,6 +58,12 @@ import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.jobs.DeleteConversationWorker import com.nextcloud.talk.jobs.LeaveConversationWorker import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.converters.EnumNotificationLevelConverter +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.Participant +import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DisplayUtils @@ -92,587 +91,680 @@ import java.util.Calendar import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class ConversationInfoController(args: Bundle) : BaseController(), FlexibleAdapter.OnItemClickListener { - @BindView(R.id.notification_settings) - lateinit var notificationsPreferenceScreen: MaterialPreferenceScreen - @BindView(R.id.progressBar) - lateinit var progressBar: ProgressBar - @BindView(R.id.conversation_info_message_notifications) - lateinit var messageNotificationLevel: MaterialChoicePreference - @BindView(R.id.webinar_settings) - lateinit var conversationInfoWebinar: MaterialPreferenceScreen - @BindView(R.id.conversation_info_lobby) - lateinit var conversationInfoLobby: MaterialSwitchPreference - @BindView(R.id.conversation_info_name) - lateinit var nameCategoryView: MaterialPreferenceCategory - @BindView(R.id.start_time_preferences) - lateinit var startTimeView: MaterialStandardPreference - @BindView(R.id.avatar_image) - lateinit var conversationAvatarImageView: SimpleDraweeView - @BindView(R.id.display_name_text) - lateinit var conversationDisplayName: EmojiTextView - @BindView(R.id.participants_list_category) - lateinit var participantsListCategory: MaterialPreferenceCategoryWithRightLink - @BindView(R.id.recyclerView) - lateinit var recyclerView: RecyclerView - @BindView(R.id.deleteConversationAction) - lateinit var deleteConversationAction: MaterialStandardPreference - @BindView(R.id.leaveConversationAction) - lateinit var leaveConversationAction: MaterialStandardPreference - @BindView(R.id.ownOptions) - lateinit var ownOptionsCategory: MaterialPreferenceCategory - @BindView(R.id.muteCalls) - lateinit var muteCalls: MaterialSwitchPreference - @BindView(R.id.mpc_action) - lateinit var actionTextView: TextView +class ConversationInfoController(args: Bundle) : BaseController(), + FlexibleAdapter.OnItemClickListener { + @BindView(R.id.notification_settings) + lateinit var notificationsPreferenceScreen: MaterialPreferenceScreen + @BindView(R.id.progressBar) + lateinit var progressBar: ProgressBar + @BindView(R.id.conversation_info_message_notifications) + lateinit var messageNotificationLevel: MaterialChoicePreference + @BindView(R.id.webinar_settings) + lateinit var conversationInfoWebinar: MaterialPreferenceScreen + @BindView(R.id.conversation_info_lobby) + lateinit var conversationInfoLobby: MaterialSwitchPreference + @BindView(R.id.conversation_info_name) + lateinit var nameCategoryView: MaterialPreferenceCategory + @BindView(R.id.start_time_preferences) + lateinit var startTimeView: MaterialStandardPreference + @BindView(R.id.avatar_image) + lateinit var conversationAvatarImageView: SimpleDraweeView + @BindView(R.id.display_name_text) + lateinit var conversationDisplayName: EmojiTextView + @BindView(R.id.participants_list_category) + lateinit var participantsListCategory: MaterialPreferenceCategoryWithRightLink + @BindView(R.id.recyclerView) + lateinit var recyclerView: RecyclerView + @BindView(R.id.deleteConversationAction) + lateinit var deleteConversationAction: MaterialStandardPreference + @BindView(R.id.leaveConversationAction) + lateinit var leaveConversationAction: MaterialStandardPreference + @BindView(R.id.ownOptions) + lateinit var ownOptionsCategory: MaterialPreferenceCategory + @BindView(R.id.muteCalls) + lateinit var muteCalls: MaterialSwitchPreference + @BindView(R.id.mpc_action) + lateinit var actionTextView: TextView - @set:Inject - lateinit var ncApi: NcApi + @set:Inject + lateinit var ncApi: NcApi - private val conversationToken: String? - private val conversationUser: UserEntity? - private val credentials: String? - private var roomDisposable: Disposable? = null - private var participantsDisposable: Disposable? = null + private val conversationToken: String? + private val conversationUser: UserEntity? + private val credentials: String? + private var roomDisposable: Disposable? = null + private var participantsDisposable: Disposable? = null - private var databaseStorageModule: DatabaseStorageModule? = null - private var conversation: Conversation? = null + private var databaseStorageModule: DatabaseStorageModule? = null + private var conversation: Conversation? = null - private var adapter: FlexibleAdapter>? = null - private var recyclerViewItems: MutableList> = ArrayList() + private var adapter: FlexibleAdapter>? = null + private var recyclerViewItems: MutableList> = ArrayList() - private var saveStateHandler: LovelySaveStateHandler? = null + private var saveStateHandler: LovelySaveStateHandler? = null - private val workerData: Data? - get() { - if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) { - val data = Data.Builder() - data.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken) - data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id) - return data.build() - } + private val workerData: Data? + get() { + if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) { + val data = Data.Builder() + data.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken) + data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id) + return data.build() + } - return null - } - - init { - setHasOptionsMenu(true) - NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this) - conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY) - conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN) - credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token) + return null } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> { - router.popCurrentController() - return true - } - else -> return super.onOptionsItemSelected(item) - } - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.controller_conversation_info, container, false) - } - - override fun onDetach(view: View) { - eventBus.unregister(this) - super.onDetach(view) - } - - override fun onAttach(view: View) { - super.onAttach(view) - eventBus.register(this) - - if (databaseStorageModule == null) { - databaseStorageModule = DatabaseStorageModule(conversationUser!!, conversationToken) - } - - notificationsPreferenceScreen.setStorageModule(databaseStorageModule) - conversationInfoWebinar.setStorageModule(databaseStorageModule) - - fetchRoomInfo() - } - - override fun onViewBound(view: View) { - super.onViewBound(view) - - if (saveStateHandler == null) { - saveStateHandler = LovelySaveStateHandler() - } - - actionTextView.visibility = View.GONE - } - - private fun setupWebinaryView() { - if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") && (conversation!!.type - == Conversation.ConversationType.ROOM_GROUP_CALL || conversation!!.type == - Conversation.ConversationType.ROOM_PUBLIC_CALL) && conversation!!.canModerate(conversationUser)) { - conversationInfoWebinar.visibility = View.VISIBLE - - val isLobbyOpenToModeratorsOnly = conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY - (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat) - .isChecked = isLobbyOpenToModeratorsOnly - - reconfigureLobbyTimerView() - - startTimeView.setOnClickListener { - MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { - val currentTimeCalendar = Calendar.getInstance() - if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != 0L) { - currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer * 1000 - } - - dateTimePicker(minDateTime = Calendar.getInstance(), requireFutureDateTime = - true, currentDateTime = currentTimeCalendar, dateTimeCallback = { _, - dateTime -> - reconfigureLobbyTimerView(dateTime) - submitLobbyChanges() - }) - } - } - - (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).setOnCheckedChangeListener { _, _ -> - reconfigureLobbyTimerView() - submitLobbyChanges() - } - } else { - conversationInfoWebinar.visibility = View.GONE - } - } - - fun reconfigureLobbyTimerView(dateTime: Calendar? = null) { - val isChecked = (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).isChecked - - if (dateTime != null && isChecked) { - conversation!!.lobbyTimer = (dateTime.timeInMillis - (dateTime.time.seconds * 1000)) / 1000 - } else if (!isChecked) { - conversation!!.lobbyTimer = 0 - } - - conversation!!.lobbyState = if (isChecked) Conversation.LobbyState - .LOBBY_STATE_MODERATORS_ONLY else Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS - - if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != java.lang.Long.MIN_VALUE && conversation!!.lobbyTimer != 0L) { - startTimeView.setSummary(DateUtils.getLocalDateStringFromTimestampForLobby(conversation!!.lobbyTimer)) - } else { - startTimeView.setSummary(R.string.nc_manual) - } - - if (isChecked) { - startTimeView.visibility = View.VISIBLE - } else { - startTimeView.visibility = View.GONE - } - } - - fun submitLobbyChanges() { - val state = if ((conversationInfoLobby.findViewById(R.id - .mp_checkable) as SwitchCompat).isChecked) 1 else 0 - ncApi.setLobbyForConversation(ApiUtils.getCredentials(conversationUser!!.username, - conversationUser.token), ApiUtils.getUrlForLobbyForConversation - (conversationUser.baseUrl, conversation!!.token), state, conversation!!.lobbyTimer) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onComplete() { - } - - override fun onSubscribe(d: Disposable) { - } - - override fun onNext(t: GenericOverall) { - } - - override fun onError(e: Throwable) { - } - - }) - } - - private fun showLovelyDialog(dialogId: Int, savedInstanceState: Bundle) { - when (dialogId) { - ID_DELETE_CONVERSATION_DIALOG -> showDeleteConversationDialog(savedInstanceState) - else -> { - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(eventStatus: EventStatus) { - getListOfParticipants() - } - - private fun showDeleteConversationDialog(savedInstanceState: Bundle?) { - if (activity != null) { - LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL) - .setTopColorRes(R.color.nc_darkRed) - .setIcon(DisplayUtils.getTintedDrawable( - context.resources, - R.drawable.ic_delete_black_24dp, R.color.bg_default)) - .setPositiveButtonColor(context.resources.getColor(R.color.nc_darkRed)) - .setTitle(R.string.nc_delete_call) - .setMessage(conversation!!.deleteWarningMessage) - .setPositiveButton(R.string.nc_delete) { deleteConversation() } - .setNegativeButton(R.string.nc_cancel, null) - .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler!!) - .setSavedInstanceState(savedInstanceState) - .show() - } - } - - override fun onSaveViewState(view: View, outState: Bundle) { - saveStateHandler!!.saveInstanceState(outState) - super.onSaveViewState(view, outState) - } - - override fun onRestoreViewState(view: View, savedViewState: Bundle) { - super.onRestoreViewState(view, savedViewState) - if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { - //Dialog won't be restarted automatically, so we need to call this method. - //Each dialog knows how to restore its viewState - showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState) - } - } - - private fun setupAdapter() { - if (activity != null) { - if (adapter == null) { - adapter = FlexibleAdapter(recyclerViewItems, activity, true) - } - - val layoutManager = SmoothScrollLinearLayoutManager(activity) - recyclerView.layoutManager = layoutManager - recyclerView.setHasFixedSize(true) - recyclerView.adapter = adapter - - adapter!!.addListener(this) - actionTextView.setOnClickListener { - val bundle = Bundle() - val existingParticipantsId = arrayListOf() - - recyclerViewItems.forEach { - val userItem = it as UserItem - existingParticipantsId.add(userItem.model.userId) - } - - bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true) - bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId) - bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token) - - router.pushController((RouterTransaction.with(ContactsController(bundle)) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()))) - - } - } - } - - private fun handleParticipants(participants: List) { - var userItem: UserItem - var participant: Participant - - recyclerViewItems = ArrayList() - var ownUserItem: UserItem? = null - - for (i in participants.indices) { - participant = participants[i] - userItem = UserItem(participant, conversationUser, null, activity) - userItem.isOnline = !participant.sessionId.equals("0") - if (!TextUtils.isEmpty(participant.userId) && participant.userId == conversationUser!!.userId) { - ownUserItem = userItem - ownUserItem.model.sessionId = "-1" - ownUserItem.isOnline = true - } else { - recyclerViewItems.add(userItem) - } - } - - - if (ownUserItem != null) { - recyclerViewItems.add(0, ownUserItem) - } - - setupAdapter() - - participantsListCategory.visibility = View.VISIBLE - adapter!!.updateDataSet(recyclerViewItems) - } - - override fun getTitle(): String? { - return resources!!.getString(R.string.nc_conversation_menu_conversation_info) - } - - private fun getListOfParticipants() { - ncApi.getPeersForCall(credentials, ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - participantsDisposable = d - } - - override fun onNext(participantsOverall: ParticipantsOverall) { - handleParticipants(participantsOverall.ocs.data) - } - - override fun onError(e: Throwable) { - - } - - override fun onComplete() { - participantsDisposable!!.dispose() - } - }) - - } - - @OnClick(R.id.leaveConversationAction) - internal fun leaveConversation() { - workerData?.let { - WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder - (LeaveConversationWorker::class - .java).setInputData(it).build() - ) - popTwoLastControllers() - } - } - - private fun deleteConversation() { - workerData?.let { - WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder - (DeleteConversationWorker::class.java).setInputData(it).build()) - popTwoLastControllers() - } - } - - @OnClick(R.id.deleteConversationAction) - internal fun deleteConversationClick() { - showDeleteConversationDialog(null) - } - - private fun popTwoLastControllers() { - var backstack = router.backstack - backstack = backstack.subList(0, backstack.size - 2) - router.setBackstack(backstack, HorizontalChangeHandler()) - } - - private fun fetchRoomInfo() { - ncApi.getRoom(credentials, ApiUtils.getRoom(conversationUser!!.baseUrl, conversationToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - roomDisposable = d - } - - override fun onNext(roomOverall: RoomOverall) { - conversation = roomOverall.ocs.data - - val conversationCopy = conversation - - if (conversationCopy!!.canModerate(conversationUser)) { - actionTextView.visibility = View.VISIBLE - } else { - actionTextView.visibility = View.GONE - } - - if (isAttached && (!isBeingDestroyed || !isDestroyed)) { - ownOptionsCategory.visibility = View.VISIBLE - - setupWebinaryView() - - if (!conversation!!.canLeave(conversationUser)) { - leaveConversationAction.visibility = View.GONE - } else { - leaveConversationAction.visibility = View.VISIBLE - } - - if (!conversation!!.canModerate(conversationUser)) { - deleteConversationAction.visibility = View.GONE - } else { - deleteConversationAction.visibility = View.VISIBLE - } - - if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { - muteCalls.visibility = View.GONE - } - - getListOfParticipants() - - progressBar.visibility = View.GONE - - nameCategoryView.visibility = View.VISIBLE - - conversationDisplayName.text = conversation!!.displayName - - - loadConversationAvatar() - adjustNotificationLevelUI() - - notificationsPreferenceScreen.visibility = View.VISIBLE - } - } - - override fun onError(e: Throwable) { - - } - - override fun onComplete() { - roomDisposable!!.dispose() - } - }) - } - - private fun adjustNotificationLevelUI() { - if (conversation != null) { - if (conversationUser != null && conversationUser.hasSpreedFeatureCapability("notification-levels")) { - messageNotificationLevel.isEnabled = true - messageNotificationLevel.alpha = 1.0f - - if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) { - val stringValue: String = when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) { - 1 -> "always" - 2 -> "mention" - 3 -> "never" - else -> "mention" - } - - messageNotificationLevel.value = stringValue - } else { - setProperNotificationValue(conversation) - } - } else { - messageNotificationLevel.isEnabled = false - messageNotificationLevel.alpha = 0.38f - setProperNotificationValue(conversation) - } - } - } - - private fun setProperNotificationValue(conversation: Conversation?) { - if (conversation!!.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { - // hack to see if we get mentioned always or just on mention - if (conversationUser!!.hasSpreedFeatureCapability("mention-flag")) { - messageNotificationLevel.value = "always" - } else { - messageNotificationLevel.value = "mention" - } - } else { - messageNotificationLevel.value = "mention" - } - } - - private fun loadConversationAvatar() { - when (conversation!!.type) { - Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty - (conversation!!.name)) { - val draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(conversationAvatarImageView.controller) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(conversationUser!!.baseUrl, - conversation!!.name, R.dimen.avatar_size_big), null)) - .build() - conversationAvatarImageView.controller = draweeController - } - Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils - .getRoundedBitmapDrawableFromVectorDrawableResource(resources, - R.drawable.ic_people_group_white_24px)) - Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils - .getRoundedBitmapDrawableFromVectorDrawableResource(resources, - R.drawable.ic_link_white_24px)) - Conversation.ConversationType.ROOM_SYSTEM -> { - val layers = arrayOfNulls(2) - layers[0] = context.getDrawable(R.drawable.ic_launcher_background) - layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground) - val layerDrawable = LayerDrawable(layers) - conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)) - } - - else -> { - } - } - } - - override fun onItemClick(view: View?, position: Int): Boolean { - val userItem = adapter?.getItem(position) as UserItem - val participant = userItem.model - - - if (participant.userId != conversationUser!!.userId) { - var items = mutableListOf( - BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote)), - BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote)), - BasicListItemWithImage(R.drawable.ic_delete_grey600_24dp, - context.getString(R.string.nc_remove_participant)) - ) - - if (!conversation!!.canModerate(conversationUser)) { - items = mutableListOf() - } else { - if (participant.type == Participant.ParticipantType.MODERATOR || participant.type == Participant.ParticipantType.OWNER) { - items.removeAt(0) - } else if (participant.type == Participant.ParticipantType.USER) { - items.removeAt(1) - } - } - - - if (items.isNotEmpty()) { - MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { - cornerRadius(res = R.dimen.corner_radius) - - title(text = participant.displayName) - listItemsWithImage(items = items) { dialog, index, _ -> - - if (index == 0) { - if (participant.type == Participant.ParticipantType.MODERATOR) { - ncApi.demoteModeratorToUser(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - getListOfParticipants() - } - } else if (participant.type == Participant.ParticipantType.USER) { - ncApi.promoteUserToModerator(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - getListOfParticipants() - } - } - } else if (index == 1) { - if (participant.type == Participant.ParticipantType.GUEST || - participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK) { - ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, true), participant.sessionId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - getListOfParticipants() - } - - } else { - ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, false), participant.userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - getListOfParticipants() - // get participants again - } - } - } - } - } - } - } - + init { + setHasOptionsMenu(true) + NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this) + conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY) + conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN) + credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + router.popCurrentController() return true + } + else -> return super.onOptionsItemSelected(item) + } + } + + override fun inflateView( + inflater: LayoutInflater, + container: ViewGroup + ): View { + return inflater.inflate(R.layout.controller_conversation_info, container, false) + } + + override fun onDetach(view: View) { + eventBus.unregister(this) + super.onDetach(view) + } + + override fun onAttach(view: View) { + super.onAttach(view) + eventBus.register(this) + + if (databaseStorageModule == null) { + databaseStorageModule = DatabaseStorageModule(conversationUser!!, conversationToken) } - companion object { + notificationsPreferenceScreen.setStorageModule(databaseStorageModule) + conversationInfoWebinar.setStorageModule(databaseStorageModule) - private const val ID_DELETE_CONVERSATION_DIALOG = 0 + fetchRoomInfo() + } + + override fun onViewBound(view: View) { + super.onViewBound(view) + + if (saveStateHandler == null) { + saveStateHandler = LovelySaveStateHandler() } + + actionTextView.visibility = View.GONE + } + + private fun setupWebinaryView() { + if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") && (conversation!!.type + == Conversation.ConversationType.ROOM_GROUP_CALL || conversation!!.type == + Conversation.ConversationType.ROOM_PUBLIC_CALL) && conversation!!.canModerate( + conversationUser + ) + ) { + conversationInfoWebinar.visibility = View.VISIBLE + + val isLobbyOpenToModeratorsOnly = + conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY + (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat) + .isChecked = isLobbyOpenToModeratorsOnly + + reconfigureLobbyTimerView() + + startTimeView.setOnClickListener { + MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { + val currentTimeCalendar = Calendar.getInstance() + if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != 0L) { + currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer * 1000 + } + + dateTimePicker(minDateTime = Calendar.getInstance(), requireFutureDateTime = + true, currentDateTime = currentTimeCalendar, dateTimeCallback = { _, + dateTime -> + reconfigureLobbyTimerView(dateTime) + submitLobbyChanges() + }) + } + } + + (conversationInfoLobby.findViewById( + R.id.mp_checkable + ) as SwitchCompat).setOnCheckedChangeListener { _, _ -> + reconfigureLobbyTimerView() + submitLobbyChanges() + } + } else { + conversationInfoWebinar.visibility = View.GONE + } + } + + fun reconfigureLobbyTimerView(dateTime: Calendar? = null) { + val isChecked = + (conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat).isChecked + + if (dateTime != null && isChecked) { + conversation!!.lobbyTimer = (dateTime.timeInMillis - (dateTime.time.seconds * 1000)) / 1000 + } else if (!isChecked) { + conversation!!.lobbyTimer = 0 + } + + conversation!!.lobbyState = if (isChecked) Conversation.LobbyState + .LOBBY_STATE_MODERATORS_ONLY else Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS + + if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != java.lang.Long.MIN_VALUE && conversation!!.lobbyTimer != 0L) { + startTimeView.setSummary( + DateUtils.getLocalDateStringFromTimestampForLobby(conversation!!.lobbyTimer) + ) + } else { + startTimeView.setSummary(R.string.nc_manual) + } + + if (isChecked) { + startTimeView.visibility = View.VISIBLE + } else { + startTimeView.visibility = View.GONE + } + } + + fun submitLobbyChanges() { + val state = if ((conversationInfoLobby.findViewById( + R.id + .mp_checkable + ) as SwitchCompat).isChecked + ) 1 else 0 + ncApi.setLobbyForConversation( + ApiUtils.getCredentials( + conversationUser!!.username, + conversationUser.token + ), ApiUtils.getUrlForLobbyForConversation + (conversationUser.baseUrl, conversation!!.token), state, conversation!!.lobbyTimer + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onComplete() { + } + + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(t: GenericOverall) { + } + + override fun onError(e: Throwable) { + } + + }) + } + + private fun showLovelyDialog( + dialogId: Int, + savedInstanceState: Bundle + ) { + when (dialogId) { + ID_DELETE_CONVERSATION_DIALOG -> showDeleteConversationDialog(savedInstanceState) + else -> { + } + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onMessageEvent(eventStatus: EventStatus) { + getListOfParticipants() + } + + private fun showDeleteConversationDialog(savedInstanceState: Bundle?) { + if (activity != null) { + LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL) + .setTopColorRes(R.color.nc_darkRed) + .setIcon( + DisplayUtils.getTintedDrawable( + context.resources, + R.drawable.ic_delete_black_24dp, R.color.bg_default + ) + ) + .setPositiveButtonColor(context.resources.getColor(R.color.nc_darkRed)) + .setTitle(R.string.nc_delete_call) + .setMessage(conversation!!.deleteWarningMessage) + .setPositiveButton(R.string.nc_delete) { deleteConversation() } + .setNegativeButton(R.string.nc_cancel, null) + .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler!!) + .setSavedInstanceState(savedInstanceState) + .show() + } + } + + override fun onSaveViewState( + view: View, + outState: Bundle + ) { + saveStateHandler!!.saveInstanceState(outState) + super.onSaveViewState(view, outState) + } + + override fun onRestoreViewState( + view: View, + savedViewState: Bundle + ) { + super.onRestoreViewState(view, savedViewState) + if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { + //Dialog won't be restarted automatically, so we need to call this method. + //Each dialog knows how to restore its viewState + showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState) + } + } + + private fun setupAdapter() { + if (activity != null) { + if (adapter == null) { + adapter = FlexibleAdapter(recyclerViewItems, activity, true) + } + + val layoutManager = SmoothScrollLinearLayoutManager(activity) + recyclerView.layoutManager = layoutManager + recyclerView.setHasFixedSize(true) + recyclerView.adapter = adapter + + adapter!!.addListener(this) + actionTextView.setOnClickListener { + val bundle = Bundle() + val existingParticipantsId = arrayListOf() + + recyclerViewItems.forEach { + val userItem = it as UserItem + existingParticipantsId.add(userItem.model.userId) + } + + bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true) + bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId) + bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token) + + router.pushController( + (RouterTransaction.with(ContactsController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + ) + + } + } + } + + private fun handleParticipants(participants: List) { + var userItem: UserItem + var participant: Participant + + recyclerViewItems = ArrayList() + var ownUserItem: UserItem? = null + + for (i in participants.indices) { + participant = participants[i] + userItem = UserItem(participant, conversationUser, null, activity) + userItem.isOnline = !participant.sessionId.equals("0") + if (!TextUtils.isEmpty( + participant.userId + ) && participant.userId == conversationUser!!.userId + ) { + ownUserItem = userItem + ownUserItem.model.sessionId = "-1" + ownUserItem.isOnline = true + } else { + recyclerViewItems.add(userItem) + } + } + + + if (ownUserItem != null) { + recyclerViewItems.add(0, ownUserItem) + } + + setupAdapter() + + participantsListCategory.visibility = View.VISIBLE + adapter!!.updateDataSet(recyclerViewItems) + } + + override fun getTitle(): String? { + return resources!!.getString(R.string.nc_conversation_menu_conversation_info) + } + + private fun getListOfParticipants() { + ncApi.getPeersForCall( + credentials, ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + participantsDisposable = d + } + + override fun onNext(participantsOverall: ParticipantsOverall) { + handleParticipants(participantsOverall.ocs.data) + } + + override fun onError(e: Throwable) { + + } + + override fun onComplete() { + participantsDisposable!!.dispose() + } + }) + + } + + @OnClick(R.id.leaveConversationAction) + internal fun leaveConversation() { + workerData?.let { + WorkManager.getInstance() + .enqueue( + OneTimeWorkRequest.Builder + ( + LeaveConversationWorker::class + .java + ).setInputData(it).build() + ) + popTwoLastControllers() + } + } + + private fun deleteConversation() { + workerData?.let { + WorkManager.getInstance() + .enqueue( + OneTimeWorkRequest.Builder + (DeleteConversationWorker::class.java).setInputData(it).build() + ) + popTwoLastControllers() + } + } + + @OnClick(R.id.deleteConversationAction) + internal fun deleteConversationClick() { + showDeleteConversationDialog(null) + } + + private fun popTwoLastControllers() { + var backstack = router.backstack + backstack = backstack.subList(0, backstack.size - 2) + router.setBackstack(backstack, HorizontalChangeHandler()) + } + + private fun fetchRoomInfo() { + ncApi.getRoom(credentials, ApiUtils.getRoom(conversationUser!!.baseUrl, conversationToken)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + roomDisposable = d + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.ocs.data + + val conversationCopy = conversation + + if (conversationCopy!!.canModerate(conversationUser)) { + actionTextView.visibility = View.VISIBLE + } else { + actionTextView.visibility = View.GONE + } + + if (isAttached && (!isBeingDestroyed || !isDestroyed)) { + ownOptionsCategory.visibility = View.VISIBLE + + setupWebinaryView() + + if (!conversation!!.canLeave(conversationUser)) { + leaveConversationAction.visibility = View.GONE + } else { + leaveConversationAction.visibility = View.VISIBLE + } + + if (!conversation!!.canModerate(conversationUser)) { + deleteConversationAction.visibility = View.GONE + } else { + deleteConversationAction.visibility = View.VISIBLE + } + + if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { + muteCalls.visibility = View.GONE + } + + getListOfParticipants() + + progressBar.visibility = View.GONE + + nameCategoryView.visibility = View.VISIBLE + + conversationDisplayName.text = conversation!!.displayName + + + loadConversationAvatar() + adjustNotificationLevelUI() + + notificationsPreferenceScreen.visibility = View.VISIBLE + } + } + + override fun onError(e: Throwable) { + + } + + override fun onComplete() { + roomDisposable!!.dispose() + } + }) + } + + private fun adjustNotificationLevelUI() { + if (conversation != null) { + if (conversationUser != null && conversationUser.hasSpreedFeatureCapability( + "notification-levels" + ) + ) { + messageNotificationLevel.isEnabled = true + messageNotificationLevel.alpha = 1.0f + + if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) { + val stringValue: String = + when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) { + 1 -> "always" + 2 -> "mention" + 3 -> "never" + else -> "mention" + } + + messageNotificationLevel.value = stringValue + } else { + setProperNotificationValue(conversation) + } + } else { + messageNotificationLevel.isEnabled = false + messageNotificationLevel.alpha = 0.38f + setProperNotificationValue(conversation) + } + } + } + + private fun setProperNotificationValue(conversation: Conversation?) { + if (conversation!!.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { + // hack to see if we get mentioned always or just on mention + if (conversationUser!!.hasSpreedFeatureCapability("mention-flag")) { + messageNotificationLevel.value = "always" + } else { + messageNotificationLevel.value = "mention" + } + } else { + messageNotificationLevel.value = "mention" + } + } + + private fun loadConversationAvatar() { + when (conversation!!.type) { + Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty + (conversation!!.name) + ) { + val draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(conversationAvatarImageView.controller) + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName( + conversationUser!!.baseUrl, + conversation!!.name, R.dimen.avatar_size_big + ), null + ) + ) + .build() + conversationAvatarImageView.controller = draweeController + } + Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage( + DisplayUtils + .getRoundedBitmapDrawableFromVectorDrawableResource( + resources, + R.drawable.ic_people_group_white_24px + ) + ) + Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage( + DisplayUtils + .getRoundedBitmapDrawableFromVectorDrawableResource( + resources, + R.drawable.ic_link_white_24px + ) + ) + Conversation.ConversationType.ROOM_SYSTEM -> { + val layers = arrayOfNulls(2) + layers[0] = context.getDrawable(R.drawable.ic_launcher_background) + layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground) + val layerDrawable = LayerDrawable(layers) + conversationAvatarImageView.hierarchy.setPlaceholderImage( + DisplayUtils.getRoundedDrawable(layerDrawable) + ) + } + + else -> { + } + } + } + + override fun onItemClick( + view: View?, + position: Int + ): Boolean { + val userItem = adapter?.getItem(position) as UserItem + val participant = userItem.model + + + if (participant.userId != conversationUser!!.userId) { + var items = mutableListOf( + BasicListItemWithImage( + R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote) + ), + BasicListItemWithImage( + R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote) + ), + BasicListItemWithImage( + R.drawable.ic_delete_grey600_24dp, + context.getString(R.string.nc_remove_participant) + ) + ) + + if (!conversation!!.canModerate(conversationUser)) { + items = mutableListOf() + } else { + if (participant.type == Participant.ParticipantType.MODERATOR || participant.type == Participant.ParticipantType.OWNER) { + items.removeAt(0) + } else if (participant.type == Participant.ParticipantType.USER) { + items.removeAt(1) + } + } + + + if (items.isNotEmpty()) { + MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { + cornerRadius(res = R.dimen.corner_radius) + + title(text = participant.displayName) + listItemsWithImage(items = items) { dialog, index, _ -> + + if (index == 0) { + if (participant.type == Participant.ParticipantType.MODERATOR) { + ncApi.demoteModeratorToUser( + credentials, + ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), + participant.userId + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + getListOfParticipants() + } + } else if (participant.type == Participant.ParticipantType.USER) { + ncApi.promoteUserToModerator( + credentials, + ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), + participant.userId + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + getListOfParticipants() + } + } + } else if (index == 1) { + if (participant.type == Participant.ParticipantType.GUEST || + participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK + ) { + ncApi.removeParticipantFromConversation( + credentials, ApiUtils.getUrlForRemovingParticipantFromConversation( + conversationUser.baseUrl, conversation!!.token, true + ), participant.sessionId + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + getListOfParticipants() + } + + } else { + ncApi.removeParticipantFromConversation( + credentials, ApiUtils.getUrlForRemovingParticipantFromConversation( + conversationUser.baseUrl, conversation!!.token, false + ), participant.userId + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + getListOfParticipants() + // get participants again + } + } + } + } + } + } + } + + return true + } + + companion object { + + private const val ID_DELETE_CONVERSATION_DIALOG = 0 + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index 9958492f9..e14619e65 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -36,8 +36,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; import androidx.annotation.NonNull; import androidx.appcompat.widget.SearchView; import androidx.core.graphics.drawable.RoundedBitmapDrawable; @@ -65,8 +63,6 @@ import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.request.ImageRequest; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.kennyc.bottomsheet.BottomSheet; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.R; import com.nextcloud.talk.activities.MagicCallActivity; import com.nextcloud.talk.adapters.items.CallItem; @@ -82,6 +78,8 @@ import com.nextcloud.talk.events.MoreMenuClickEvent; import com.nextcloud.talk.interfaces.ConversationMenuInterface; import com.nextcloud.talk.jobs.DeleteConversationWorker; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.conversations.Conversation; +import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ConductorRemapping; import com.nextcloud.talk.utils.DisplayUtils; @@ -111,207 +109,218 @@ import org.parceler.Parcels; import retrofit2.HttpException; @AutoInjector(NextcloudTalkApplication.class) -public class ConversationsListController extends BaseController implements SearchView.OnQueryTextListener, - FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, FastScroller - .OnScrollStateChangeListener, ConversationMenuInterface { +public class ConversationsListController extends BaseController + implements SearchView.OnQueryTextListener, + FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, FastScroller + .OnScrollStateChangeListener, ConversationMenuInterface { - public static final String TAG = "ConversationsListController"; - public static final int ID_DELETE_CONVERSATION_DIALOG = 0; - private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery"; - @Inject - UserUtils userUtils; + public static final String TAG = "ConversationsListController"; + public static final int ID_DELETE_CONVERSATION_DIALOG = 0; + private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery"; + @Inject + UserUtils userUtils; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - @Inject - Context context; + @Inject + Context context; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @BindView(R.id.recyclerView) - RecyclerView recyclerView; + @BindView(R.id.recyclerView) + RecyclerView recyclerView; - @BindView(R.id.swipeRefreshLayoutView) - SwipeRefreshLayout swipeRefreshLayout; + @BindView(R.id.swipeRefreshLayoutView) + SwipeRefreshLayout swipeRefreshLayout; - @BindView(R.id.fast_scroller) - FastScroller fastScroller; + @BindView(R.id.fast_scroller) + FastScroller fastScroller; - @BindView(R.id.floatingActionButton) - FloatingActionButton floatingActionButton; + @BindView(R.id.floatingActionButton) + FloatingActionButton floatingActionButton; - private UserEntity currentUser; - private FlexibleAdapter adapter; - private List callItems = new ArrayList<>(); + private UserEntity currentUser; + private FlexibleAdapter adapter; + private List callItems = new ArrayList<>(); - private BottomSheet bottomSheet; - private MenuItem searchItem; - private SearchView searchView; - private String searchQuery; + private BottomSheet bottomSheet; + private MenuItem searchItem; + private SearchView searchView; + private String searchQuery; - private View view; - private boolean shouldUseLastMessageLayout; + private View view; + private boolean shouldUseLastMessageLayout; - private String credentials; + private String credentials; - private boolean adapterWasNull = true; + private boolean adapterWasNull = true; - private boolean isRefreshing; + private boolean isRefreshing; - private LovelySaveStateHandler saveStateHandler; + private LovelySaveStateHandler saveStateHandler; - private Bundle conversationMenuBundle = null; + private Bundle conversationMenuBundle = null; - public ConversationsListController() { - super(); - setHasOptionsMenu(true); + public ConversationsListController() { + super(); + setHasOptionsMenu(true); + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_conversations_rv, container, false); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + if (getActionBar() != null) { + getActionBar().show(); } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_conversations_rv, container, false); + if (saveStateHandler == null) { + saveStateHandler = new LovelySaveStateHandler(); } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + if (adapter == null) { + adapter = new FlexibleAdapter<>(callItems, getActivity(), true); + } else { + //progressBarView.setVisibility(View.GONE); + } - if (getActionBar() != null) { - getActionBar().show(); + adapter.addListener(this); + prepareViews(); + } + + private void loadUserAvatar(MenuItem menuItem) { + if (getActivity() != null) { + int avatarSize = (int) DisplayUtils.convertDpToPixel(menuItem.getIcon().getIntrinsicHeight(), + getActivity()); + ImageRequest imageRequest = DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithNameAndPixels(currentUser.getBaseUrl(), + currentUser.getUserId(), avatarSize), null); + + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + DataSource> dataSource = + imagePipeline.fetchDecodedImage(imageRequest, null); + dataSource.subscribe(new BaseBitmapDataSubscriber() { + @Override + protected void onNewResultImpl(Bitmap bitmap) { + if (bitmap != null && getResources() != null) { + RoundedBitmapDrawable roundedBitmapDrawable = + RoundedBitmapDrawableFactory.create(getResources(), bitmap); + roundedBitmapDrawable.setCircular(true); + roundedBitmapDrawable.setAntiAlias(true); + menuItem.setIcon(roundedBitmapDrawable); + } } - if (saveStateHandler == null) { - saveStateHandler = new LovelySaveStateHandler(); + @Override + protected void onFailureImpl(DataSource> dataSource) { + menuItem.setIcon(R.drawable.ic_settings_white_24dp); } + }, UiThreadImmediateExecutorService.getInstance()); + } + } - if (adapter == null) { - adapter = new FlexibleAdapter<>(callItems, getActivity(), true); - } else { + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + currentUser = userUtils.getCurrentUser(); + + if (currentUser != null) { + credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); + shouldUseLastMessageLayout = currentUser.hasSpreedFeatureCapability("last-room-activity"); + fetchData(false); + } + } + + private void initSearchView() { + if (getActivity() != null) { + SearchManager searchManager = + (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); + if (searchItem != null) { + searchView = (SearchView) MenuItemCompat.getActionView(searchItem); + searchView.setMaxWidth(Integer.MAX_VALUE); + searchView.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER); + int imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && appPreferences.getIsKeyboardIncognito()) { + imeOptions |= EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; + } + searchView.setImeOptions(imeOptions); + searchView.setQueryHint(getResources().getString(R.string.nc_search)); + if (searchManager != null) { + searchView.setSearchableInfo( + searchManager.getSearchableInfo(getActivity().getComponentName())); + } + searchView.setOnQueryTextListener(this); + } + } + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case R.id.action_settings: + ArrayList names = new ArrayList<>(); + names.add("userAvatar.transitionTag"); + getRouter().pushController((RouterTransaction.with(new SettingsController()) + .pushChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), + new VerticalChangeHandler())) + .popChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), + new VerticalChangeHandler())))); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_conversation_plus_filter, menu); + searchItem = menu.findItem(R.id.action_search); + initSearchView(); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + searchItem.setVisible(callItems.size() > 0); + if (adapter.hasFilter()) { + searchItem.expandActionView(); + searchView.setQuery(adapter.getFilter(String.class), false); + } + + MenuItem menuItem = menu.findItem(R.id.action_settings); + loadUserAvatar(menuItem); + } + + private void fetchData(boolean fromBottomSheet) { + isRefreshing = true; + + callItems = new ArrayList<>(); + + ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(currentUser.getBaseUrl())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(roomsOverall -> { + + if (adapterWasNull) { + adapterWasNull = false; //progressBarView.setVisibility(View.GONE); - } - - adapter.addListener(this); - prepareViews(); - } - - private void loadUserAvatar(MenuItem menuItem) { - if (getActivity() != null) { - int avatarSize = (int) DisplayUtils.convertDpToPixel(menuItem.getIcon().getIntrinsicHeight(), getActivity()); - ImageRequest imageRequest = DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameAndPixels(currentUser.getBaseUrl(), - currentUser.getUserId(), avatarSize), null); - - ImagePipeline imagePipeline = Fresco.getImagePipeline(); - DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null); - dataSource.subscribe(new BaseBitmapDataSubscriber() { - @Override - protected void onNewResultImpl(Bitmap bitmap) { - if (bitmap != null && getResources() != null) { - RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(), bitmap); - roundedBitmapDrawable.setCircular(true); - roundedBitmapDrawable.setAntiAlias(true); - menuItem.setIcon(roundedBitmapDrawable); - } - } - - @Override - protected void onFailureImpl(DataSource> dataSource) { - menuItem.setIcon(R.drawable.ic_settings_white_24dp); - } - }, UiThreadImmediateExecutorService.getInstance()); - } - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - currentUser = userUtils.getCurrentUser(); - - if (currentUser != null) { - credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); - shouldUseLastMessageLayout = currentUser.hasSpreedFeatureCapability("last-room-activity"); - fetchData(false); - } - } - - - private void initSearchView() { - if (getActivity() != null) { - SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); - if (searchItem != null) { - searchView = (SearchView) MenuItemCompat.getActionView(searchItem); - searchView.setMaxWidth(Integer.MAX_VALUE); - searchView.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER); - int imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.getIsKeyboardIncognito()) { - imeOptions |= EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; - } - searchView.setImeOptions(imeOptions); - searchView.setQueryHint(getResources().getString(R.string.nc_search)); - if (searchManager != null) { - searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); - } - searchView.setOnQueryTextListener(this); - } - } - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_settings: - ArrayList names = new ArrayList<>(); - names.add("userAvatar.transitionTag"); - getRouter().pushController((RouterTransaction.with(new SettingsController()) - .pushChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), new VerticalChangeHandler())) - .popChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), new VerticalChangeHandler())))); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_conversation_plus_filter, menu); - searchItem = menu.findItem(R.id.action_search); - initSearchView(); - } - - @Override - public void onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - searchItem.setVisible(callItems.size() > 0); - if (adapter.hasFilter()) { - searchItem.expandActionView(); - searchView.setQuery(adapter.getFilter(String.class), false); - } - - MenuItem menuItem = menu.findItem(R.id.action_settings); - loadUserAvatar(menuItem); - } - - private void fetchData(boolean fromBottomSheet) { - isRefreshing = true; - - callItems = new ArrayList<>(); - - ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(currentUser.getBaseUrl())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(roomsOverall -> { - - if (adapterWasNull) { - adapterWasNull = false; - //progressBarView.setVisibility(View.GONE); - } + } /*if (roomsOverall.getOcs().getData().size() > 0) { if (emptyLayoutView.getVisibility() != View.GONE) { @@ -331,55 +340,54 @@ public class ConversationsListController extends BaseController implements Searc } }*/ - Conversation conversation; - for (int i = 0; i < roomsOverall.getOcs().getData().size(); i++) { - conversation = roomsOverall.getOcs().getData().get(i); - if (shouldUseLastMessageLayout) { - if (getActivity() != null) { - ConversationItem conversationItem = new ConversationItem(conversation - , currentUser, getActivity()); - callItems.add(conversationItem); - } - } else { - CallItem callItem = new CallItem(conversation, currentUser); - callItems.add(callItem); - } - } + Conversation conversation; + for (int i = 0; i < roomsOverall.getOcs().getData().size(); i++) { + conversation = roomsOverall.getOcs().getData().get(i); + if (shouldUseLastMessageLayout) { + if (getActivity() != null) { + ConversationItem conversationItem = new ConversationItem(conversation + , currentUser, getActivity()); + callItems.add(conversationItem); + } + } else { + CallItem callItem = new CallItem(conversation, currentUser); + callItems.add(callItem); + } + } - if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { - Collections.sort(callItems, (o1, o2) -> { - Conversation conversation1 = ((ConversationItem) o1).getModel(); - Conversation conversation2 = ((ConversationItem) o2).getModel(); - return new CompareToBuilder() - .append(conversation2.isFavorite(), conversation1.isFavorite()) - .append(conversation2.getLastActivity(), conversation1.getLastActivity()) - .toComparison(); - }); - } else { - Collections.sort(callItems, (callItem, t1) -> - Long.compare(((CallItem) t1).getModel().getLastPing(), - ((CallItem) callItem).getModel().getLastPing())); - } + if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { + Collections.sort(callItems, (o1, o2) -> { + Conversation conversation1 = ((ConversationItem) o1).getModel(); + Conversation conversation2 = ((ConversationItem) o2).getModel(); + return new CompareToBuilder() + .append(conversation2.isFavorite(), conversation1.isFavorite()) + .append(conversation2.getLastActivity(), conversation1.getLastActivity()) + .toComparison(); + }); + } else { + Collections.sort(callItems, (callItem, t1) -> + Long.compare(((CallItem) t1).getModel().getLastPing(), + ((CallItem) callItem).getModel().getLastPing())); + } - adapter.updateDataSet(callItems, false); + adapter.updateDataSet(callItems, false); - if (searchItem != null) { - searchItem.setVisible(callItems.size() > 0); - } + if (searchItem != null) { + searchItem.setVisible(callItems.size() > 0); + } - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } + }, throwable -> { + if (searchItem != null) { + searchItem.setVisible(false); + } - }, throwable -> { - if (searchItem != null) { - searchItem.setVisible(false); - } - - if (throwable instanceof HttpException) { - HttpException exception = (HttpException) throwable; - switch (exception.code()) { - case 401: + if (throwable instanceof HttpException) { + HttpException exception = (HttpException) throwable; + switch (exception.code()) { + case 401: /*if (getParentController() != null && getParentController().getRouter() != null) { getParentController().getRouter().pushController((RouterTransaction.with @@ -388,325 +396,333 @@ public class ConversationsListController extends BaseController implements Searc .pushChangeHandler(new VerticalChangeHandler()) .popChangeHandler(new VerticalChangeHandler()))); }*/ - break; - default: - break; - } - } - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } - }, () -> { - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } + break; + default: + break; + } + } + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } + }, () -> { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } - if (fromBottomSheet) { - new Handler().postDelayed(() -> { - bottomSheet.setCancelable(true); - if (bottomSheet.isShowing()) { - bottomSheet.cancel(); - } - }, 2500); - } + if (fromBottomSheet) { + new Handler().postDelayed(() -> { + bottomSheet.setCancelable(true); + if (bottomSheet.isShowing()) { + bottomSheet.cancel(); + } + }, 2500); + } - isRefreshing = false; - }); - - } - - private void prepareViews() { - SmoothScrollLinearLayoutManager layoutManager = - new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - - recyclerView.setAdapter(adapter); - - swipeRefreshLayout.setOnRefreshListener(() -> fetchData(false)); - swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); - - //emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen()); - floatingActionButton.setOnClickListener(v -> { - showNewConversationsScreen(); + isRefreshing = false; }); + } - fastScroller.addOnScrollStateChangeListener(this); - adapter.setFastScroller(fastScroller); + private void prepareViews() { + SmoothScrollLinearLayoutManager layoutManager = + new SmoothScrollLinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setHasFixedSize(true); - fastScroller.setBubbleTextCreator(position -> { - String displayName; - if (shouldUseLastMessageLayout) { - displayName = ((ConversationItem) adapter.getItem(position)).getModel().getDisplayName(); - } else { - displayName = ((CallItem) adapter.getItem(position)).getModel().getDisplayName(); - } + recyclerView.setAdapter(adapter); - if (displayName.length() > 8) { - displayName = displayName.substring(0, 4) + "..."; - } - return displayName; - }); + swipeRefreshLayout.setOnRefreshListener(() -> fetchData(false)); + swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); + + //emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen()); + floatingActionButton.setOnClickListener(v -> { + showNewConversationsScreen(); + }); + + fastScroller.addOnScrollStateChangeListener(this); + adapter.setFastScroller(fastScroller); + + fastScroller.setBubbleTextCreator(position -> { + String displayName; + if (shouldUseLastMessageLayout) { + displayName = ((ConversationItem) adapter.getItem(position)).getModel().getDisplayName(); + } else { + displayName = ((CallItem) adapter.getItem(position)).getModel().getDisplayName(); + } + + if (displayName.length() > 8) { + displayName = displayName.substring(0, 4) + "..."; + } + return displayName; + }); + } + + private void showNewConversationsScreen() { + Bundle bundle = new Bundle(); + bundle.putBoolean(BundleKeys.INSTANCE.getKEY_NEW_CONVERSATION(), true); + getRouter().pushController((RouterTransaction.with(new ContactsController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler()))); + } + + @Override + public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { + saveStateHandler.saveInstanceState(outState); + + if (searchView != null && !TextUtils.isEmpty(searchView.getQuery())) { + outState.putString(KEY_SEARCH_QUERY, searchView.getQuery().toString()); } - private void showNewConversationsScreen() { - Bundle bundle = new Bundle(); - bundle.putBoolean(BundleKeys.INSTANCE.getKEY_NEW_CONVERSATION(), true); - getRouter().pushController((RouterTransaction.with(new ContactsController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler()))); + super.onSaveViewState(view, outState); + } + + @Override + public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { + super.onRestoreViewState(view, savedViewState); + searchQuery = savedViewState.getString(KEY_SEARCH_QUERY, ""); + if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { + //Dialog won't be restarted automatically, so we need to call this method. + //Each dialog knows how to restore its viewState + showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); + } + } + + @Override + public boolean onQueryTextChange(String newText) { + if (adapter.hasNewFilter(newText) || !TextUtils.isEmpty(searchQuery)) { + + if (!TextUtils.isEmpty(searchQuery)) { + adapter.setFilter(searchQuery); + searchQuery = ""; + adapter.filterItems(); + } else { + adapter.setFilter(newText); + adapter.filterItems(300); + } } - @Override - public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { - saveStateHandler.saveInstanceState(outState); - - if (searchView != null && !TextUtils.isEmpty(searchView.getQuery())) { - outState.putString(KEY_SEARCH_QUERY, searchView.getQuery().toString()); - } - - super.onSaveViewState(view, outState); + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setEnabled(!adapter.hasFilter()); } - @Override - public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { - super.onRestoreViewState(view, savedViewState); - searchQuery = savedViewState.getString(KEY_SEARCH_QUERY, ""); - if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { - //Dialog won't be restarted automatically, so we need to call this method. - //Each dialog knows how to restore its viewState - showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); - } - } + return true; + } - @Override - public boolean onQueryTextChange(String newText) { - if (adapter.hasNewFilter(newText) || !TextUtils.isEmpty(searchQuery)) { + @Override + public boolean onQueryTextSubmit(String query) { + return onQueryTextChange(query); + } - if (!TextUtils.isEmpty(searchQuery)) { - adapter.setFilter(searchQuery); - searchQuery = ""; - adapter.filterItems(); - } else { - adapter.setFilter(newText); - adapter.filterItems(300); - } - } - - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setEnabled(!adapter.hasFilter()); - } - - return true; - } - - @Override - public boolean onQueryTextSubmit(String query) { - return onQueryTextChange(query); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(BottomSheetLockEvent bottomSheetLockEvent) { - if (bottomSheet != null) { - if (!bottomSheetLockEvent.isCancelable()) { - bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); - } else { - if (bottomSheetLockEvent.getDelay() != 0 && bottomSheetLockEvent.isShouldRefreshData()) { - fetchData(true); - } else { - bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); - if (bottomSheet.isShowing() && bottomSheetLockEvent.isCancel()) { - bottomSheet.cancel(); - } - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(MoreMenuClickEvent moreMenuClickEvent) { - Bundle bundle = new Bundle(); - Conversation conversation = moreMenuClickEvent.getConversation(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_MENU_TYPE(), Parcels.wrap(CallMenuController.MenuType.REGULAR)); - - prepareAndShowBottomSheetWithBundle(bundle, true); - } - - private void prepareAndShowBottomSheetWithBundle(Bundle bundle, boolean shouldShowCallMenuController) { - if (view == null) { - view = getActivity().getLayoutInflater().inflate(R.layout.bottom_sheet, null, false); - } - - if (shouldShowCallMenuController) { - getChildRouter((ViewGroup) view).setRoot( - RouterTransaction.with(new CallMenuController(bundle, this)) - .popChangeHandler(new VerticalChangeHandler()) - .pushChangeHandler(new VerticalChangeHandler())); + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(BottomSheetLockEvent bottomSheetLockEvent) { + if (bottomSheet != null) { + if (!bottomSheetLockEvent.isCancelable()) { + bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); + } else { + if (bottomSheetLockEvent.getDelay() != 0 && bottomSheetLockEvent.isShouldRefreshData()) { + fetchData(true); } else { - getChildRouter((ViewGroup) view).setRoot( - RouterTransaction.with(new EntryMenuController(bundle)) - .popChangeHandler(new VerticalChangeHandler()) - .pushChangeHandler(new VerticalChangeHandler())); + bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); + if (bottomSheet.isShowing() && bottomSheetLockEvent.isCancel()) { + bottomSheet.cancel(); + } + } + } + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(MoreMenuClickEvent moreMenuClickEvent) { + Bundle bundle = new Bundle(); + Conversation conversation = moreMenuClickEvent.getConversation(); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_MENU_TYPE(), + Parcels.wrap(CallMenuController.MenuType.REGULAR)); + + prepareAndShowBottomSheetWithBundle(bundle, true); + } + + private void prepareAndShowBottomSheetWithBundle(Bundle bundle, + boolean shouldShowCallMenuController) { + if (view == null) { + view = getActivity().getLayoutInflater().inflate(R.layout.bottom_sheet, null, false); + } + + if (shouldShowCallMenuController) { + getChildRouter((ViewGroup) view).setRoot( + RouterTransaction.with(new CallMenuController(bundle, this)) + .popChangeHandler(new VerticalChangeHandler()) + .pushChangeHandler(new VerticalChangeHandler())); + } else { + getChildRouter((ViewGroup) view).setRoot( + RouterTransaction.with(new EntryMenuController(bundle)) + .popChangeHandler(new VerticalChangeHandler()) + .pushChangeHandler(new VerticalChangeHandler())); + } + + if (bottomSheet == null) { + bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create(); + } + + bottomSheet.setOnShowListener( + dialog -> new KeyboardUtils(getActivity(), bottomSheet.getLayout(), true)); + bottomSheet.setOnDismissListener( + dialog -> getActionBar().setDisplayHomeAsUpEnabled(getRouter().getBackstackSize() > 1)); + bottomSheet.show(); + } + + @Override + public String getTitle() { + return getResources().getString(R.string.nc_app_name); + } + + @Override + public void onFastScrollerStateChange(boolean scrolling) { + swipeRefreshLayout.setEnabled(!scrolling); + } + + @Override + public boolean onItemClick(View view, int position) { + Object clickedItem = adapter.getItem(position); + if (clickedItem != null && getActivity() != null) { + Conversation conversation; + if (shouldUseLastMessageLayout) { + conversation = ((ConversationItem) clickedItem).getModel(); + } else { + conversation = ((CallItem) clickedItem).getModel(); + } + + Bundle bundle = new Bundle(); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); + + if (conversation.hasPassword && (conversation.getParticipantType() + .equals(Participant.ParticipantType.GUEST) || + conversation.getParticipantType() + .equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) { + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); + prepareAndShowBottomSheetWithBundle(bundle, false); + } else { + currentUser = userUtils.getCurrentUser(); + + if (currentUser.hasSpreedFeatureCapability("chat-v2")) { + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), + Parcels.wrap(conversation)); + ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(), + conversation.getToken(), bundle, false); + } else { + overridePushHandler(new NoOpControllerChangeHandler()); + overridePopHandler(new NoOpControllerChangeHandler()); + Intent callIntent = new Intent(getActivity(), MagicCallActivity.class); + callIntent.putExtras(bundle); + startActivity(callIntent); + } + } + } + + return true; + } + + @Override + public void onItemLongClick(int position) { + if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { + Object clickedItem = adapter.getItem(position); + if (clickedItem != null) { + Conversation conversation; + if (shouldUseLastMessageLayout) { + conversation = ((ConversationItem) clickedItem).getModel(); + } else { + conversation = ((CallItem) clickedItem).getModel(); } - if (bottomSheet == null) { - bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create(); - } - - bottomSheet.setOnShowListener(dialog -> new KeyboardUtils(getActivity(), bottomSheet.getLayout(), true)); - bottomSheet.setOnDismissListener(dialog -> getActionBar().setDisplayHomeAsUpEnabled(getRouter().getBackstackSize() > 1)); - bottomSheet.show(); + MoreMenuClickEvent moreMenuClickEvent = new MoreMenuClickEvent(conversation); + onMessageEvent(moreMenuClickEvent); + } } + } - - @Override - public String getTitle() { - return getResources().getString(R.string.nc_app_name); + @Subscribe(sticky = true, threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(EventStatus eventStatus) { + if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) { + switch (eventStatus.getEventType()) { + case CONVERSATION_UPDATE: + if (eventStatus.isAllGood() && !isRefreshing) { + fetchData(false); + } + break; + default: + break; + } } + } - @Override - public void onFastScrollerStateChange(boolean scrolling) { - swipeRefreshLayout.setEnabled(!scrolling); + private void showDeleteConversationDialog(Bundle savedInstanceState) { + if (getActivity() != null + && conversationMenuBundle != null + && currentUser != null + && conversationMenuBundle.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID()) + == currentUser.getId()) { + + Conversation conversation = + Parcels.unwrap(conversationMenuBundle.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); + + if (conversation != null) { + new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL) + .setTopColorRes(R.color.nc_darkRed) + .setIcon(DisplayUtils.getTintedDrawable(context.getResources(), + R.drawable.ic_delete_black_24dp, R.color.bg_default)) + .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed)) + .setTitle(R.string.nc_delete_call) + .setMessage(conversation.getDeleteWarningMessage()) + .setPositiveButton(R.string.nc_delete, new View.OnClickListener() { + @Override + public void onClick(View v) { + Data.Builder data = new Data.Builder(); + data.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), + conversationMenuBundle.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID())); + data.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); + conversationMenuBundle = null; + deleteConversation(data.build()); + } + }) + .setNegativeButton(R.string.nc_cancel, new View.OnClickListener() { + @Override + public void onClick(View v) { + conversationMenuBundle = null; + } + }) + .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler) + .setSavedInstanceState(savedInstanceState) + .show(); + } } + } - @Override - public boolean onItemClick(View view, int position) { - Object clickedItem = adapter.getItem(position); - if (clickedItem != null && getActivity() != null) { - Conversation conversation; - if (shouldUseLastMessageLayout) { - conversation = ((ConversationItem) clickedItem).getModel(); - } else { - conversation = ((CallItem) clickedItem).getModel(); - } + private void deleteConversation(Data data) { + OneTimeWorkRequest deleteConversationWorker = + new OneTimeWorkRequest.Builder(DeleteConversationWorker.class).setInputData(data).build(); + WorkManager.getInstance().enqueue(deleteConversationWorker); + } - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); - - if (conversation.hasPassword && (conversation.getParticipantType().equals(Participant.ParticipantType.GUEST) || - conversation.getParticipantType().equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) { - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); - prepareAndShowBottomSheetWithBundle(bundle, false); - } else { - currentUser = userUtils.getCurrentUser(); - - if (currentUser.hasSpreedFeatureCapability("chat-v2")) { - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation)); - ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(), - conversation.getToken(), bundle, false); - } else { - overridePushHandler(new NoOpControllerChangeHandler()); - overridePopHandler(new NoOpControllerChangeHandler()); - Intent callIntent = new Intent(getActivity(), MagicCallActivity.class); - callIntent.putExtras(bundle); - startActivity(callIntent); - } - } - } - - return true; + private void showLovelyDialog(int dialogId, Bundle savedInstanceState) { + switch (dialogId) { + case ID_DELETE_CONVERSATION_DIALOG: + showDeleteConversationDialog(savedInstanceState); + break; + default: + break; } + } - @Override - public void onItemLongClick(int position) { - if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { - Object clickedItem = adapter.getItem(position); - if (clickedItem != null) { - Conversation conversation; - if (shouldUseLastMessageLayout) { - conversation = ((ConversationItem) clickedItem).getModel(); - } else { - conversation = ((CallItem) clickedItem).getModel(); - } - - MoreMenuClickEvent moreMenuClickEvent = new MoreMenuClickEvent(conversation); - onMessageEvent(moreMenuClickEvent); - } - } - } - - @Subscribe(sticky = true, threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(EventStatus eventStatus) { - if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) { - switch (eventStatus.getEventType()) { - case CONVERSATION_UPDATE: - if (eventStatus.isAllGood() && !isRefreshing) { - fetchData(false); - } - break; - default: - break; - } - } - } - - private void showDeleteConversationDialog(Bundle savedInstanceState) { - if (getActivity() != null && conversationMenuBundle != null && currentUser != null && conversationMenuBundle.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID()) == currentUser.getId()) { - - Conversation conversation = - Parcels.unwrap(conversationMenuBundle.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - - if (conversation != null) { - new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL) - .setTopColorRes(R.color.nc_darkRed) - .setIcon(DisplayUtils.getTintedDrawable(context.getResources(), - R.drawable.ic_delete_black_24dp, R.color.bg_default)) - .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed)) - .setTitle(R.string.nc_delete_call) - .setMessage(conversation.getDeleteWarningMessage()) - .setPositiveButton(R.string.nc_delete, new View.OnClickListener() { - @Override - public void onClick(View v) { - Data.Builder data = new Data.Builder(); - data.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), - conversationMenuBundle.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID())); - data.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - conversationMenuBundle = null; - deleteConversation(data.build()); - } - }) - .setNegativeButton(R.string.nc_cancel, new View.OnClickListener() { - @Override - public void onClick(View v) { - conversationMenuBundle = null; - } - }) - .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler) - .setSavedInstanceState(savedInstanceState) - .show(); - } - } - } - - private void deleteConversation(Data data) { - OneTimeWorkRequest deleteConversationWorker = - new OneTimeWorkRequest.Builder(DeleteConversationWorker.class).setInputData(data).build(); - WorkManager.getInstance().enqueue(deleteConversationWorker); - } - - private void showLovelyDialog(int dialogId, Bundle savedInstanceState) { - switch (dialogId) { - case ID_DELETE_CONVERSATION_DIALOG: - showDeleteConversationDialog(savedInstanceState); - break; - default: - break; - } - } - - @Override - public void openLovelyDialogWithIdAndBundle(int dialogId, Bundle bundle) { - conversationMenuBundle = bundle; - switch (dialogId) { - case ID_DELETE_CONVERSATION_DIALOG: - showLovelyDialog(dialogId, null); - break; - default: - break; - } - + @Override + public void openLovelyDialogWithIdAndBundle(int dialogId, Bundle bundle) { + conversationMenuBundle = bundle; + switch (dialogId) { + case ID_DELETE_CONVERSATION_DIALOG: + showLovelyDialog(dialogId, null); + break; + default: + break; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java b/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java index 720bb2e71..ec0d66444 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java @@ -48,122 +48,132 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class LockedController extends BaseController { - public static final String TAG = "LockedController"; - private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 112; + public static final String TAG = "LockedController"; + private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 112; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_locked, container, false); + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_locked, container, false); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + if (getActionBar() != null) { + getActionBar().hide(); } + } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - if (getActionBar() != null) { - getActionBar().hide(); - } - } + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + checkIfWeAreSecure(); + } - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - checkIfWeAreSecure(); - } + @RequiresApi(api = Build.VERSION_CODES.M) + @OnClick(R.id.unlockTextView) + void unlock() { + checkIfWeAreSecure(); + } - @RequiresApi(api = Build.VERSION_CODES.M) - @OnClick(R.id.unlockTextView) - void unlock() { - checkIfWeAreSecure(); - } + @RequiresApi(api = Build.VERSION_CODES.M) + private void showBiometricDialog() { + Context context = getActivity(); - @RequiresApi(api = Build.VERSION_CODES.M) - private void showBiometricDialog() { - Context context = getActivity(); + if (context != null) { + final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder() + .setTitle(String.format(context.getString(R.string.nc_biometric_unlock), + context.getString(R.string.nc_app_name))) + .setNegativeButtonText(context.getString(R.string.nc_cancel)) + .build(); - if (context != null) { - final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder() - .setTitle(String.format(context.getString(R.string.nc_biometric_unlock), context.getString(R.string.nc_app_name))) - .setNegativeButtonText(context.getString(R.string.nc_cancel)) - .build(); + Executor executor = Executors.newSingleThreadExecutor(); - Executor executor = Executors.newSingleThreadExecutor(); - - final BiometricPrompt biometricPrompt = new BiometricPrompt((FragmentActivity) context, executor, - new BiometricPrompt.AuthenticationCallback() { - @Override - public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { - super.onAuthenticationSucceeded(result); - Log.d(TAG, "Fingerprint recognised successfully"); - new Handler(Looper.getMainLooper()).post(() -> getRouter().popCurrentController()); - } - - @Override - public void onAuthenticationFailed() { - super.onAuthenticationFailed(); - Log.d(TAG, "Fingerprint not recognised"); - } - - @Override - public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { - super.onAuthenticationError(errorCode, errString); - showAuthenticationScreen(); - } - } - ); - - BiometricPrompt.CryptoObject cryptoObject = SecurityUtils.getCryptoObject(); - if (cryptoObject != null) { - biometricPrompt.authenticate(promptInfo, cryptoObject); - } else { - biometricPrompt.authenticate(promptInfo); - } - } - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private void checkIfWeAreSecure() { - if (getActivity() != null) { - KeyguardManager keyguardManager = (KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE); - if (keyguardManager != null && keyguardManager.isKeyguardSecure() && appPreferences.getIsScreenLocked()) { - if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.getScreenLockTimeout())) { - showBiometricDialog(); - } else { - getRouter().popCurrentController(); + final BiometricPrompt biometricPrompt = + new BiometricPrompt((FragmentActivity) context, executor, + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationSucceeded( + @NonNull BiometricPrompt.AuthenticationResult result) { + super.onAuthenticationSucceeded(result); + Log.d(TAG, "Fingerprint recognised successfully"); + new Handler(Looper.getMainLooper()).post( + () -> getRouter().popCurrentController()); } - } - } - } - private void showAuthenticationScreen() { - if (getActivity() != null) { - KeyguardManager keyguardManager = (KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE); - Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null, null); - if (intent != null) { - startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); - } - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { - if (resultCode == Activity.RESULT_OK) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (SecurityUtils.checkIfWeAreAuthenticated(appPreferences.getScreenLockTimeout())) { - Log.d(TAG, "All went well, dismiss locked controller"); - getRouter().popCurrentController(); - } + @Override + public void onAuthenticationFailed() { + super.onAuthenticationFailed(); + Log.d(TAG, "Fingerprint not recognised"); } - } else { - Log.d(TAG, "Authorization failed"); - } - } + + @Override + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + super.onAuthenticationError(errorCode, errString); + showAuthenticationScreen(); + } + } + ); + + BiometricPrompt.CryptoObject cryptoObject = SecurityUtils.getCryptoObject(); + if (cryptoObject != null) { + biometricPrompt.authenticate(promptInfo, cryptoObject); + } else { + biometricPrompt.authenticate(promptInfo); + } } + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private void checkIfWeAreSecure() { + if (getActivity() != null) { + KeyguardManager keyguardManager = + (KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE); + if (keyguardManager != null + && keyguardManager.isKeyguardSecure() + && appPreferences.getIsScreenLocked()) { + if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.getScreenLockTimeout())) { + showBiometricDialog(); + } else { + getRouter().popCurrentController(); + } + } + } + } + + private void showAuthenticationScreen() { + if (getActivity() != null) { + KeyguardManager keyguardManager = + (KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE); + Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null, null); + if (intent != null) { + startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); + } + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { + if (resultCode == Activity.RESULT_OK) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (SecurityUtils.checkIfWeAreAuthenticated(appPreferences.getScreenLockTimeout())) { + Log.d(TAG, "All went well, dismiss locked controller"); + getRouter().popCurrentController(); + } + } + } else { + Log.d(TAG, "Authorization failed"); + } + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java index 39f2290e7..d1b608461 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java @@ -40,11 +40,11 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import autodagger.AutoInjector; import butterknife.BindView; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.R; import com.nextcloud.talk.adapters.items.NotificationSoundItem; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; +import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.preferences.AppPreferences; import eu.davidea.flexibleadapter.FlexibleAdapter; @@ -57,247 +57,255 @@ import java.util.List; import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) -public class RingtoneSelectionController extends BaseController implements FlexibleAdapter.OnItemClickListener { +public class RingtoneSelectionController extends BaseController + implements FlexibleAdapter.OnItemClickListener { - private static final String TAG = "RingtoneSelectionController"; + private static final String TAG = "RingtoneSelectionController"; - @BindView(R.id.recyclerView) - RecyclerView recyclerView; + @BindView(R.id.recyclerView) + RecyclerView recyclerView; - @BindView(R.id.swipe_refresh_layout) - SwipeRefreshLayout swipeRefreshLayout; + @BindView(R.id.swipe_refresh_layout) + SwipeRefreshLayout swipeRefreshLayout; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @Inject - Context context; + @Inject + Context context; - private FlexibleAdapter adapter; - private RecyclerView.AdapterDataObserver adapterDataObserver; - private List abstractFlexibleItemList = new ArrayList<>(); + private FlexibleAdapter adapter; + private RecyclerView.AdapterDataObserver adapterDataObserver; + private List abstractFlexibleItemList = new ArrayList<>(); - private boolean callNotificationSounds; - private MediaPlayer mediaPlayer; - private Handler cancelMediaPlayerHandler; + private boolean callNotificationSounds; + private MediaPlayer mediaPlayer; + private Handler cancelMediaPlayerHandler; - public RingtoneSelectionController(Bundle args) { - super(); - setHasOptionsMenu(true); - this.callNotificationSounds = args.getBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), false); + public RingtoneSelectionController(Bundle args) { + super(); + setHasOptionsMenu(true); + this.callNotificationSounds = + args.getBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), false); + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_generic_rv, container, false); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + if (adapter == null) { + adapter = new FlexibleAdapter<>(abstractFlexibleItemList, getActivity(), false); + + adapter.setNotifyChangeOfUnfilteredItems(true) + .setMode(SelectableAdapter.Mode.SINGLE); + + adapter.addListener(this); + + cancelMediaPlayerHandler = new Handler(); } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_generic_rv, container, false); + adapter.addListener(this); + prepareViews(); + fetchNotificationSounds(); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + return getRouter().popCurrentController(); + default: + return super.onOptionsItemSelected(item); + } + } + + private void prepareViews() { + RecyclerView.LayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setHasFixedSize(true); + recyclerView.setAdapter(adapter); + + adapterDataObserver = new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + findSelectedSound(); + } + }; + + adapter.registerAdapterDataObserver(adapterDataObserver); + swipeRefreshLayout.setEnabled(false); + } + + @SuppressLint("LongLogTag") + private void findSelectedSound() { + boolean foundDefault = false; + + String preferencesString = null; + if ((callNotificationSounds && TextUtils.isEmpty( + (preferencesString = appPreferences.getCallRingtoneUri()))) + || (!callNotificationSounds && TextUtils.isEmpty((preferencesString = appPreferences + .getMessageRingtoneUri())))) { + adapter.toggleSelection(1); + foundDefault = true; } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - if (adapter == null) { - adapter = new FlexibleAdapter<>(abstractFlexibleItemList, getActivity(), false); - - adapter.setNotifyChangeOfUnfilteredItems(true) - .setMode(SelectableAdapter.Mode.SINGLE); - - adapter.addListener(this); - - cancelMediaPlayerHandler = new Handler(); - } - - adapter.addListener(this); - prepareViews(); - fetchNotificationSounds(); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - return getRouter().popCurrentController(); - default: - return super.onOptionsItemSelected(item); - } - } - - private void prepareViews() { - RecyclerView.LayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - recyclerView.setAdapter(adapter); - - adapterDataObserver = new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - super.onChanged(); - findSelectedSound(); - } - }; - - adapter.registerAdapterDataObserver(adapterDataObserver); - swipeRefreshLayout.setEnabled(false); - } - - @SuppressLint("LongLogTag") - private void findSelectedSound() { - boolean foundDefault = false; - - String preferencesString = null; - if ((callNotificationSounds && TextUtils.isEmpty((preferencesString = appPreferences.getCallRingtoneUri()))) - || (!callNotificationSounds && TextUtils.isEmpty((preferencesString = appPreferences - .getMessageRingtoneUri())))) { - adapter.toggleSelection(1); - foundDefault = true; - } - - if (!TextUtils.isEmpty(preferencesString) && !foundDefault) { - try { - RingtoneSettings ringtoneSettings = LoganSquare.parse(preferencesString, RingtoneSettings.class); - if (ringtoneSettings.getRingtoneUri() == null) { - adapter.toggleSelection(0); - } else if (ringtoneSettings.getRingtoneUri().toString().equals(getRingtoneString())) { - adapter.toggleSelection(1); - } else { - NotificationSoundItem notificationSoundItem; - for (int i = 2; i < adapter.getItemCount(); i++) { - notificationSoundItem = (NotificationSoundItem) adapter.getItem(i); - if (notificationSoundItem.getNotificationSoundUri().equals(ringtoneSettings.getRingtoneUri().toString())) { - adapter.toggleSelection(i); - break; - } - } - } - } catch (IOException e) { - Log.e(TAG, "Failed to parse ringtone settings"); - } - } - - adapter.unregisterAdapterDataObserver(adapterDataObserver); - adapterDataObserver = null; - } - - private String getRingtoneString() { - if (callNotificationSounds) { - return ("android.resource://" + context.getPackageName() + - "/raw/librem_by_feandesign_call"); + if (!TextUtils.isEmpty(preferencesString) && !foundDefault) { + try { + RingtoneSettings ringtoneSettings = + LoganSquare.parse(preferencesString, RingtoneSettings.class); + if (ringtoneSettings.getRingtoneUri() == null) { + adapter.toggleSelection(0); + } else if (ringtoneSettings.getRingtoneUri().toString().equals(getRingtoneString())) { + adapter.toggleSelection(1); } else { - return ("android.resource://" + context.getPackageName() + "/raw" + - "/librem_by_feandesign_message"); - } - - } - - private void fetchNotificationSounds() { - abstractFlexibleItemList.add(new NotificationSoundItem(getResources().getString(R.string.nc_settings_no_ringtone), null)); - abstractFlexibleItemList.add(new NotificationSoundItem(getResources() - .getString(R.string.nc_settings_default_ringtone), getRingtoneString())); - - - if (getActivity() != null) { - RingtoneManager manager = new RingtoneManager(getActivity()); - - if (callNotificationSounds) { - manager.setType(RingtoneManager.TYPE_RINGTONE); - } else { - manager.setType(RingtoneManager.TYPE_NOTIFICATION); - } - - Cursor cursor = manager.getCursor(); - - NotificationSoundItem notificationSoundItem; - - while (cursor.moveToNext()) { - String notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); - String notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX); - String completeNotificationUri = notificationUri + "/" + cursor.getString(RingtoneManager - .ID_COLUMN_INDEX); - - notificationSoundItem = new NotificationSoundItem(notificationTitle, completeNotificationUri); - - abstractFlexibleItemList.add(notificationSoundItem); + NotificationSoundItem notificationSoundItem; + for (int i = 2; i < adapter.getItemCount(); i++) { + notificationSoundItem = (NotificationSoundItem) adapter.getItem(i); + if (notificationSoundItem.getNotificationSoundUri() + .equals(ringtoneSettings.getRingtoneUri().toString())) { + adapter.toggleSelection(i); + break; } + } } - - adapter.updateDataSet(abstractFlexibleItemList, false); + } catch (IOException e) { + Log.e(TAG, "Failed to parse ringtone settings"); + } } - @Override - public String getTitle() { - return getResources().getString(R.string.nc_settings_notification_sounds); + adapter.unregisterAdapterDataObserver(adapterDataObserver); + adapterDataObserver = null; + } + + private String getRingtoneString() { + if (callNotificationSounds) { + return ("android.resource://" + context.getPackageName() + + "/raw/librem_by_feandesign_call"); + } else { + return ("android.resource://" + context.getPackageName() + "/raw" + + "/librem_by_feandesign_message"); + } + } + + private void fetchNotificationSounds() { + abstractFlexibleItemList.add( + new NotificationSoundItem(getResources().getString(R.string.nc_settings_no_ringtone), + null)); + abstractFlexibleItemList.add(new NotificationSoundItem(getResources() + .getString(R.string.nc_settings_default_ringtone), getRingtoneString())); + + if (getActivity() != null) { + RingtoneManager manager = new RingtoneManager(getActivity()); + + if (callNotificationSounds) { + manager.setType(RingtoneManager.TYPE_RINGTONE); + } else { + manager.setType(RingtoneManager.TYPE_NOTIFICATION); + } + + Cursor cursor = manager.getCursor(); + + NotificationSoundItem notificationSoundItem; + + while (cursor.moveToNext()) { + String notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); + String notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX); + String completeNotificationUri = notificationUri + "/" + cursor.getString(RingtoneManager + .ID_COLUMN_INDEX); + + notificationSoundItem = + new NotificationSoundItem(notificationTitle, completeNotificationUri); + + abstractFlexibleItemList.add(notificationSoundItem); + } } - @SuppressLint("LongLogTag") - @Override - public boolean onItemClick(View view, int position) { - NotificationSoundItem notificationSoundItem = (NotificationSoundItem) adapter.getItem(position); + adapter.updateDataSet(abstractFlexibleItemList, false); + } - Uri ringtoneUri = null; + @Override + public String getTitle() { + return getResources().getString(R.string.nc_settings_notification_sounds); + } - if (!TextUtils.isEmpty(notificationSoundItem.getNotificationSoundUri())) { - ringtoneUri = Uri.parse(notificationSoundItem.getNotificationSoundUri()); + @SuppressLint("LongLogTag") + @Override + public boolean onItemClick(View view, int position) { + NotificationSoundItem notificationSoundItem = (NotificationSoundItem) adapter.getItem(position); - endMediaPlayer(); - mediaPlayer = MediaPlayer.create(getActivity(), ringtoneUri); + Uri ringtoneUri = null; - cancelMediaPlayerHandler = new Handler(); - cancelMediaPlayerHandler.postDelayed(new Runnable() { - @Override - public void run() { - endMediaPlayer(); - } - }, mediaPlayer.getDuration() + 25); - mediaPlayer.start(); + if (!TextUtils.isEmpty(notificationSoundItem.getNotificationSoundUri())) { + ringtoneUri = Uri.parse(notificationSoundItem.getNotificationSoundUri()); + + endMediaPlayer(); + mediaPlayer = MediaPlayer.create(getActivity(), ringtoneUri); + + cancelMediaPlayerHandler = new Handler(); + cancelMediaPlayerHandler.postDelayed(new Runnable() { + @Override + public void run() { + endMediaPlayer(); } - - if (adapter.getSelectedPositions().size() == 0 || adapter.getSelectedPositions().get(0) != position) { - RingtoneSettings ringtoneSettings = new RingtoneSettings(); - ringtoneSettings.setRingtoneName(notificationSoundItem.getNotificationSoundName()); - ringtoneSettings.setRingtoneUri(ringtoneUri); - - if (callNotificationSounds) { - try { - appPreferences.setCallRingtoneUri(LoganSquare.serialize(ringtoneSettings)); - adapter.toggleSelection(position); - adapter.notifyDataSetChanged(); - } catch (IOException e) { - Log.e(TAG, "Failed to store selected ringtone for calls"); - } - } else { - try { - appPreferences.setMessageRingtoneUri(LoganSquare.serialize(ringtoneSettings)); - adapter.toggleSelection(position); - adapter.notifyDataSetChanged(); - } catch (IOException e) { - Log.e(TAG, "Failed to store selected ringtone for calls"); - } - } - } - - return true; + }, mediaPlayer.getDuration() + 25); + mediaPlayer.start(); } - private void endMediaPlayer() { - if (cancelMediaPlayerHandler != null) { - cancelMediaPlayerHandler.removeCallbacksAndMessages(null); - } + if (adapter.getSelectedPositions().size() == 0 + || adapter.getSelectedPositions().get(0) != position) { + RingtoneSettings ringtoneSettings = new RingtoneSettings(); + ringtoneSettings.setRingtoneName(notificationSoundItem.getNotificationSoundName()); + ringtoneSettings.setRingtoneUri(ringtoneUri); - if (mediaPlayer != null) { - if (mediaPlayer.isPlaying()) { - mediaPlayer.stop(); - } - - mediaPlayer.release(); - mediaPlayer = null; + if (callNotificationSounds) { + try { + appPreferences.setCallRingtoneUri(LoganSquare.serialize(ringtoneSettings)); + adapter.toggleSelection(position); + adapter.notifyDataSetChanged(); + } catch (IOException e) { + Log.e(TAG, "Failed to store selected ringtone for calls"); } + } else { + try { + appPreferences.setMessageRingtoneUri(LoganSquare.serialize(ringtoneSettings)); + adapter.toggleSelection(position); + adapter.notifyDataSetChanged(); + } catch (IOException e) { + Log.e(TAG, "Failed to store selected ringtone for calls"); + } + } } - @Override - public void onDestroy() { - endMediaPlayer(); - super.onDestroy(); + return true; + } + + private void endMediaPlayer() { + if (cancelMediaPlayerHandler != null) { + cancelMediaPlayerHandler.removeCallbacksAndMessages(null); } + if (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) { + mediaPlayer.stop(); + } + + mediaPlayer.release(); + mediaPlayer = null; + } + } + + @Override + public void onDestroy() { + endMediaPlayer(); + super.onDestroy(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java index f6998b6e6..d80f0a4a7 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java @@ -62,301 +62,309 @@ import studio.carbonylgroup.textfieldboxes.TextFieldBoxes; @AutoInjector(NextcloudTalkApplication.class) public class ServerSelectionController extends BaseController { - public static final String TAG = "ServerSelectionController"; + public static final String TAG = "ServerSelectionController"; - @BindView(R.id.extended_edit_text) - ExtendedEditText serverEntry; - @BindView(R.id.text_field_boxes) - TextFieldBoxes textFieldBoxes; - @BindView(R.id.progress_bar) - ProgressBar progressBar; - @BindView(R.id.helper_text_view) - TextView providersTextView; - @BindView(R.id.cert_text_view) - TextView certTextView; + @BindView(R.id.extended_edit_text) + ExtendedEditText serverEntry; + @BindView(R.id.text_field_boxes) + TextFieldBoxes textFieldBoxes; + @BindView(R.id.progress_bar) + ProgressBar progressBar; + @BindView(R.id.helper_text_view) + TextView providersTextView; + @BindView(R.id.cert_text_view) + TextView certTextView; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_server_selection, container, false); - } + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_server_selection, container, false); + } - @SuppressLint("LongLogTag") - @OnClick(R.id.cert_text_view) - public void onCertClick() { - if (getActivity() != null) { - KeyChain.choosePrivateKeyAlias(getActivity(), alias -> { - if (alias != null) { - appPreferences.setTemporaryClientCertAlias(alias); - } else { - appPreferences.removeTemporaryClientCertAlias(); - } - - setCertTextView(); - }, new String[]{"RSA", "EC"}, null, null, -1, null); - } - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } - - if (getActionBar() != null) { - getActionBar().hide(); - } - - textFieldBoxes.getEndIconImageButton().setBackgroundDrawable(getResources().getDrawable(R.drawable - .ic_arrow_forward_white_24px)); - textFieldBoxes.getEndIconImageButton().setAlpha(0.5f); - textFieldBoxes.getEndIconImageButton().setEnabled(false); - textFieldBoxes.getEndIconImageButton().setVisibility(View.VISIBLE); - textFieldBoxes.getEndIconImageButton().setOnClickListener(view1 -> checkServerAndProceed()); - - if (TextUtils.isEmpty(getResources().getString(R.string.nc_providers_url)) && (TextUtils.isEmpty(getResources - ().getString(R.string.nc_import_account_type)))) { - providersTextView.setVisibility(View.INVISIBLE); + @SuppressLint("LongLogTag") + @OnClick(R.id.cert_text_view) + public void onCertClick() { + if (getActivity() != null) { + KeyChain.choosePrivateKeyAlias(getActivity(), alias -> { + if (alias != null) { + appPreferences.setTemporaryClientCertAlias(alias); } else { - if ((TextUtils.isEmpty(getResources - ().getString(R.string.nc_import_account_type)) || - AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() == 0) && - userUtils.getUsers().size() == 0) { - - providersTextView.setText(R.string.nc_get_from_provider); - providersTextView.setOnClickListener(view12 -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources() - .getString(R.string.nc_providers_url))); - startActivity(browserIntent); - }); - } else if (AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() > 0) { - if (!TextUtils.isEmpty(AccountUtils.INSTANCE.getAppNameBasedOnPackage(getResources() - .getString(R.string.nc_import_accounts_from)))) { - if (AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() > 1) { - providersTextView.setText(String.format(getResources().getString(R.string - .nc_server_import_accounts), AccountUtils.INSTANCE.getAppNameBasedOnPackage(getResources() - .getString(R.string.nc_import_accounts_from)))); - } else { - providersTextView.setText(String.format(getResources().getString(R.string - .nc_server_import_account), AccountUtils.INSTANCE.getAppNameBasedOnPackage(getResources() - .getString(R.string.nc_import_accounts_from)))); - } - } else { - if (AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() > 1) { - providersTextView.setText(getResources().getString(R.string.nc_server_import_accounts_plain)); - } else { - providersTextView.setText(getResources().getString(R.string - .nc_server_import_account_plain)); - } - } - - providersTextView.setOnClickListener(view13 -> { - Bundle bundle = new Bundle(); - bundle.putBoolean(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT(), true); - getRouter().pushController(RouterTransaction.with( - new SwitchAccountController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - }); - } else { - providersTextView.setVisibility(View.INVISIBLE); - } - } - - serverEntry.requestFocus(); - - serverEntry.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { - - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - - } - - @Override - public void afterTextChanged(Editable editable) { - if (!textFieldBoxes.isOnError() && !TextUtils.isEmpty(serverEntry.getText())) { - toggleProceedButton(true); - } else { - toggleProceedButton(false); - } - } - }); - - serverEntry.setOnEditorActionListener((textView, i, keyEvent) -> { - if (i == EditorInfo.IME_ACTION_DONE) { - checkServerAndProceed(); - } - - return false; - }); - } - - private void toggleProceedButton(boolean show) { - textFieldBoxes.getEndIconImageButton().setEnabled(show); - - if (show) { - textFieldBoxes.getEndIconImageButton().setAlpha(1f); - } else { - textFieldBoxes.getEndIconImageButton().setAlpha(0.5f); - } - } - - private void checkServerAndProceed() { - String url = serverEntry.getText().toString().trim(); - - serverEntry.setEnabled(false); - progressBar.setVisibility(View.VISIBLE); - if (providersTextView.getVisibility() != View.INVISIBLE) { - providersTextView.setVisibility(View.INVISIBLE); - certTextView.setVisibility(View.INVISIBLE); - } - - if (url.endsWith("/")) { - url = url.substring(0, url.length() - 1); - } - - String queryUrl = url + ApiUtils.getUrlPostfixForStatus(); - - if (url.startsWith("http://") || url.startsWith("https://")) { - checkServer(queryUrl, false); - } else { - checkServer("https://" + queryUrl, true); - } - } - - private void checkServer(String queryUrl, boolean checkForcedHttps) { - ncApi.getServerStatus(queryUrl) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(status -> { - String productName = getResources().getString(R.string.nc_server_product_name); - - String versionString = status.getVersion().substring(0, status.getVersion().indexOf(".")); - int version = Integer.parseInt(versionString); - if (status.isInstalled() && !status.isMaintenance() && - !status.isNeedsUpgrade() && - version >= 13) { - - getRouter().pushController(RouterTransaction.with( - new WebViewLoginController(queryUrl.replace("/status.php", ""), - false)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (!status.isInstalled()) { - textFieldBoxes.setError(String.format( - getResources().getString(R.string.nc_server_not_installed), productName), - true); - toggleProceedButton(false); - } else if (status.isNeedsUpgrade()) { - textFieldBoxes.setError(String.format(getResources(). - getString(R.string.nc_server_db_upgrade_needed), - productName), true); - toggleProceedButton(false); - } else if (status.isMaintenance()) { - textFieldBoxes.setError(String.format(getResources(). - getString(R.string.nc_server_maintenance), - productName), - true); - toggleProceedButton(false); - } else if (!status.getVersion().startsWith("13.")) { - textFieldBoxes.setError(String.format(getResources(). - getString(R.string.nc_server_version), - getResources().getString(R.string.nc_app_name) - , productName), true); - toggleProceedButton(false); - } - - }, throwable -> { - if (checkForcedHttps) { - checkServer(queryUrl.replace("https://", "http://"), false); - } else { - if (throwable.getLocalizedMessage() != null) { - textFieldBoxes.setError(throwable.getLocalizedMessage(), true); - } else if (throwable.getCause() instanceof CertificateException) { - textFieldBoxes.setError(getResources().getString(R.string.nc_certificate_error), - false); - } - - if (serverEntry != null) { - serverEntry.setEnabled(true); - } - - progressBar.setVisibility(View.INVISIBLE); - if (providersTextView.getVisibility() != View.INVISIBLE) { - providersTextView.setVisibility(View.VISIBLE); - certTextView.setVisibility(View.VISIBLE); - } - toggleProceedButton(false); - } - }, () -> { - progressBar.setVisibility(View.INVISIBLE); - if (providersTextView.getVisibility() != View.INVISIBLE) { - providersTextView.setVisibility(View.VISIBLE); - certTextView.setVisibility(View.VISIBLE); - } - }); - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - if (ApplicationWideMessageHolder.getInstance().getMessageType() != null) { - if (ApplicationWideMessageHolder.getInstance().getMessageType() - .equals(ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION)) { - textFieldBoxes.setError(getResources().getString(R.string.nc_account_scheduled_for_deletion), - false); - ApplicationWideMessageHolder.getInstance().setMessageType(null); - } else if (ApplicationWideMessageHolder.getInstance().getMessageType() - .equals(ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK)) { - textFieldBoxes.setError(getResources().getString(R.string.nc_settings_no_talk_installed), - false); - } else if (ApplicationWideMessageHolder.getInstance().getMessageType() - .equals(ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT)) { - textFieldBoxes.setError(getResources().getString(R.string.nc_server_failed_to_import_account), - false); - } - ApplicationWideMessageHolder.getInstance().setMessageType(null); + appPreferences.removeTemporaryClientCertAlias(); } setCertTextView(); + }, new String[] { "RSA", "EC" }, null, null, -1, null); + } + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + if (getActivity() != null) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } - private void setCertTextView() { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - if (!TextUtils.isEmpty(appPreferences.getTemporaryClientCertAlias())) { - certTextView.setText(R.string.nc_change_cert_auth); - } else { - certTextView.setText(R.string.nc_configure_cert_auth); - } + if (getActionBar() != null) { + getActionBar().hide(); + } - textFieldBoxes.setError("", true); - toggleProceedButton(true); - }); + textFieldBoxes.getEndIconImageButton() + .setBackgroundDrawable(getResources().getDrawable(R.drawable + .ic_arrow_forward_white_24px)); + textFieldBoxes.getEndIconImageButton().setAlpha(0.5f); + textFieldBoxes.getEndIconImageButton().setEnabled(false); + textFieldBoxes.getEndIconImageButton().setVisibility(View.VISIBLE); + textFieldBoxes.getEndIconImageButton().setOnClickListener(view1 -> checkServerAndProceed()); + + if (TextUtils.isEmpty(getResources().getString(R.string.nc_providers_url)) + && (TextUtils.isEmpty(getResources + ().getString(R.string.nc_import_account_type)))) { + providersTextView.setVisibility(View.INVISIBLE); + } else { + if ((TextUtils.isEmpty(getResources + ().getString(R.string.nc_import_account_type)) || + AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() == 0) && + userUtils.getUsers().size() == 0) { + + providersTextView.setText(R.string.nc_get_from_provider); + providersTextView.setOnClickListener(view12 -> { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources() + .getString(R.string.nc_providers_url))); + startActivity(browserIntent); + }); + } else if (AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() > 0) { + if (!TextUtils.isEmpty(AccountUtils.INSTANCE.getAppNameBasedOnPackage(getResources() + .getString(R.string.nc_import_accounts_from)))) { + if (AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() > 1) { + providersTextView.setText(String.format(getResources().getString(R.string + .nc_server_import_accounts), + AccountUtils.INSTANCE.getAppNameBasedOnPackage(getResources() + .getString(R.string.nc_import_accounts_from)))); + } else { + providersTextView.setText(String.format(getResources().getString(R.string + .nc_server_import_account), + AccountUtils.INSTANCE.getAppNameBasedOnPackage(getResources() + .getString(R.string.nc_import_accounts_from)))); + } + } else { + if (AccountUtils.INSTANCE.findAccounts(userUtils.getUsers()).size() > 1) { + providersTextView.setText( + getResources().getString(R.string.nc_server_import_accounts_plain)); + } else { + providersTextView.setText(getResources().getString(R.string + .nc_server_import_account_plain)); + } } + + providersTextView.setOnClickListener(view13 -> { + Bundle bundle = new Bundle(); + bundle.putBoolean(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT(), true); + getRouter().pushController(RouterTransaction.with( + new SwitchAccountController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + }); + } else { + providersTextView.setVisibility(View.INVISIBLE); + } } - @Override - protected void onDestroyView(@NonNull View view) { - super.onDestroyView(view); - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + serverEntry.requestFocus(); + + serverEntry.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void afterTextChanged(Editable editable) { + if (!textFieldBoxes.isOnError() && !TextUtils.isEmpty(serverEntry.getText())) { + toggleProceedButton(true); + } else { + toggleProceedButton(false); } + } + }); + + serverEntry.setOnEditorActionListener((textView, i, keyEvent) -> { + if (i == EditorInfo.IME_ACTION_DONE) { + checkServerAndProceed(); + } + + return false; + }); + } + + private void toggleProceedButton(boolean show) { + textFieldBoxes.getEndIconImageButton().setEnabled(show); + + if (show) { + textFieldBoxes.getEndIconImageButton().setAlpha(1f); + } else { + textFieldBoxes.getEndIconImageButton().setAlpha(0.5f); } + } + + private void checkServerAndProceed() { + String url = serverEntry.getText().toString().trim(); + + serverEntry.setEnabled(false); + progressBar.setVisibility(View.VISIBLE); + if (providersTextView.getVisibility() != View.INVISIBLE) { + providersTextView.setVisibility(View.INVISIBLE); + certTextView.setVisibility(View.INVISIBLE); + } + + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + + String queryUrl = url + ApiUtils.getUrlPostfixForStatus(); + + if (url.startsWith("http://") || url.startsWith("https://")) { + checkServer(queryUrl, false); + } else { + checkServer("https://" + queryUrl, true); + } + } + + private void checkServer(String queryUrl, boolean checkForcedHttps) { + ncApi.getServerStatus(queryUrl) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(status -> { + String productName = getResources().getString(R.string.nc_server_product_name); + + String versionString = status.getVersion().substring(0, status.getVersion().indexOf(".")); + int version = Integer.parseInt(versionString); + if (status.isInstalled() && !status.isMaintenance() && + !status.isNeedsUpgrade() && + version >= 13) { + + getRouter().pushController(RouterTransaction.with( + new WebViewLoginController(queryUrl.replace("/status.php", ""), + false)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else if (!status.isInstalled()) { + textFieldBoxes.setError(String.format( + getResources().getString(R.string.nc_server_not_installed), productName), + true); + toggleProceedButton(false); + } else if (status.isNeedsUpgrade()) { + textFieldBoxes.setError(String.format(getResources(). + getString(R.string.nc_server_db_upgrade_needed), + productName), true); + toggleProceedButton(false); + } else if (status.isMaintenance()) { + textFieldBoxes.setError(String.format(getResources(). + getString(R.string.nc_server_maintenance), + productName), + true); + toggleProceedButton(false); + } else if (!status.getVersion().startsWith("13.")) { + textFieldBoxes.setError(String.format(getResources(). + getString(R.string.nc_server_version), + getResources().getString(R.string.nc_app_name) + , productName), true); + toggleProceedButton(false); + } + }, throwable -> { + if (checkForcedHttps) { + checkServer(queryUrl.replace("https://", "http://"), false); + } else { + if (throwable.getLocalizedMessage() != null) { + textFieldBoxes.setError(throwable.getLocalizedMessage(), true); + } else if (throwable.getCause() instanceof CertificateException) { + textFieldBoxes.setError(getResources().getString(R.string.nc_certificate_error), + false); + } + + if (serverEntry != null) { + serverEntry.setEnabled(true); + } + + progressBar.setVisibility(View.INVISIBLE); + if (providersTextView.getVisibility() != View.INVISIBLE) { + providersTextView.setVisibility(View.VISIBLE); + certTextView.setVisibility(View.VISIBLE); + } + toggleProceedButton(false); + } + }, () -> { + progressBar.setVisibility(View.INVISIBLE); + if (providersTextView.getVisibility() != View.INVISIBLE) { + providersTextView.setVisibility(View.VISIBLE); + certTextView.setVisibility(View.VISIBLE); + } + }); + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + if (ApplicationWideMessageHolder.getInstance().getMessageType() != null) { + if (ApplicationWideMessageHolder.getInstance().getMessageType() + .equals(ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION)) { + textFieldBoxes.setError( + getResources().getString(R.string.nc_account_scheduled_for_deletion), + false); + ApplicationWideMessageHolder.getInstance().setMessageType(null); + } else if (ApplicationWideMessageHolder.getInstance().getMessageType() + .equals(ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK)) { + textFieldBoxes.setError(getResources().getString(R.string.nc_settings_no_talk_installed), + false); + } else if (ApplicationWideMessageHolder.getInstance().getMessageType() + .equals(ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT)) { + textFieldBoxes.setError( + getResources().getString(R.string.nc_server_failed_to_import_account), + false); + } + ApplicationWideMessageHolder.getInstance().setMessageType(null); + } + + setCertTextView(); + } + + private void setCertTextView() { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + if (!TextUtils.isEmpty(appPreferences.getTemporaryClientCertAlias())) { + certTextView.setText(R.string.nc_change_cert_auth); + } else { + certTextView.setText(R.string.nc_configure_cert_auth); + } + + textFieldBoxes.setError("", true); + toggleProceedButton(true); + }); + } + } + + @Override + protected void onDestroyView(@NonNull View view) { + super.onDestroyView(view); + if (getActivity() != null) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java index 69acad333..f4dd20b08 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java @@ -52,13 +52,13 @@ import com.bluelinelabs.logansquare.LoganSquare; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; -import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.BuildConfig; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.jobs.AccountRemovalWorker; +import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; @@ -95,670 +95,685 @@ import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener; @AutoInjector(NextcloudTalkApplication.class) public class SettingsController extends BaseController { - public static final String TAG = "SettingsController"; - private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0; - @BindView(R.id.settings_screen) - MaterialPreferenceScreen settingsScreen; - @BindView(R.id.settings_proxy_choice) - MaterialChoicePreference proxyChoice; - @BindView(R.id.settings_proxy_port_edit) - MaterialEditTextPreference proxyPortEditText; - @BindView(R.id.settings_licence) - MaterialStandardPreference licenceButton; - @BindView(R.id.settings_privacy) - MaterialStandardPreference privacyButton; - @BindView(R.id.settings_source_code) - MaterialStandardPreference sourceCodeButton; - @BindView(R.id.settings_version) - MaterialStandardPreference versionInfo; - @BindView(R.id.avatar_image) - SimpleDraweeView avatarImageView; - @BindView(R.id.display_name_text) - EmojiTextView displayNameTextView; - @BindView(R.id.base_url_text) - TextView baseUrlTextView; - @BindView(R.id.settings_call_sound) - MaterialStandardPreference settingsCallSound; - @BindView(R.id.settings_message_sound) - MaterialStandardPreference settingsMessageSound; - @BindView(R.id.settings_remove_account) - MaterialStandardPreference removeAccountButton; - @BindView(R.id.settings_switch) - MaterialStandardPreference switchAccountButton; - @BindView(R.id.settings_reauthorize) - MaterialStandardPreference reauthorizeButton; - @BindView(R.id.settings_add_account) - MaterialStandardPreference addAccountButton; - @BindView(R.id.message_view) - MaterialPreferenceCategory messageView; - @BindView(R.id.settings_client_cert) - MaterialStandardPreference certificateSetup; - @BindView(R.id.settings_always_vibrate) - MaterialSwitchPreference shouldVibrateSwitchPreference; - @BindView(R.id.settings_incognito_keyboard) - MaterialSwitchPreference incognitoKeyboardSwitchPreference; - @BindView(R.id.settings_screen_security) - MaterialSwitchPreference screenSecuritySwitchPreference; - @BindView(R.id.settings_link_previews) - MaterialSwitchPreference linkPreviewsSwitchPreference; - @BindView(R.id.settings_screen_lock) - MaterialSwitchPreference screenLockSwitchPreference; - @BindView(R.id.settings_screen_lock_timeout) - MaterialChoicePreference screenLockTimeoutChoicePreference; + public static final String TAG = "SettingsController"; + private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0; + @BindView(R.id.settings_screen) + MaterialPreferenceScreen settingsScreen; + @BindView(R.id.settings_proxy_choice) + MaterialChoicePreference proxyChoice; + @BindView(R.id.settings_proxy_port_edit) + MaterialEditTextPreference proxyPortEditText; + @BindView(R.id.settings_licence) + MaterialStandardPreference licenceButton; + @BindView(R.id.settings_privacy) + MaterialStandardPreference privacyButton; + @BindView(R.id.settings_source_code) + MaterialStandardPreference sourceCodeButton; + @BindView(R.id.settings_version) + MaterialStandardPreference versionInfo; + @BindView(R.id.avatar_image) + SimpleDraweeView avatarImageView; + @BindView(R.id.display_name_text) + EmojiTextView displayNameTextView; + @BindView(R.id.base_url_text) + TextView baseUrlTextView; + @BindView(R.id.settings_call_sound) + MaterialStandardPreference settingsCallSound; + @BindView(R.id.settings_message_sound) + MaterialStandardPreference settingsMessageSound; + @BindView(R.id.settings_remove_account) + MaterialStandardPreference removeAccountButton; + @BindView(R.id.settings_switch) + MaterialStandardPreference switchAccountButton; + @BindView(R.id.settings_reauthorize) + MaterialStandardPreference reauthorizeButton; + @BindView(R.id.settings_add_account) + MaterialStandardPreference addAccountButton; + @BindView(R.id.message_view) + MaterialPreferenceCategory messageView; + @BindView(R.id.settings_client_cert) + MaterialStandardPreference certificateSetup; + @BindView(R.id.settings_always_vibrate) + MaterialSwitchPreference shouldVibrateSwitchPreference; + @BindView(R.id.settings_incognito_keyboard) + MaterialSwitchPreference incognitoKeyboardSwitchPreference; + @BindView(R.id.settings_screen_security) + MaterialSwitchPreference screenSecuritySwitchPreference; + @BindView(R.id.settings_link_previews) + MaterialSwitchPreference linkPreviewsSwitchPreference; + @BindView(R.id.settings_screen_lock) + MaterialSwitchPreference screenLockSwitchPreference; + @BindView(R.id.settings_screen_lock_timeout) + MaterialChoicePreference screenLockTimeoutChoicePreference; - @BindView(R.id.message_text) - TextView messageText; - @Inject - AppPreferences appPreferences; - @Inject - NcApi ncApi; - @Inject - UserUtils userUtils; - @Inject - Context context; - private LovelySaveStateHandler saveStateHandler; - private UserEntity currentUser; - private String credentials; - private OnPreferenceValueChangedListener proxyTypeChangeListener; - private OnPreferenceValueChangedListener proxyCredentialsChangeListener; - private OnPreferenceValueChangedListener screenSecurityChangeListener; - private OnPreferenceValueChangedListener screenLockChangeListener; - private OnPreferenceValueChangedListener screenLockTimeoutChangeListener; - private OnPreferenceValueChangedListener themeChangeListener; + @BindView(R.id.message_text) + TextView messageText; + @Inject + AppPreferences appPreferences; + @Inject + NcApi ncApi; + @Inject + UserUtils userUtils; + @Inject + Context context; + private LovelySaveStateHandler saveStateHandler; + private UserEntity currentUser; + private String credentials; + private OnPreferenceValueChangedListener proxyTypeChangeListener; + private OnPreferenceValueChangedListener proxyCredentialsChangeListener; + private OnPreferenceValueChangedListener screenSecurityChangeListener; + private OnPreferenceValueChangedListener screenLockChangeListener; + private OnPreferenceValueChangedListener screenLockTimeoutChangeListener; + private OnPreferenceValueChangedListener themeChangeListener; - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_settings, container, false); + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_settings, container, false); + } + + private void getCurrentUser() { + currentUser = userUtils.getCurrentUser(); + credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + setHasOptionsMenu(true); + + ViewCompat.setTransitionName(avatarImageView, "userAvatar.transitionTag"); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + getCurrentUser(); + + if (saveStateHandler == null) { + saveStateHandler = new LovelySaveStateHandler(); } - private void getCurrentUser() { - currentUser = userUtils.getCurrentUser(); - credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); + appPreferences.registerProxyTypeListener( + proxyTypeChangeListener = new ProxyTypeChangeListener()); + appPreferences.registerProxyCredentialsListener( + proxyCredentialsChangeListener = new ProxyCredentialsChangeListener()); + appPreferences.registerScreenSecurityListener( + screenSecurityChangeListener = new ScreenSecurityChangeListener()); + appPreferences.registerScreenLockListener(screenLockChangeListener = new ScreenLockListener()); + appPreferences.registerScreenLockTimeoutListener( + screenLockTimeoutChangeListener = new ScreenLockTimeoutListener()); + appPreferences.registerThemeChangeListener(themeChangeListener = new ThemeChangeListener()); + + List listWithIntFields = new ArrayList<>(); + listWithIntFields.add("proxy_port"); + + settingsScreen.setUserInputModule(new MagicUserInputModule(getActivity(), listWithIntFields)); + settingsScreen.setVisibilityController(R.id.settings_proxy_use_credentials, + Arrays.asList(R.id.settings_proxy_username_edit, R.id.settings_proxy_password_edit), + true); + + if (!TextUtils.isEmpty(getResources().getString(R.string.nc_gpl3_url))) { + licenceButton.addPreferenceClickListener(view1 -> { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources(). + getString(R.string.nc_gpl3_url))); + startActivity(browserIntent); + }); + } else { + licenceButton.setVisibility(View.GONE); } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - setHasOptionsMenu(true); + if (!DoNotDisturbUtils.INSTANCE.hasVibrator()) { + shouldVibrateSwitchPreference.setVisibility(View.GONE); + } - ViewCompat.setTransitionName(avatarImageView, "userAvatar.transitionTag"); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + incognitoKeyboardSwitchPreference.setVisibility(View.GONE); + } - getCurrentUser(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + screenLockSwitchPreference.setVisibility(View.GONE); + screenLockTimeoutChoicePreference.setVisibility(View.GONE); + } else { + screenLockSwitchPreference.setSummary(String.format(Locale.getDefault(), + getResources().getString(R.string.nc_settings_screen_lock_desc), + getResources().getString(R.string.nc_app_name))); + } - if (saveStateHandler == null) { - saveStateHandler = new LovelySaveStateHandler(); - } + if (!TextUtils.isEmpty(getResources().getString(R.string.nc_privacy_url))) { + privacyButton.addPreferenceClickListener(view12 -> { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources(). + getString(R.string.nc_privacy_url))); + startActivity(browserIntent); + }); + } else { + privacyButton.setVisibility(View.GONE); + } - appPreferences.registerProxyTypeListener(proxyTypeChangeListener = new ProxyTypeChangeListener()); - appPreferences.registerProxyCredentialsListener(proxyCredentialsChangeListener = new ProxyCredentialsChangeListener()); - appPreferences.registerScreenSecurityListener(screenSecurityChangeListener = new ScreenSecurityChangeListener()); - appPreferences.registerScreenLockListener(screenLockChangeListener = new ScreenLockListener()); - appPreferences.registerScreenLockTimeoutListener(screenLockTimeoutChangeListener = new ScreenLockTimeoutListener()); - appPreferences.registerThemeChangeListener(themeChangeListener = new ThemeChangeListener()); + if (!TextUtils.isEmpty(getResources().getString(R.string.nc_source_code_url))) { + sourceCodeButton.addPreferenceClickListener(view13 -> { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources(). + getString(R.string.nc_source_code_url))); + startActivity(browserIntent); + }); + } else { + sourceCodeButton.setVisibility(View.GONE); + } - List listWithIntFields = new ArrayList<>(); - listWithIntFields.add("proxy_port"); + versionInfo.setSummary("v" + BuildConfig.VERSION_NAME); - settingsScreen.setUserInputModule(new MagicUserInputModule(getActivity(), listWithIntFields)); - settingsScreen.setVisibilityController(R.id.settings_proxy_use_credentials, - Arrays.asList(R.id.settings_proxy_username_edit, R.id.settings_proxy_password_edit), - true); + settingsCallSound.setOnClickListener(v -> { + Bundle bundle = new Bundle(); + bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), true); + getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + }); - if (!TextUtils.isEmpty(getResources().getString(R.string.nc_gpl3_url))) { - licenceButton.addPreferenceClickListener(view1 -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources(). - getString(R.string.nc_gpl3_url))); - startActivity(browserIntent); - }); - } else { - licenceButton.setVisibility(View.GONE); - } + settingsMessageSound.setOnClickListener(v -> { + Bundle bundle = new Bundle(); + bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), false); + getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + }); - if (!DoNotDisturbUtils.INSTANCE.hasVibrator()) { - shouldVibrateSwitchPreference.setVisibility(View.GONE); - } + addAccountButton.addPreferenceClickListener(view15 -> { + getRouter().pushController(RouterTransaction.with(new + ServerSelectionController()).pushChangeHandler(new VerticalChangeHandler()) + .popChangeHandler(new VerticalChangeHandler())); + }); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - incognitoKeyboardSwitchPreference.setVisibility(View.GONE); - } + switchAccountButton.addPreferenceClickListener(view16 -> { + getRouter().pushController(RouterTransaction.with(new + SwitchAccountController()).pushChangeHandler(new VerticalChangeHandler()) + .popChangeHandler(new VerticalChangeHandler())); + }); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - screenLockSwitchPreference.setVisibility(View.GONE); - screenLockTimeoutChoicePreference.setVisibility(View.GONE); - } else { - screenLockSwitchPreference.setSummary(String.format(Locale.getDefault(), - getResources().getString(R.string.nc_settings_screen_lock_desc), - getResources().getString(R.string.nc_app_name))); - } + String host = null; + int port = -1; + URI uri; + try { + uri = new URI(currentUser.getBaseUrl()); + host = uri.getHost(); + port = uri.getPort(); + } catch (URISyntaxException e) { + Log.e(TAG, "Failed to create uri"); + } - if (!TextUtils.isEmpty(getResources().getString(R.string.nc_privacy_url))) { - privacyButton.addPreferenceClickListener(view12 -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources(). - getString(R.string.nc_privacy_url))); - startActivity(browserIntent); - }); - } else { - privacyButton.setVisibility(View.GONE); - } - - if (!TextUtils.isEmpty(getResources().getString(R.string.nc_source_code_url))) { - sourceCodeButton.addPreferenceClickListener(view13 -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources(). - getString(R.string.nc_source_code_url))); - startActivity(browserIntent); - }); - } else { - sourceCodeButton.setVisibility(View.GONE); - } - - versionInfo.setSummary("v" + BuildConfig.VERSION_NAME); - - settingsCallSound.setOnClickListener(v -> { - Bundle bundle = new Bundle(); - bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), true); - getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - }); - - settingsMessageSound.setOnClickListener(v -> { - Bundle bundle = new Bundle(); - bundle.putBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), false); - getRouter().pushController(RouterTransaction.with(new RingtoneSelectionController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - }); - - addAccountButton.addPreferenceClickListener(view15 -> { - getRouter().pushController(RouterTransaction.with(new - ServerSelectionController()).pushChangeHandler(new VerticalChangeHandler()) - .popChangeHandler(new VerticalChangeHandler())); - }); - - switchAccountButton.addPreferenceClickListener(view16 -> { - getRouter().pushController(RouterTransaction.with(new - SwitchAccountController()).pushChangeHandler(new VerticalChangeHandler()) - .popChangeHandler(new VerticalChangeHandler())); - }); - - String host = null; - int port = -1; - - URI uri; - try { - uri = new URI(currentUser.getBaseUrl()); - host = uri.getHost(); - port = uri.getPort(); - } catch (URISyntaxException e) { - Log.e(TAG, "Failed to create uri"); - } - - String finalHost = host; - int finalPort = port; - certificateSetup.addPreferenceClickListener(v -> KeyChain.choosePrivateKeyAlias(Objects.requireNonNull(getActivity()), alias -> { - String finalAlias = alias; - getActivity().runOnUiThread(() -> { + String finalHost = host; + int finalPort = port; + certificateSetup.addPreferenceClickListener( + v -> KeyChain.choosePrivateKeyAlias(Objects.requireNonNull(getActivity()), alias -> { + String finalAlias = alias; + getActivity().runOnUiThread(() -> { if (finalAlias != null) { - certificateSetup.setTitle(R.string.nc_client_cert_change); + certificateSetup.setTitle(R.string.nc_client_cert_change); } else { - certificateSetup.setTitle(R.string.nc_client_cert_setup); + certificateSetup.setTitle(R.string.nc_client_cert_setup); } - }); + }); - if (alias == null) { + if (alias == null) { alias = ""; - } + } - - userUtils.createOrUpdateUser(null, null, null, null, null, null, null, currentUser.getId(), - null, alias, null); - }, new String[]{"RSA", "EC"}, null, finalHost, finalPort, currentUser.getClientCertificate + userUtils.createOrUpdateUser(null, null, null, null, null, null, null, + currentUser.getId(), + null, alias, null); + }, new String[] { "RSA", "EC" }, null, finalHost, finalPort, + currentUser.getClientCertificate ())); + } + + private void showLovelyDialog(int dialogId, Bundle savedInstanceState) { + switch (dialogId) { + case ID_REMOVE_ACCOUNT_WARNING_DIALOG: + showRemoveAccountWarning(savedInstanceState); + break; + default: + break; } + } - private void showLovelyDialog(int dialogId, Bundle savedInstanceState) { - switch (dialogId) { - case ID_REMOVE_ACCOUNT_WARNING_DIALOG: - showRemoveAccountWarning(savedInstanceState); - break; - default: - break; - } + @OnClick(R.id.settings_version) + void sendLogs() { + if (getResources().getBoolean(R.bool.nc_is_debug)) { + LoggingUtils.INSTANCE.sendMailWithAttachment(context); } + } - @OnClick(R.id.settings_version) - void sendLogs() { - if (getResources().getBoolean(R.bool.nc_is_debug)) { - LoggingUtils.INSTANCE.sendMailWithAttachment(context); - } + @Override + protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { + saveStateHandler.saveInstanceState(outState); + super.onSaveViewState(view, outState); + } + + @Override + protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { + super.onRestoreViewState(view, savedViewState); + if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { + //Dialog won't be restarted automatically, so we need to call this method. + //Each dialog knows how to restore its viewState + showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); } + } - @Override - protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { - saveStateHandler.saveInstanceState(outState); - super.onSaveViewState(view, outState); - } - - @Override - protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { - super.onRestoreViewState(view, savedViewState); - if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { - //Dialog won't be restarted automatically, so we need to call this method. - //Each dialog knows how to restore its viewState - showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); - } - } - - private void showRemoveAccountWarning(Bundle savedInstanceState) { - if (getActivity() != null) { - new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL) - .setTopColorRes(R.color.nc_darkRed) - .setIcon(DisplayUtils.getTintedDrawable(getResources(), - R.drawable.ic_delete_black_24dp, R.color.bg_default)) - .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed)) - .setTitle(R.string.nc_settings_remove_account) - .setMessage(R.string.nc_settings_remove_confirmation) - .setPositiveButton(R.string.nc_settings_remove, new View.OnClickListener() { - @Override - public void onClick(View v) { - removeCurrentAccount(); - } - }) - .setNegativeButton(R.string.nc_cancel, null) - .setInstanceStateHandler(ID_REMOVE_ACCOUNT_WARNING_DIALOG, saveStateHandler) - .setSavedInstanceState(savedInstanceState) - .show(); - } - } - - private void removeCurrentAccount() { - boolean otherUserExists = userUtils.scheduleUserForDeletionWithId(currentUser.getId()); - - OneTimeWorkRequest accountRemovalWork = new OneTimeWorkRequest.Builder(AccountRemovalWorker.class).build(); - WorkManager.getInstance().enqueue(accountRemovalWork); - - if (otherUserExists && getView() != null) { - onViewBound(getView()); - onAttach(getView()); - } else if (!otherUserExists) { - getRouter().setRoot(RouterTransaction.with( - new ServerSelectionController()) - .pushChangeHandler(new VerticalChangeHandler()) - .popChangeHandler(new VerticalChangeHandler())); - } - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - - if (getActionBar() != null) { - getActionBar().show(); - } - - getCurrentUser(); - - if (!TextUtils.isEmpty(currentUser.getClientCertificate())) { - certificateSetup.setTitle(R.string.nc_client_cert_change); - } else { - certificateSetup.setTitle(R.string.nc_client_cert_setup); - } - - if (shouldVibrateSwitchPreference.getVisibility() == View.VISIBLE) { - ((Checkable) shouldVibrateSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getShouldVibrateSetting()); - } - - ((Checkable) screenSecuritySwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenSecured()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito()); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito()); - } - - ((Checkable) linkPreviewsSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getAreLinkPreviewsAllowed()); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); - - if (keyguardManager.isKeyguardSecure()) { - screenLockSwitchPreference.setEnabled(true); - screenLockTimeoutChoicePreference.setEnabled(true); - ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenLocked()); - - screenLockTimeoutChoicePreference.setEnabled(appPreferences.getIsScreenLocked()); - - if (appPreferences.getIsScreenLocked()) { - screenLockTimeoutChoicePreference.setAlpha(1.0f); - } else { - screenLockTimeoutChoicePreference.setAlpha(0.38f); - } - - screenLockSwitchPreference.setAlpha(1.0f); - } else { - screenLockSwitchPreference.setEnabled(false); - screenLockTimeoutChoicePreference.setEnabled(false); - appPreferences.removeScreenLock(); - appPreferences.removeScreenLockTimeout(); - ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(false); - screenLockSwitchPreference.setAlpha(0.38f); - screenLockTimeoutChoicePreference.setAlpha(0.38f); + private void showRemoveAccountWarning(Bundle savedInstanceState) { + if (getActivity() != null) { + new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL) + .setTopColorRes(R.color.nc_darkRed) + .setIcon(DisplayUtils.getTintedDrawable(getResources(), + R.drawable.ic_delete_black_24dp, R.color.bg_default)) + .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed)) + .setTitle(R.string.nc_settings_remove_account) + .setMessage(R.string.nc_settings_remove_confirmation) + .setPositiveButton(R.string.nc_settings_remove, new View.OnClickListener() { + @Override + public void onClick(View v) { + removeCurrentAccount(); } - } + }) + .setNegativeButton(R.string.nc_cancel, null) + .setInstanceStateHandler(ID_REMOVE_ACCOUNT_WARNING_DIALOG, saveStateHandler) + .setSavedInstanceState(savedInstanceState) + .show(); + } + } - String ringtoneName = ""; - RingtoneSettings ringtoneSettings; - if (!TextUtils.isEmpty(appPreferences.getCallRingtoneUri())) { - try { - ringtoneSettings = LoganSquare.parse(appPreferences.getCallRingtoneUri(), RingtoneSettings.class); - ringtoneName = ringtoneSettings.getRingtoneName(); - } catch (IOException e) { - Log.e(TAG, "Failed to parse ringtone name"); - } - settingsCallSound.setSummary(ringtoneName); + private void removeCurrentAccount() { + boolean otherUserExists = userUtils.scheduleUserForDeletionWithId(currentUser.getId()); + + OneTimeWorkRequest accountRemovalWork = + new OneTimeWorkRequest.Builder(AccountRemovalWorker.class).build(); + WorkManager.getInstance().enqueue(accountRemovalWork); + + if (otherUserExists && getView() != null) { + onViewBound(getView()); + onAttach(getView()); + } else if (!otherUserExists) { + getRouter().setRoot(RouterTransaction.with( + new ServerSelectionController()) + .pushChangeHandler(new VerticalChangeHandler()) + .popChangeHandler(new VerticalChangeHandler())); + } + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + + if (getActionBar() != null) { + getActionBar().show(); + } + + getCurrentUser(); + + if (!TextUtils.isEmpty(currentUser.getClientCertificate())) { + certificateSetup.setTitle(R.string.nc_client_cert_change); + } else { + certificateSetup.setTitle(R.string.nc_client_cert_setup); + } + + if (shouldVibrateSwitchPreference.getVisibility() == View.VISIBLE) { + ((Checkable) shouldVibrateSwitchPreference.findViewById(R.id.mp_checkable)).setChecked( + appPreferences.getShouldVibrateSetting()); + } + + ((Checkable) screenSecuritySwitchPreference.findViewById(R.id.mp_checkable)).setChecked( + appPreferences.getIsScreenSecured()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked( + appPreferences.getIsKeyboardIncognito()); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked( + appPreferences.getIsKeyboardIncognito()); + } + + ((Checkable) linkPreviewsSwitchPreference.findViewById(R.id.mp_checkable)).setChecked( + appPreferences.getAreLinkPreviewsAllowed()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + KeyguardManager keyguardManager = + (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + + if (keyguardManager.isKeyguardSecure()) { + screenLockSwitchPreference.setEnabled(true); + screenLockTimeoutChoicePreference.setEnabled(true); + ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked( + appPreferences.getIsScreenLocked()); + + screenLockTimeoutChoicePreference.setEnabled(appPreferences.getIsScreenLocked()); + + if (appPreferences.getIsScreenLocked()) { + screenLockTimeoutChoicePreference.setAlpha(1.0f); } else { - settingsCallSound.setSummary(R.string.nc_settings_default_ringtone); + screenLockTimeoutChoicePreference.setAlpha(0.38f); } - ringtoneName = ""; + screenLockSwitchPreference.setAlpha(1.0f); + } else { + screenLockSwitchPreference.setEnabled(false); + screenLockTimeoutChoicePreference.setEnabled(false); + appPreferences.removeScreenLock(); + appPreferences.removeScreenLockTimeout(); + ((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(false); + screenLockSwitchPreference.setAlpha(0.38f); + screenLockTimeoutChoicePreference.setAlpha(0.38f); + } + } - if (!TextUtils.isEmpty(appPreferences.getMessageRingtoneUri())) { - try { - ringtoneSettings = LoganSquare.parse(appPreferences.getMessageRingtoneUri(), RingtoneSettings.class); - ringtoneName = ringtoneSettings.getRingtoneName(); - } catch (IOException e) { - Log.e(TAG, "Failed to parse ringtone name"); - } - settingsMessageSound.setSummary(ringtoneName); - } else { - settingsMessageSound.setSummary(R.string.nc_settings_default_ringtone); - } + String ringtoneName = ""; + RingtoneSettings ringtoneSettings; + if (!TextUtils.isEmpty(appPreferences.getCallRingtoneUri())) { + try { + ringtoneSettings = + LoganSquare.parse(appPreferences.getCallRingtoneUri(), RingtoneSettings.class); + ringtoneName = ringtoneSettings.getRingtoneName(); + } catch (IOException e) { + Log.e(TAG, "Failed to parse ringtone name"); + } + settingsCallSound.setSummary(ringtoneName); + } else { + settingsCallSound.setSummary(R.string.nc_settings_default_ringtone); + } - if ("No proxy".equals(appPreferences.getProxyType()) || appPreferences.getProxyType() == null) { - hideProxySettings(); - } else { - showProxySettings(); - } + ringtoneName = ""; - if (appPreferences.getProxyCredentials()) { - showProxyCredentials(); - } else { - hideProxyCredentials(); - } + if (!TextUtils.isEmpty(appPreferences.getMessageRingtoneUri())) { + try { + ringtoneSettings = + LoganSquare.parse(appPreferences.getMessageRingtoneUri(), RingtoneSettings.class); + ringtoneName = ringtoneSettings.getRingtoneName(); + } catch (IOException e) { + Log.e(TAG, "Failed to parse ringtone name"); + } + settingsMessageSound.setSummary(ringtoneName); + } else { + settingsMessageSound.setSummary(R.string.nc_settings_default_ringtone); + } - if (currentUser != null) { + if ("No proxy".equals(appPreferences.getProxyType()) || appPreferences.getProxyType() == null) { + hideProxySettings(); + } else { + showProxySettings(); + } - baseUrlTextView.setText(Uri.parse(currentUser.getBaseUrl()).getHost()); + if (appPreferences.getProxyCredentials()) { + showProxyCredentials(); + } else { + hideProxyCredentials(); + } - reauthorizeButton.addPreferenceClickListener(view14 -> { - getRouter().pushController(RouterTransaction.with( - new WebViewLoginController(currentUser.getBaseUrl(), true)) - .pushChangeHandler(new VerticalChangeHandler()) - .popChangeHandler(new VerticalChangeHandler())); - }); + if (currentUser != null) { - if (currentUser.getDisplayName() != null) { - displayNameTextView.setText(currentUser.getDisplayName()); + baseUrlTextView.setText(Uri.parse(currentUser.getBaseUrl()).getHost()); + + reauthorizeButton.addPreferenceClickListener(view14 -> { + getRouter().pushController(RouterTransaction.with( + new WebViewLoginController(currentUser.getBaseUrl(), true)) + .pushChangeHandler(new VerticalChangeHandler()) + .popChangeHandler(new VerticalChangeHandler())); + }); + + if (currentUser.getDisplayName() != null) { + displayNameTextView.setText(currentUser.getDisplayName()); + } + + loadAvatarImage(); + + ncApi.getUserProfile(credentials, + ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(userProfileOverall -> { + + String displayName = null; + if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() + .getDisplayName())) { + displayName = userProfileOverall.getOcs().getData() + .getDisplayName(); + } else if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() + .getDisplayNameAlt())) { + displayName = userProfileOverall.getOcs().getData() + .getDisplayNameAlt(); } - loadAvatarImage(); + if ((!TextUtils.isEmpty(displayName) && !displayName.equals( + currentUser.getDisplayName()))) { - ncApi.getUserProfile(credentials, - ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(userProfileOverall -> { - - String displayName = null; - if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() - .getDisplayName())) { - displayName = userProfileOverall.getOcs().getData() - .getDisplayName(); - } else if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData() - .getDisplayNameAlt())) { - displayName = userProfileOverall.getOcs().getData() - .getDisplayNameAlt(); - } - - - if ((!TextUtils.isEmpty(displayName) && !displayName.equals(currentUser.getDisplayName()))) { - - userUtils.createOrUpdateUser(null, - null, - null, displayName, null, null, - null, currentUser.getId(), null, null, null) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(userEntityResult -> { - displayNameTextView.setText(userEntityResult.getDisplayName()); - }, - throwable -> { - }, () -> Log.d(TAG, "")); - - } - }, throwable -> { - }, () -> Log.d(TAG, "")); - - - removeAccountButton.addPreferenceClickListener(view1 -> { - showLovelyDialog(ID_REMOVE_ACCOUNT_WARNING_DIALOG, null); - }); - } - - - if (userUtils.getUsers().size() <= 1) { - switchAccountButton.setVisibility(View.GONE); - } - - if (ApplicationWideMessageHolder.getInstance().getMessageType() != null) { - switch (ApplicationWideMessageHolder.getInstance().getMessageType()) { - case ACCOUNT_UPDATED_NOT_ADDED: - messageText.setTextColor(getResources().getColor(R.color.colorPrimary)); - messageText.setText(getResources().getString(R.string.nc_settings_account_updated)); - messageView.setVisibility(View.VISIBLE); - break; - case SERVER_WITHOUT_TALK: - messageText.setTextColor(getResources().getColor(R.color.nc_darkRed)); - messageText.setText(getResources().getString(R.string.nc_settings_wrong_account)); - messageView.setVisibility(View.VISIBLE); - case ACCOUNT_WAS_IMPORTED: - messageText.setTextColor(getResources().getColor(R.color.colorPrimary)); - messageText.setText(getResources().getString(R.string.nc_Server_account_imported)); - messageView.setVisibility(View.VISIBLE); - break; - case FAILED_TO_IMPORT_ACCOUNT: - messageText.setTextColor(getResources().getColor(R.color.nc_darkRed)); - messageText.setText(getResources().getString(R.string.nc_server_failed_to_import_account)); - messageView.setVisibility(View.VISIBLE); - break; - default: - messageView.setVisibility(View.GONE); - break; + userUtils.createOrUpdateUser(null, + null, + null, displayName, null, null, + null, currentUser.getId(), null, null, null) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(userEntityResult -> { + displayNameTextView.setText(userEntityResult.getDisplayName()); + }, + throwable -> { + }, () -> Log.d(TAG, "")); } - ApplicationWideMessageHolder.getInstance().setMessageType(null); + }, throwable -> { + }, () -> Log.d(TAG, "")); - messageView.animate() - .translationY(0) - .alpha(0.0f) - .setDuration(2500) - .setStartDelay(5000) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - if (messageView != null) { - messageView.setVisibility(View.GONE); - } - } - }); + removeAccountButton.addPreferenceClickListener(view1 -> { + showLovelyDialog(ID_REMOVE_ACCOUNT_WARNING_DIALOG, null); + }); + } - } else { - if (messageView != null) { + if (userUtils.getUsers().size() <= 1) { + switchAccountButton.setVisibility(View.GONE); + } + + if (ApplicationWideMessageHolder.getInstance().getMessageType() != null) { + switch (ApplicationWideMessageHolder.getInstance().getMessageType()) { + case ACCOUNT_UPDATED_NOT_ADDED: + messageText.setTextColor(getResources().getColor(R.color.colorPrimary)); + messageText.setText(getResources().getString(R.string.nc_settings_account_updated)); + messageView.setVisibility(View.VISIBLE); + break; + case SERVER_WITHOUT_TALK: + messageText.setTextColor(getResources().getColor(R.color.nc_darkRed)); + messageText.setText(getResources().getString(R.string.nc_settings_wrong_account)); + messageView.setVisibility(View.VISIBLE); + case ACCOUNT_WAS_IMPORTED: + messageText.setTextColor(getResources().getColor(R.color.colorPrimary)); + messageText.setText(getResources().getString(R.string.nc_Server_account_imported)); + messageView.setVisibility(View.VISIBLE); + break; + case FAILED_TO_IMPORT_ACCOUNT: + messageText.setTextColor(getResources().getColor(R.color.nc_darkRed)); + messageText.setText( + getResources().getString(R.string.nc_server_failed_to_import_account)); + messageView.setVisibility(View.VISIBLE); + break; + default: + messageView.setVisibility(View.GONE); + break; + } + ApplicationWideMessageHolder.getInstance().setMessageType(null); + + messageView.animate() + .translationY(0) + .alpha(0.0f) + .setDuration(2500) + .setStartDelay(5000) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (messageView != null) { messageView.setVisibility(View.GONE); + } } - } + }); + } else { + if (messageView != null) { + messageView.setVisibility(View.GONE); + } + } + } + + private void loadAvatarImage() { + String avatarId; + if (!TextUtils.isEmpty(currentUser.getUserId())) { + avatarId = currentUser.getUserId(); + } else { + avatarId = currentUser.getUsername(); } - private void loadAvatarImage() { - String avatarId; - if (!TextUtils.isEmpty(currentUser.getUserId())) { - avatarId = currentUser.getUserId(); - } else { - avatarId = currentUser.getUsername(); - } + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(avatarImageView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(), + avatarId, R.dimen.avatar_size_big), null)) + .build(); + avatarImageView.setController(draweeController); + } - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(avatarImageView.getController()) - .setAutoPlayAnimations(true) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(), - avatarId, R.dimen.avatar_size_big), null)) - .build(); - avatarImageView.setController(draweeController); + @Override + public void onDestroy() { + if (appPreferences != null) { + appPreferences.unregisterProxyTypeListener(proxyTypeChangeListener); + appPreferences.unregisterProxyCredentialsListener(proxyCredentialsChangeListener); + appPreferences.unregisterScreenSecurityListener(screenSecurityChangeListener); + appPreferences.unregisterScreenLockListener(screenLockChangeListener); + appPreferences.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener); + appPreferences.unregisterThemeChangeListener(themeChangeListener); } + super.onDestroy(); + } + + private void hideProxySettings() { + appPreferences.removeProxyHost(); + appPreferences.removeProxyPort(); + appPreferences.removeProxyCredentials(); + appPreferences.removeProxyUsername(); + appPreferences.removeProxyPassword(); + settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.GONE); + settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.GONE); + settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.GONE); + settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE); + settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE); + } + + private void showProxySettings() { + settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.VISIBLE); + settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.VISIBLE); + settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.VISIBLE); + } + + private void showProxyCredentials() { + settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.VISIBLE); + settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.VISIBLE); + } + + private void hideProxyCredentials() { + appPreferences.removeProxyUsername(); + appPreferences.removeProxyPassword(); + settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE); + settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE); + } + + @Override + public String getTitle() { + return getResources().getString(R.string.nc_settings); + } + + private class ScreenLockTimeoutListener implements OnPreferenceValueChangedListener { @Override - public void onDestroy() { - if (appPreferences != null) { - appPreferences.unregisterProxyTypeListener(proxyTypeChangeListener); - appPreferences.unregisterProxyCredentialsListener(proxyCredentialsChangeListener); - appPreferences.unregisterScreenSecurityListener(screenSecurityChangeListener); - appPreferences.unregisterScreenLockListener(screenLockChangeListener); - appPreferences.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener); - appPreferences.unregisterThemeChangeListener(themeChangeListener); - } - super.onDestroy(); + public void onChanged(String newValue) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + SecurityUtils.createKey(appPreferences.getScreenLockTimeout()); + } } + } - - private void hideProxySettings() { - appPreferences.removeProxyHost(); - appPreferences.removeProxyPort(); - appPreferences.removeProxyCredentials(); - appPreferences.removeProxyUsername(); - appPreferences.removeProxyPassword(); - settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.GONE); - settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.GONE); - settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.GONE); - settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE); - settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE); - } - - private void showProxySettings() { - settingsScreen.findViewById(R.id.settings_proxy_host_edit).setVisibility(View.VISIBLE); - settingsScreen.findViewById(R.id.settings_proxy_port_edit).setVisibility(View.VISIBLE); - settingsScreen.findViewById(R.id.settings_proxy_use_credentials).setVisibility(View.VISIBLE); - } - - private void showProxyCredentials() { - settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.VISIBLE); - settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.VISIBLE); - } - - private void hideProxyCredentials() { - appPreferences.removeProxyUsername(); - appPreferences.removeProxyPassword(); - settingsScreen.findViewById(R.id.settings_proxy_username_edit).setVisibility(View.GONE); - settingsScreen.findViewById(R.id.settings_proxy_password_edit).setVisibility(View.GONE); - } + private class ScreenLockListener implements OnPreferenceValueChangedListener { @Override - public String getTitle() { - return getResources().getString(R.string.nc_settings); + public void onChanged(Boolean newValue) { + screenLockTimeoutChoicePreference.setEnabled(newValue); + + if (newValue) { + screenLockTimeoutChoicePreference.setAlpha(1.0f); + } else { + screenLockTimeoutChoicePreference.setAlpha(0.38f); + } } + } - private class ScreenLockTimeoutListener implements OnPreferenceValueChangedListener { + private class ScreenSecurityChangeListener implements OnPreferenceValueChangedListener { - @Override - public void onChanged(String newValue) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - SecurityUtils.createKey(appPreferences.getScreenLockTimeout()); + @Override + public void onChanged(Boolean newValue) { + if (newValue) { + if (getActivity() != null) { + getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + } else { + if (getActivity() != null) { + getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + } + } + } + + private class ProxyCredentialsChangeListener + implements OnPreferenceValueChangedListener { + + @Override + public void onChanged(Boolean newValue) { + if (newValue) { + showProxyCredentials(); + } else { + hideProxyCredentials(); + } + } + } + + private class ProxyTypeChangeListener implements OnPreferenceValueChangedListener { + + @Override + public void onChanged(String newValue) { + if ("No proxy".equals(newValue)) { + hideProxySettings(); + } else { + switch (newValue) { + case "HTTP": + if (proxyPortEditText != null) { + proxyPortEditText.setValue("3128"); } - } - } - - private class ScreenLockListener implements OnPreferenceValueChangedListener { - - @Override - public void onChanged(Boolean newValue) { - screenLockTimeoutChoicePreference.setEnabled(newValue); - - if (newValue) { - screenLockTimeoutChoicePreference.setAlpha(1.0f); - } else { - screenLockTimeoutChoicePreference.setAlpha(0.38f); + break; + case "DIRECT": + if (proxyPortEditText != null) { + proxyPortEditText.setValue("8080"); } - } - } - - private class ScreenSecurityChangeListener implements OnPreferenceValueChangedListener { - - @Override - public void onChanged(Boolean newValue) { - if (newValue) { - if (getActivity() != null) { - getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - } else { - if (getActivity() != null) { - getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - } + break; + case "SOCKS": + if (proxyPortEditText != null) { + proxyPortEditText.setValue("1080"); } + break; + default: + break; } + + showProxySettings(); + } } + } - private class ProxyCredentialsChangeListener implements OnPreferenceValueChangedListener { - - @Override - public void onChanged(Boolean newValue) { - if (newValue) { - showProxyCredentials(); - } else { - hideProxyCredentials(); - } - } - } - - private class ProxyTypeChangeListener implements OnPreferenceValueChangedListener { - - @Override - public void onChanged(String newValue) { - if ("No proxy".equals(newValue)) { - hideProxySettings(); - } else { - switch (newValue) { - case "HTTP": - if (proxyPortEditText != null) { - proxyPortEditText.setValue("3128"); - } - break; - case "DIRECT": - if (proxyPortEditText != null) { - proxyPortEditText.setValue("8080"); - } - break; - case "SOCKS": - if (proxyPortEditText != null) { - proxyPortEditText.setValue("1080"); - } - break; - default: - break; - } - - showProxySettings(); - } - } - } - - private class ThemeChangeListener implements OnPreferenceValueChangedListener { - @Override - public void onChanged(String newValue) { - NextcloudTalkApplication.Companion.setAppTheme(newValue); - } + private class ThemeChangeListener implements OnPreferenceValueChangedListener { + @Override + public void onChanged(String newValue) { + NextcloudTalkApplication.Companion.setAppTheme(newValue); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java index 6663e301c..7674c4528 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java @@ -37,13 +37,13 @@ import autodagger.AutoInjector; import butterknife.BindView; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.nextcloud.talk.models.ImportAccount; -import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.R; import com.nextcloud.talk.adapters.items.AdvancedUserItem; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; +import com.nextcloud.talk.models.ImportAccount; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.utils.AccountUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -61,189 +61,191 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class SwitchAccountController extends BaseController { - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @BindView(R.id.recyclerView) - RecyclerView recyclerView; + @BindView(R.id.recyclerView) + RecyclerView recyclerView; - @Inject - CookieManager cookieManager; + @Inject + CookieManager cookieManager; - @BindView(R.id.swipe_refresh_layout) - SwipeRefreshLayout swipeRefreshLayout; - private FlexibleAdapter adapter; - private List userItems = new ArrayList<>(); + @BindView(R.id.swipe_refresh_layout) + SwipeRefreshLayout swipeRefreshLayout; + private FlexibleAdapter adapter; + private List userItems = new ArrayList<>(); - private boolean isAccountImport = false; + private boolean isAccountImport = false; - private FlexibleAdapter.OnItemClickListener onImportItemClickListener = new FlexibleAdapter.OnItemClickListener() { + private FlexibleAdapter.OnItemClickListener onImportItemClickListener = + new FlexibleAdapter.OnItemClickListener() { @Override public boolean onItemClick(View view, int position) { - if (userItems.size() > position) { - Account account = ((AdvancedUserItem) userItems.get(position)).getAccount(); - reauthorizeFromImport(account); - } + if (userItems.size() > position) { + Account account = ((AdvancedUserItem) userItems.get(position)).getAccount(); + reauthorizeFromImport(account); + } - return true; + return true; } - }; + }; - private FlexibleAdapter.OnItemClickListener onSwitchItemClickListener = new FlexibleAdapter.OnItemClickListener() { + private FlexibleAdapter.OnItemClickListener onSwitchItemClickListener = + new FlexibleAdapter.OnItemClickListener() { @Override public boolean onItemClick(View view, int position) { - if (userItems.size() > position) { - UserEntity userEntity = ((AdvancedUserItem) userItems.get(position)).getEntity(); - userUtils.createOrUpdateUser(null, - null, null, null, - null, true, null, userEntity.getId(), null, null, null) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + if (userItems.size() > position) { + UserEntity userEntity = ((AdvancedUserItem) userItems.get(position)).getEntity(); + userUtils.createOrUpdateUser(null, + null, null, null, + null, true, null, userEntity.getId(), null, null, null) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - } + } - @Override - public void onNext(UserEntity userEntity) { - cookieManager.getCookieStore().removeAll(); + @Override + public void onNext(UserEntity userEntity) { + cookieManager.getCookieStore().removeAll(); - userUtils.disableAllUsersWithoutId(userEntity.getId()); - if (getActivity() != null) { - getActivity().runOnUiThread(() -> getRouter().popCurrentController()); - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - return true; - } - }; - - public SwitchAccountController() { - setHasOptionsMenu(true); - } - - public SwitchAccountController(Bundle args) { - super(); - setHasOptionsMenu(true); - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT())) { - isAccountImport = true; - } - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - getRouter().popCurrentController(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_generic_rv, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - swipeRefreshLayout.setEnabled(false); - - if (getActionBar() != null) { - getActionBar().show(); - } - - if (adapter == null) { - adapter = new FlexibleAdapter<>(userItems, getActivity(), false); - - UserEntity userEntity; - Participant participant; - - if (!isAccountImport) { - for (Object userEntityObject : userUtils.getUsers()) { - userEntity = (UserEntity) userEntityObject; - if (!userEntity.getCurrent()) { - participant = new Participant(); - participant.setName(userEntity.getDisplayName()); - - String userId; - - if (userEntity.getUserId() != null) { - userId = userEntity.getUserId(); - } else { - userId = userEntity.getUsername(); - } - participant.setUserId(userId); - userItems.add(new AdvancedUserItem(participant, userEntity, null)); + userUtils.disableAllUsersWithoutId(userEntity.getId()); + if (getActivity() != null) { + getActivity().runOnUiThread(() -> getRouter().popCurrentController()); } - } + } - adapter.addListener(onSwitchItemClickListener); - adapter.updateDataSet(userItems, false); + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + return true; + } + }; + + public SwitchAccountController() { + setHasOptionsMenu(true); + } + + public SwitchAccountController(Bundle args) { + super(); + setHasOptionsMenu(true); + + if (args.containsKey(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT())) { + isAccountImport = true; + } + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + getRouter().popCurrentController(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_generic_rv, container, false); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + swipeRefreshLayout.setEnabled(false); + + if (getActionBar() != null) { + getActionBar().show(); + } + + if (adapter == null) { + adapter = new FlexibleAdapter<>(userItems, getActivity(), false); + + UserEntity userEntity; + Participant participant; + + if (!isAccountImport) { + for (Object userEntityObject : userUtils.getUsers()) { + userEntity = (UserEntity) userEntityObject; + if (!userEntity.getCurrent()) { + participant = new Participant(); + participant.setName(userEntity.getDisplayName()); + + String userId; + + if (userEntity.getUserId() != null) { + userId = userEntity.getUserId(); } else { - Account account; - ImportAccount importAccount; - for (Object accountObject : AccountUtils.INSTANCE.findAccounts(userUtils.getUsers())) { - account = (Account) accountObject; - importAccount = AccountUtils.INSTANCE.getInformationFromAccount(account); - - participant = new Participant(); - participant.setName(importAccount.getUsername()); - participant.setUserId(importAccount.getUsername()); - userEntity = new UserEntity(); - userEntity.setBaseUrl(importAccount.getBaseUrl()); - userItems.add(new AdvancedUserItem(participant, userEntity, account)); - } - - adapter.addListener(onImportItemClickListener); - adapter.updateDataSet(userItems, false); + userId = userEntity.getUsername(); } - + participant.setUserId(userId); + userItems.add(new AdvancedUserItem(participant, userEntity, null)); + } } - prepareViews(); + adapter.addListener(onSwitchItemClickListener); + adapter.updateDataSet(userItems, false); + } else { + Account account; + ImportAccount importAccount; + for (Object accountObject : AccountUtils.INSTANCE.findAccounts(userUtils.getUsers())) { + account = (Account) accountObject; + importAccount = AccountUtils.INSTANCE.getInformationFromAccount(account); + + participant = new Participant(); + participant.setName(importAccount.getUsername()); + participant.setUserId(importAccount.getUsername()); + userEntity = new UserEntity(); + userEntity.setBaseUrl(importAccount.getBaseUrl()); + userItems.add(new AdvancedUserItem(participant, userEntity, account)); + } + + adapter.addListener(onImportItemClickListener); + adapter.updateDataSet(userItems, false); + } } + prepareViews(); + } - private void prepareViews() { - LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - recyclerView.setAdapter(adapter); + private void prepareViews() { + LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setHasFixedSize(true); + recyclerView.setAdapter(adapter); - swipeRefreshLayout.setEnabled(false); - } + swipeRefreshLayout.setEnabled(false); + } - private void reauthorizeFromImport(Account account) { - ImportAccount importAccount = AccountUtils.INSTANCE.getInformationFromAccount(account); - Bundle bundle = new Bundle(); - bundle.putString(BundleKeys.INSTANCE.getKEY_BASE_URL(), importAccount.getBaseUrl()); - bundle.putString(BundleKeys.INSTANCE.getKEY_USERNAME(), importAccount.getUsername()); - bundle.putString(BundleKeys.INSTANCE.getKEY_TOKEN(), importAccount.getToken()); - bundle.putBoolean(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT(), true); - getRouter().pushController(RouterTransaction.with(new AccountVerificationController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } + private void reauthorizeFromImport(Account account) { + ImportAccount importAccount = AccountUtils.INSTANCE.getInformationFromAccount(account); + Bundle bundle = new Bundle(); + bundle.putString(BundleKeys.INSTANCE.getKEY_BASE_URL(), importAccount.getBaseUrl()); + bundle.putString(BundleKeys.INSTANCE.getKEY_USERNAME(), importAccount.getUsername()); + bundle.putString(BundleKeys.INSTANCE.getKEY_TOKEN(), importAccount.getToken()); + bundle.putBoolean(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT(), true); + getRouter().pushController(RouterTransaction.with(new AccountVerificationController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } - @Override - public String getTitle() { - return getResources().getString(R.string.nc_select_an_account); - } + @Override + public String getTitle() { + return getResources().getString(R.string.nc_select_an_account); + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java b/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java index 84c701cde..05b18c449 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java @@ -50,12 +50,12 @@ import autodagger.AutoInjector; import butterknife.BindView; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.nextcloud.talk.models.LoginData; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.CertificateEvent; import com.nextcloud.talk.jobs.PushRegistrationWorker; +import com.nextcloud.talk.models.LoginData; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -84,388 +84,400 @@ import org.greenrobot.eventbus.EventBus; @AutoInjector(NextcloudTalkApplication.class) public class WebViewLoginController extends BaseController { - public static final String TAG = "WebViewLoginController"; + public static final String TAG = "WebViewLoginController"; - private final String PROTOCOL_SUFFIX = "://"; - private final String LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":"; + private final String PROTOCOL_SUFFIX = "://"; + private final String LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":"; - @Inject - UserUtils userUtils; - @Inject - AppPreferences appPreferences; - @Inject - ReactiveEntityStore dataStore; - @Inject - MagicTrustManager magicTrustManager; - @Inject - EventBus eventBus; - @Inject - CookieManager cookieManager; + @Inject + UserUtils userUtils; + @Inject + AppPreferences appPreferences; + @Inject + ReactiveEntityStore dataStore; + @Inject + MagicTrustManager magicTrustManager; + @Inject + EventBus eventBus; + @Inject + CookieManager cookieManager; + @BindView(R.id.webview) + WebView webView; - @BindView(R.id.webview) - WebView webView; + @BindView(R.id.progress_bar) + ProgressBar progressBar; - @BindView(R.id.progress_bar) - ProgressBar progressBar; + private String assembledPrefix; - private String assembledPrefix; + private Disposable userQueryDisposable; - private Disposable userQueryDisposable; + private String baseUrl; + private boolean isPasswordUpdate; - private String baseUrl; - private boolean isPasswordUpdate; + private String username; + private String password; + private int loginStep = 0; - private String username; - private String password; - private int loginStep = 0; + private boolean automatedLoginAttempted = false; - private boolean automatedLoginAttempted = false; + private WebViewFidoBridge webViewFidoBridge; - private WebViewFidoBridge webViewFidoBridge; + public WebViewLoginController(String baseUrl, boolean isPasswordUpdate) { + this.baseUrl = baseUrl; + this.isPasswordUpdate = isPasswordUpdate; + } - public WebViewLoginController(String baseUrl, boolean isPasswordUpdate) { - this.baseUrl = baseUrl; - this.isPasswordUpdate = isPasswordUpdate; + public WebViewLoginController(String baseUrl, boolean isPasswordUpdate, String username, + String password) { + this.baseUrl = baseUrl; + this.isPasswordUpdate = isPasswordUpdate; + this.username = username; + this.password = password; + } + + private String getWebLoginUserAgent() { + return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + + Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL + " (" + + getResources().getString(R.string.nc_app_name) + ")"; + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_web_view_login, container, false); + } + + @SuppressLint("SetJavaScriptEnabled") + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + if (getActivity() != null) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } - public WebViewLoginController(String baseUrl, boolean isPasswordUpdate, String username, String password) { - this.baseUrl = baseUrl; - this.isPasswordUpdate = isPasswordUpdate; - this.username = username; - this.password = password; + if (getActionBar() != null) { + getActionBar().hide(); } - private String getWebLoginUserAgent() { - return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + - Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL + " (" - + getResources().getString(R.string.nc_app_name) + ")"; - } + assembledPrefix = + getResources().getString(R.string.nc_talk_login_scheme) + PROTOCOL_SUFFIX + "login/"; - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_web_view_login, container, false); - } + webView.getSettings().setAllowFileAccess(false); + webView.getSettings().setAllowFileAccessFromFileURLs(false); + webView.getSettings().setJavaScriptEnabled(true); + webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false); + webView.getSettings().setDomStorageEnabled(true); + webView.getSettings().setUserAgentString(getWebLoginUserAgent()); + webView.getSettings().setSaveFormData(false); + webView.getSettings().setSavePassword(false); + webView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH); + webView.clearCache(true); + webView.clearFormData(); + webView.clearHistory(); + WebView.clearClientCertPreferences(null); - @SuppressLint("SetJavaScriptEnabled") - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + webViewFidoBridge = + WebViewFidoBridge.createInstanceForWebView((AppCompatActivity) getActivity(), webView); - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + CookieSyncManager.createInstance(getActivity()); + android.webkit.CookieManager.getInstance().removeAllCookies(null); + + Map headers = new HashMap<>(); + headers.put("OCS-APIRequest", "true"); + + webView.setWebViewClient(new WebViewClient() { + private boolean basePageLoaded; + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + webViewFidoBridge.delegateShouldInterceptRequest(view, request); + return super.shouldInterceptRequest(view, request); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + webViewFidoBridge.delegateOnPageStarted(view, url, favicon); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (url.startsWith(assembledPrefix)) { + parseAndLoginFromWebView(url); + return true; + } + return false; + } + + @Override + public void onPageFinished(WebView view, String url) { + loginStep++; + + if (!basePageLoaded) { + if (progressBar != null) { + progressBar.setVisibility(View.GONE); + } + + if (webView != null) { + webView.setVisibility(View.VISIBLE); + } + basePageLoaded = true; } - if (getActionBar() != null) { - getActionBar().hide(); - } - - assembledPrefix = getResources().getString(R.string.nc_talk_login_scheme) + PROTOCOL_SUFFIX + "login/"; - - webView.getSettings().setAllowFileAccess(false); - webView.getSettings().setAllowFileAccessFromFileURLs(false); - webView.getSettings().setJavaScriptEnabled(true); - webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false); - webView.getSettings().setDomStorageEnabled(true); - webView.getSettings().setUserAgentString(getWebLoginUserAgent()); - webView.getSettings().setSaveFormData(false); - webView.getSettings().setSavePassword(false); - webView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH); - webView.clearCache(true); - webView.clearFormData(); - webView.clearHistory(); - WebView.clearClientCertPreferences(null); - - webViewFidoBridge = WebViewFidoBridge.createInstanceForWebView((AppCompatActivity) getActivity(), webView); - - CookieSyncManager.createInstance(getActivity()); - android.webkit.CookieManager.getInstance().removeAllCookies(null); - - Map headers = new HashMap<>(); - headers.put("OCS-APIRequest", "true"); - - webView.setWebViewClient(new WebViewClient() { - private boolean basePageLoaded; - - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - webViewFidoBridge.delegateShouldInterceptRequest(view, request); - return super.shouldInterceptRequest(view, request); - } - - @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - super.onPageStarted(view, url, favicon); - webViewFidoBridge.delegateOnPageStarted(view, url, favicon); - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (url.startsWith(assembledPrefix)) { - parseAndLoginFromWebView(url); - return true; - } - return false; - } - - @Override - public void onPageFinished(WebView view, String url) { - loginStep++; - - if (!basePageLoaded) { - if (progressBar != null) { - progressBar.setVisibility(View.GONE); - } - - if (webView != null) { - webView.setVisibility(View.VISIBLE); - } - basePageLoaded = true; - } - - if (!TextUtils.isEmpty(username) && webView != null) { - if (loginStep == 1) { - webView.loadUrl("javascript: {document.getElementsByClassName('login')[0].click(); };"); - } else if (!automatedLoginAttempted) { - automatedLoginAttempted = true; - if (TextUtils.isEmpty(password)) { - webView.loadUrl("javascript:var justStore = document.getElementById('user').value = '" + username + "';"); - } else { - webView.loadUrl("javascript: {" + - "document.getElementById('user').value = '" + username + "';" + - "document.getElementById('password').value = '" + password + "';" + - "document.getElementById('submit').click(); };"); - } - } - } - - super.onPageFinished(view, url); - } - - @Override - public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { - UserEntity userEntity = userUtils.getCurrentUser(); - - String alias = null; - if (!isPasswordUpdate) { - alias = appPreferences.getTemporaryClientCertAlias(); - } - - if (TextUtils.isEmpty(alias) && (userEntity != null)) { - alias = userEntity.getClientCertificate(); - } - - if (!TextUtils.isEmpty(alias)) { - String finalAlias = alias; - new Thread(() -> { - try { - PrivateKey privateKey = KeyChain.getPrivateKey(getActivity(), finalAlias); - X509Certificate[] certificates = KeyChain.getCertificateChain(getActivity(), finalAlias); - if (privateKey != null && certificates != null) { - request.proceed(privateKey, certificates); - } else { - request.cancel(); - } - } catch (KeyChainException | InterruptedException e) { - request.cancel(); - } - }).start(); - } else { - KeyChain.choosePrivateKeyAlias(getActivity(), chosenAlias -> { - if (chosenAlias != null) { - appPreferences.setTemporaryClientCertAlias(chosenAlias); - new Thread(() -> { - PrivateKey privateKey = null; - try { - privateKey = KeyChain.getPrivateKey(getActivity(), chosenAlias); - X509Certificate[] certificates = KeyChain.getCertificateChain(getActivity(), chosenAlias); - if (privateKey != null && certificates != null) { - request.proceed(privateKey, certificates); - } else { - request.cancel(); - } - } catch (KeyChainException | InterruptedException e) { - request.cancel(); - } - }).start(); - } else { - request.cancel(); - } - }, new String[]{"RSA", "EC"}, null, request.getHost(), request.getPort(), null); - } - } - - @Override - public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - try { - SslCertificate sslCertificate = error.getCertificate(); - Field f = sslCertificate.getClass().getDeclaredField("mX509Certificate"); - f.setAccessible(true); - X509Certificate cert = (X509Certificate) f.get(sslCertificate); - - if (cert == null) { - handler.cancel(); - } else { - try { - magicTrustManager.checkServerTrusted(new X509Certificate[]{cert}, "generic"); - handler.proceed(); - } catch (CertificateException exception) { - eventBus.post(new CertificateEvent(cert, magicTrustManager, handler)); - } - } - } catch (Exception exception) { - handler.cancel(); - } - } - - @Override - public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { - super.onReceivedError(view, errorCode, description, failingUrl); - } - }); - - webView.loadUrl(baseUrl + "/index.php/login/flow", headers); - } - - private void dispose() { - if (userQueryDisposable != null && !userQueryDisposable.isDisposed()) { - userQueryDisposable.dispose(); - } - - userQueryDisposable = null; - } - - private void parseAndLoginFromWebView(String dataString) { - LoginData loginData = parseLoginData(assembledPrefix, dataString); - - if (loginData != null) { - dispose(); - - UserEntity currentUser = userUtils.getCurrentUser(); - - ApplicationWideMessageHolder.MessageType messageType = null; - - if (!isPasswordUpdate && userUtils.getIfUserWithUsernameAndServer(loginData.getUsername(), baseUrl)) { - messageType = ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED; - } - - if (userUtils.checkIfUserIsScheduledForDeletion(loginData.getUsername(), baseUrl)) { - ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION); - - if (!isPasswordUpdate) { - getRouter().popToRoot(); - } else { - getRouter().popCurrentController(); - } - } - - ApplicationWideMessageHolder.MessageType finalMessageType = messageType; - cookieManager.getCookieStore().removeAll(); - - if (!isPasswordUpdate && finalMessageType == null) { - Bundle bundle = new Bundle(); - bundle.putString(BundleKeys.INSTANCE.getKEY_USERNAME(), loginData.getUsername()); - bundle.putString(BundleKeys.INSTANCE.getKEY_TOKEN(), loginData.getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_BASE_URL(), loginData.getServerUrl()); - String protocol = ""; - - if (baseUrl.startsWith("http://")) { - protocol = "http://"; - } else if (baseUrl.startsWith("https://")) { - protocol = "https://"; - } - - if (!TextUtils.isEmpty(protocol)) { - bundle.putString(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL(), protocol); - } - - getRouter().pushController(RouterTransaction.with(new AccountVerificationController - (bundle)).pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); + if (!TextUtils.isEmpty(username) && webView != null) { + if (loginStep == 1) { + webView.loadUrl("javascript: {document.getElementsByClassName('login')[0].click(); };"); + } else if (!automatedLoginAttempted) { + automatedLoginAttempted = true; + if (TextUtils.isEmpty(password)) { + webView.loadUrl("javascript:var justStore = document.getElementById('user').value = '" + + username + + "';"); } else { - if (isPasswordUpdate) { - if (currentUser != null) { - userQueryDisposable = userUtils.createOrUpdateUser(null, loginData.getToken(), - null, null, "", true, - null, currentUser.getId(), null, appPreferences.getTemporaryClientCertAlias(), null) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(userEntity -> { - if (finalMessageType != null) { - ApplicationWideMessageHolder.getInstance().setMessageType(finalMessageType); - } - - OneTimeWorkRequest pushRegistrationWork = new OneTimeWorkRequest.Builder(PushRegistrationWorker.class).build(); - WorkManager.getInstance().enqueue(pushRegistrationWork); - - getRouter().popCurrentController(); - }, throwable -> dispose(), - this::dispose); - } - } else { - if (finalMessageType != null) { - ApplicationWideMessageHolder.getInstance().setMessageType(finalMessageType); - } - getRouter().popToRoot(); - - } + webView.loadUrl("javascript: {" + + "document.getElementById('user').value = '" + username + "';" + + "document.getElementById('password').value = '" + password + "';" + + "document.getElementById('submit').click(); };"); } - } - } - - private LoginData parseLoginData(String prefix, String dataString) { - if (dataString.length() < prefix.length()) { - return null; + } } - LoginData loginData = new LoginData(); + super.onPageFinished(view, url); + } - // format is xxx://login/server:xxx&user:xxx&password:xxx - String data = dataString.substring(prefix.length()); + @Override + public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { + UserEntity userEntity = userUtils.getCurrentUser(); - String[] values = data.split("&"); - - if (values.length != 3) { - return null; + String alias = null; + if (!isPasswordUpdate) { + alias = appPreferences.getTemporaryClientCertAlias(); } - for (String value : values) { - if (value.startsWith("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginData.setUsername(URLDecoder.decode( - value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); - } else if (value.startsWith("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginData.setToken(URLDecoder.decode( - value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); - } else if (value.startsWith("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginData.setServerUrl(URLDecoder.decode( - value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); - } else { - return null; + if (TextUtils.isEmpty(alias) && (userEntity != null)) { + alias = userEntity.getClientCertificate(); + } + + if (!TextUtils.isEmpty(alias)) { + String finalAlias = alias; + new Thread(() -> { + try { + PrivateKey privateKey = KeyChain.getPrivateKey(getActivity(), finalAlias); + X509Certificate[] certificates = + KeyChain.getCertificateChain(getActivity(), finalAlias); + if (privateKey != null && certificates != null) { + request.proceed(privateKey, certificates); + } else { + request.cancel(); + } + } catch (KeyChainException | InterruptedException e) { + request.cancel(); } - } - - if (!TextUtils.isEmpty(loginData.getServerUrl()) && !TextUtils.isEmpty(loginData.getUsername()) && - !TextUtils.isEmpty(loginData.getToken())) { - return loginData; + }).start(); } else { - return null; + KeyChain.choosePrivateKeyAlias(getActivity(), chosenAlias -> { + if (chosenAlias != null) { + appPreferences.setTemporaryClientCertAlias(chosenAlias); + new Thread(() -> { + PrivateKey privateKey = null; + try { + privateKey = KeyChain.getPrivateKey(getActivity(), chosenAlias); + X509Certificate[] certificates = + KeyChain.getCertificateChain(getActivity(), chosenAlias); + if (privateKey != null && certificates != null) { + request.proceed(privateKey, certificates); + } else { + request.cancel(); + } + } catch (KeyChainException | InterruptedException e) { + request.cancel(); + } + }).start(); + } else { + request.cancel(); + } + }, new String[] { "RSA", "EC" }, null, request.getHost(), request.getPort(), null); } + } + + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + try { + SslCertificate sslCertificate = error.getCertificate(); + Field f = sslCertificate.getClass().getDeclaredField("mX509Certificate"); + f.setAccessible(true); + X509Certificate cert = (X509Certificate) f.get(sslCertificate); + + if (cert == null) { + handler.cancel(); + } else { + try { + magicTrustManager.checkServerTrusted(new X509Certificate[] { cert }, "generic"); + handler.proceed(); + } catch (CertificateException exception) { + eventBus.post(new CertificateEvent(cert, magicTrustManager, handler)); + } + } + } catch (Exception exception) { + handler.cancel(); + } + } + + @Override + public void onReceivedError(WebView view, int errorCode, String description, + String failingUrl) { + super.onReceivedError(view, errorCode, description, failingUrl); + } + }); + + webView.loadUrl(baseUrl + "/index.php/login/flow", headers); + } + + private void dispose() { + if (userQueryDisposable != null && !userQueryDisposable.isDisposed()) { + userQueryDisposable.dispose(); } - @Override - public void onDestroy() { - super.onDestroy(); - dispose(); + userQueryDisposable = null; + } + + private void parseAndLoginFromWebView(String dataString) { + LoginData loginData = parseLoginData(assembledPrefix, dataString); + + if (loginData != null) { + dispose(); + + UserEntity currentUser = userUtils.getCurrentUser(); + + ApplicationWideMessageHolder.MessageType messageType = null; + + if (!isPasswordUpdate && userUtils.getIfUserWithUsernameAndServer(loginData.getUsername(), + baseUrl)) { + messageType = ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED; + } + + if (userUtils.checkIfUserIsScheduledForDeletion(loginData.getUsername(), baseUrl)) { + ApplicationWideMessageHolder.getInstance().setMessageType( + ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION); + + if (!isPasswordUpdate) { + getRouter().popToRoot(); + } else { + getRouter().popCurrentController(); + } + } + + ApplicationWideMessageHolder.MessageType finalMessageType = messageType; + cookieManager.getCookieStore().removeAll(); + + if (!isPasswordUpdate && finalMessageType == null) { + Bundle bundle = new Bundle(); + bundle.putString(BundleKeys.INSTANCE.getKEY_USERNAME(), loginData.getUsername()); + bundle.putString(BundleKeys.INSTANCE.getKEY_TOKEN(), loginData.getToken()); + bundle.putString(BundleKeys.INSTANCE.getKEY_BASE_URL(), loginData.getServerUrl()); + String protocol = ""; + + if (baseUrl.startsWith("http://")) { + protocol = "http://"; + } else if (baseUrl.startsWith("https://")) { + protocol = "https://"; + } + + if (!TextUtils.isEmpty(protocol)) { + bundle.putString(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL(), protocol); + } + + getRouter().pushController(RouterTransaction.with(new AccountVerificationController + (bundle)).pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else { + if (isPasswordUpdate) { + if (currentUser != null) { + userQueryDisposable = userUtils.createOrUpdateUser(null, loginData.getToken(), + null, null, "", true, + null, currentUser.getId(), null, appPreferences.getTemporaryClientCertAlias(), null) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .as(AutoDispose.autoDisposable(getScopeProvider())) + .subscribe(userEntity -> { + if (finalMessageType != null) { + ApplicationWideMessageHolder.getInstance().setMessageType(finalMessageType); + } + + OneTimeWorkRequest pushRegistrationWork = + new OneTimeWorkRequest.Builder(PushRegistrationWorker.class).build(); + WorkManager.getInstance().enqueue(pushRegistrationWork); + + getRouter().popCurrentController(); + }, throwable -> dispose(), + this::dispose); + } + } else { + if (finalMessageType != null) { + ApplicationWideMessageHolder.getInstance().setMessageType(finalMessageType); + } + getRouter().popToRoot(); + } + } + } + } + + private LoginData parseLoginData(String prefix, String dataString) { + if (dataString.length() < prefix.length()) { + return null; } - @Override - protected void onDestroyView(@NonNull View view) { - super.onDestroyView(view); - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); - } + LoginData loginData = new LoginData(); + + // format is xxx://login/server:xxx&user:xxx&password:xxx + String data = dataString.substring(prefix.length()); + + String[] values = data.split("&"); + + if (values.length != 3) { + return null; } + + for (String value : values) { + if (value.startsWith("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { + loginData.setUsername(URLDecoder.decode( + value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); + } else if (value.startsWith("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { + loginData.setToken(URLDecoder.decode( + value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); + } else if (value.startsWith("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { + loginData.setServerUrl(URLDecoder.decode( + value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()))); + } else { + return null; + } + } + + if (!TextUtils.isEmpty(loginData.getServerUrl()) + && !TextUtils.isEmpty(loginData.getUsername()) + && + !TextUtils.isEmpty(loginData.getToken())) { + return loginData; + } else { + return null; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + dispose(); + } + + @Override + protected void onDestroyView(@NonNull View view) { + super.onDestroyView(view); + if (getActivity() != null) { + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt b/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt index 8e3924243..a0b1c2695 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt @@ -31,27 +31,33 @@ import com.bluelinelabs.conductor.Controller abstract class ButterKnifeController : Controller { - protected var unbinder: Unbinder? = null + protected var unbinder: Unbinder? = null - constructor() + constructor() constructor(args: Bundle) : super(args) - protected abstract fun inflateView(inflater: LayoutInflater, container: ViewGroup): View + protected abstract fun inflateView( + inflater: LayoutInflater, + container: ViewGroup + ): View - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View { - val view = inflateView(inflater, container) - unbinder = ButterKnife.bind(this, view) - onViewBound(view) - return view - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup + ): View { + val view = inflateView(inflater, container) + unbinder = ButterKnife.bind(this, view) + onViewBound(view) + return view + } - protected open fun onViewBound(view: View) {} + protected open fun onViewBound(view: View) {} - override fun onDestroyView(view: View) { - super.onDestroyView(view) - unbinder!!.unbind() - unbinder = null - } + override fun onDestroyView(view: View) { + super.onDestroyView(view) + unbinder!!.unbind() + unbinder = null + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/providers/ActionBarProvider.java b/app/src/main/java/com/nextcloud/talk/controllers/base/providers/ActionBarProvider.java index ceb393e67..771b70dcb 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/providers/ActionBarProvider.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/base/providers/ActionBarProvider.java @@ -21,5 +21,5 @@ package com.nextcloud.talk.controllers.base.providers; import androidx.appcompat.app.ActionBar; public interface ActionBarProvider { - ActionBar getSupportActionBar(); + ActionBar getSupportActionBar(); } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.java index 13d8fc9fa..1366b594e 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.java @@ -64,266 +64,279 @@ import org.parceler.Parcels; @AutoInjector(NextcloudTalkApplication.class) public class EntryMenuController extends BaseController { - @BindView(R.id.ok_button) - Button proceedButton; + @BindView(R.id.ok_button) + Button proceedButton; - @BindView(R.id.text_edit) - EmojiTextInputEditText editText; + @BindView(R.id.text_edit) + EmojiTextInputEditText editText; - @BindView(R.id.text_input_layout) - TextInputLayout textInputLayout; + @BindView(R.id.text_input_layout) + TextInputLayout textInputLayout; - @BindView(R.id.smileyButton) - ImageView smileyButton; + @BindView(R.id.smileyButton) + ImageView smileyButton; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - private int operationCode; - private Conversation conversation; - private Intent shareIntent; - private String packageName; - private String name; - private String callUrl; + private int operationCode; + private Conversation conversation; + private Intent shareIntent; + private String packageName; + private String name; + private String callUrl; - private EmojiPopup emojiPopup; + private EmojiPopup emojiPopup; + private Bundle originalBundle; - private Bundle originalBundle; + public EntryMenuController(Bundle args) { + super(); + originalBundle = args; - public EntryMenuController(Bundle args) { - super(); - originalBundle = args; - - this.operationCode = args.getInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE()); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_ROOM())) { - this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_SHARE_INTENT())) { - this.shareIntent = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_SHARE_INTENT())); - } - - this.name = args.getString(BundleKeys.INSTANCE.getKEY_APP_ITEM_NAME(), ""); - this.packageName = args.getString(BundleKeys.INSTANCE.getKEY_APP_ITEM_PACKAGE_NAME(), ""); - this.callUrl = args.getString(BundleKeys.INSTANCE.getKEY_CALL_URL(), ""); + this.operationCode = args.getInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE()); + if (args.containsKey(BundleKeys.INSTANCE.getKEY_ROOM())) { + this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_entry_menu, container, false); + if (args.containsKey(BundleKeys.INSTANCE.getKEY_SHARE_INTENT())) { + this.shareIntent = + Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_SHARE_INTENT())); } - @OnClick(R.id.smileyButton) - void onSmileyClick() { - emojiPopup.toggle(); + this.name = args.getString(BundleKeys.INSTANCE.getKEY_APP_ITEM_NAME(), ""); + this.packageName = args.getString(BundleKeys.INSTANCE.getKEY_APP_ITEM_PACKAGE_NAME(), ""); + this.callUrl = args.getString(BundleKeys.INSTANCE.getKEY_CALL_URL(), ""); + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_entry_menu, container, false); + } + + @OnClick(R.id.smileyButton) + void onSmileyClick() { + emojiPopup.toggle(); + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + if (ApplicationWideMessageHolder.getInstance().getMessageType() != null && + ApplicationWideMessageHolder.getInstance() + .getMessageType() + .equals(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG)) { + textInputLayout.setError(getResources().getString(R.string.nc_wrong_password)); + ApplicationWideMessageHolder.getInstance().setMessageType(null); + if (proceedButton.isEnabled()) { + proceedButton.setEnabled(false); + proceedButton.setAlpha(0.7f); + } + } + } + + @OnClick(R.id.ok_button) + public void onProceedButtonClick() { + Bundle bundle; + if (operationCode == 99) { + eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); + bundle = new Bundle(); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); + bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); + bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), + editText.getText().toString()); + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); + if (originalBundle.containsKey(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())) { + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), + originalBundle.getParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())); + } + + getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else if (operationCode != 7 && operationCode != 10 && operationCode != 11) { + eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); + bundle = new Bundle(); + if (operationCode == 4 || operationCode == 6) { + conversation.setPassword(editText.getText().toString()); + } else { + conversation.setName(editText.getText().toString()); + } + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); + getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else if (operationCode == 7) { + if (getActivity() != null) { + shareIntent.putExtra(Intent.EXTRA_TEXT, ShareUtils.getStringForIntent(getActivity(), + editText.getText().toString(), userUtils, conversation)); + Intent intent = new Intent(shareIntent); + intent.setComponent(new ComponentName(packageName, name)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getActivity().startActivity(intent); + eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); + } + } else if (operationCode != 11) { + eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); + bundle = new Bundle(); + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); + bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), editText.getText().toString()); + getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else if (operationCode == 11) { + eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); + originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), + editText.getText().toString()); + getRouter().pushController( + RouterTransaction.with(new OperationsMenuController(originalBundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + if (conversation != null && operationCode == 2) { + editText.setText(conversation.getName()); } - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - if (ApplicationWideMessageHolder.getInstance().getMessageType() != null && - ApplicationWideMessageHolder.getInstance().getMessageType().equals(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG)) { - textInputLayout.setError(getResources().getString(R.string.nc_wrong_password)); - ApplicationWideMessageHolder.getInstance().setMessageType(null); - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.7f); - } - } - } + editText.setOnEditorActionListener((v, actionId, event) -> { + if (actionId == EditorInfo.IME_ACTION_DONE + && proceedButton != null + && proceedButton.isEnabled()) { + proceedButton.callOnClick(); + return true; + } + return false; + }); - @OnClick(R.id.ok_button) - public void onProceedButtonClick() { - Bundle bundle; - if (operationCode == 99) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), editText.getText().toString()); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); - if (originalBundle.containsKey(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())) { - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), originalBundle.getParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())); - } + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { - getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (operationCode != 7 && operationCode != 10 && operationCode != 11) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - bundle = new Bundle(); - if (operationCode == 4 || operationCode == 6) { - conversation.setPassword(editText.getText().toString()); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (!TextUtils.isEmpty(s)) { + if (operationCode == 2) { + if (conversation.getName() == null || !conversation.getName().equals(s.toString())) { + if (!proceedButton.isEnabled()) { + proceedButton.setEnabled(true); + proceedButton.setAlpha(1.0f); + } + textInputLayout.setErrorEnabled(false); } else { - conversation.setName(editText.getText().toString()); + if (proceedButton.isEnabled()) { + proceedButton.setEnabled(false); + proceedButton.setAlpha(0.38f); + } + textInputLayout.setError(getResources().getString(R.string.nc_call_name_is_same)); } - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); - getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (operationCode == 7) { - if (getActivity() != null) { - shareIntent.putExtra(Intent.EXTRA_TEXT, ShareUtils.getStringForIntent(getActivity(), - editText.getText().toString(), userUtils, conversation)); - Intent intent = new Intent(shareIntent); - intent.setComponent(new ComponentName(packageName, name)); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getActivity().startActivity(intent); - eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); + } else if (operationCode != 10) { + if (!proceedButton.isEnabled()) { + proceedButton.setEnabled(true); + proceedButton.setAlpha(1.0f); } - } else if (operationCode != 11) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - bundle = new Bundle(); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); - bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), editText.getText().toString()); - getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - - } else if (operationCode == 11) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), editText.getText().toString()); - getRouter().pushController(RouterTransaction.with(new OperationsMenuController(originalBundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - + textInputLayout.setErrorEnabled(false); + } else if ((editText.getText().toString().startsWith("http://") || + editText.getText().toString().startsWith("https://")) && + editText.getText().toString().contains("/call/")) { + // operation code 10 + if (!proceedButton.isEnabled()) { + proceedButton.setEnabled(true); + proceedButton.setAlpha(1.0f); + } + textInputLayout.setErrorEnabled(false); + } else { + if (proceedButton.isEnabled()) { + proceedButton.setEnabled(false); + proceedButton.setAlpha(0.38f); + } + textInputLayout.setError(getResources().getString(R.string.nc_wrong_link)); + } + } else { + if (proceedButton.isEnabled()) { + proceedButton.setEnabled(false); + proceedButton.setAlpha(0.38f); + } + textInputLayout.setErrorEnabled(false); } - } + } + }); - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - if (conversation != null && operationCode == 2) { - editText.setText(conversation.getName()); - } - - editText.setOnEditorActionListener((v, actionId, event) -> { - if (actionId == EditorInfo.IME_ACTION_DONE && proceedButton != null && proceedButton.isEnabled()) { - proceedButton.callOnClick(); - return true; - } - return false; - }); - - editText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - - @Override - public void afterTextChanged(Editable s) { - if (!TextUtils.isEmpty(s)) { - if (operationCode == 2) { - if (conversation.getName() == null || !conversation.getName().equals(s.toString())) { - if (!proceedButton.isEnabled()) { - proceedButton.setEnabled(true); - proceedButton.setAlpha(1.0f); - } - textInputLayout.setErrorEnabled(false); - } else { - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.38f); - } - textInputLayout.setError(getResources().getString(R.string.nc_call_name_is_same)); - } - } else if (operationCode != 10) { - if (!proceedButton.isEnabled()) { - proceedButton.setEnabled(true); - proceedButton.setAlpha(1.0f); - } - textInputLayout.setErrorEnabled(false); - } else if ((editText.getText().toString().startsWith("http://") || - editText.getText().toString().startsWith("https://")) && - editText.getText().toString().contains("/call/")) { - // operation code 10 - if (!proceedButton.isEnabled()) { - proceedButton.setEnabled(true); - proceedButton.setAlpha(1.0f); - } - textInputLayout.setErrorEnabled(false); - } else { - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.38f); - } - textInputLayout.setError(getResources().getString(R.string.nc_wrong_link)); - } - } else { - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.38f); - } - textInputLayout.setErrorEnabled(false); + String labelText = ""; + switch (operationCode) { + case 11: + case 2: + labelText = getResources().getString(R.string.nc_call_name); + editText.setInputType(InputType.TYPE_CLASS_TEXT); + smileyButton.setVisibility(View.VISIBLE); + emojiPopup = EmojiPopup.Builder.fromRootView(view) + .setOnEmojiPopupShownListener(new OnEmojiPopupShownListener() { + @Override + public void onEmojiPopupShown() { + if (getResources() != null) { + smileyButton.setColorFilter(getResources().getColor(R.color.colorPrimary), + PorterDuff.Mode.SRC_IN); } - } - }); + } + }) + .setOnEmojiPopupDismissListener(new OnEmojiPopupDismissListener() { + @Override + public void onEmojiPopupDismiss() { + if (smileyButton != null) { + smileyButton.setColorFilter(getResources().getColor(R.color.emoji_icons), + PorterDuff.Mode.SRC_IN); + } + } + }) + .setOnEmojiClickListener(new OnEmojiClickListener() { + @Override + public void onEmojiClick(@NonNull EmojiImageView emoji, @NonNull Emoji imageView) { + editText.getEditableText().append(" "); + } + }) + .build(editText); - String labelText = ""; - switch (operationCode) { - case 11: - case 2: - labelText = getResources().getString(R.string.nc_call_name); - editText.setInputType(InputType.TYPE_CLASS_TEXT); - smileyButton.setVisibility(View.VISIBLE); - emojiPopup = EmojiPopup.Builder.fromRootView(view).setOnEmojiPopupShownListener(new OnEmojiPopupShownListener() { - @Override - public void onEmojiPopupShown() { - if (getResources() != null) { - smileyButton.setColorFilter(getResources().getColor(R.color.colorPrimary), - PorterDuff.Mode.SRC_IN); - } - } - }).setOnEmojiPopupDismissListener(new OnEmojiPopupDismissListener() { - @Override - public void onEmojiPopupDismiss() { - if (smileyButton != null) { - smileyButton.setColorFilter(getResources().getColor(R.color.emoji_icons), - PorterDuff.Mode.SRC_IN); - } - } - }).setOnEmojiClickListener(new OnEmojiClickListener() { - @Override - public void onEmojiClick(@NonNull EmojiImageView emoji, @NonNull Emoji imageView) { - editText.getEditableText().append(" "); - } - }).build(editText); - - break; - case 4: - labelText = getResources().getString(R.string.nc_new_password); - editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - break; - case 6: - case 7: - case 99: - // 99 is joining a conversation via password - labelText = getResources().getString(R.string.nc_password); - editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - break; - case 10: - labelText = getResources().getString(R.string.nc_conversation_link); - editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); - break; - default: - break; - } - - textInputLayout.setPasswordVisibilityToggleEnabled(operationCode == 99 || operationCode == 4 || operationCode == 6 || operationCode == 7); - textInputLayout.setHint(labelText); - textInputLayout.requestFocus(); + break; + case 4: + labelText = getResources().getString(R.string.nc_new_password); + editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + break; + case 6: + case 7: + case 99: + // 99 is joining a conversation via password + labelText = getResources().getString(R.string.nc_password); + editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + break; + case 10: + labelText = getResources().getString(R.string.nc_conversation_link); + editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); + break; + default: + break; } + + textInputLayout.setPasswordVisibilityToggleEnabled( + operationCode == 99 || operationCode == 4 || operationCode == 6 || operationCode == 7); + textInputLayout.setHint(labelText); + textInputLayout.requestFocus(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index 00f5ec6b3..8cd477b99 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -42,20 +42,20 @@ import butterknife.BindView; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.RetrofitBucket; -import com.nextcloud.talk.models.json.capabilities.Capabilities; -import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.conversations.RoomOverall; -import com.nextcloud.talk.models.json.generic.GenericOverall; -import com.nextcloud.talk.models.json.participants.AddParticipantOverall; import com.nextcloud.talk.R; import com.nextcloud.talk.activities.MagicCallActivity; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.BottomSheetLockEvent; +import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.capabilities.Capabilities; +import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; +import com.nextcloud.talk.models.json.conversations.Conversation; +import com.nextcloud.talk.models.json.conversations.RoomOverall; +import com.nextcloud.talk.models.json.generic.GenericOverall; +import com.nextcloud.talk.models.json.participants.AddParticipantOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ConductorRemapping; import com.nextcloud.talk.utils.DisplayUtils; @@ -76,662 +76,683 @@ import retrofit2.HttpException; @AutoInjector(NextcloudTalkApplication.class) public class OperationsMenuController extends BaseController { - @BindView(R.id.progress_bar) - ProgressBar progressBar; + @BindView(R.id.progress_bar) + ProgressBar progressBar; - @BindView(R.id.result_image_view) - ImageView resultImageView; + @BindView(R.id.result_image_view) + ImageView resultImageView; - @BindView(R.id.result_text_view) - TextView resultsTextView; + @BindView(R.id.result_text_view) + TextView resultsTextView; - @BindView(R.id.ok_button) - Button okButton; + @BindView(R.id.ok_button) + Button okButton; - @BindView(R.id.web_button) - Button webButton; + @BindView(R.id.web_button) + Button webButton; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - private int operationCode; - private Conversation conversation; + private int operationCode; + private Conversation conversation; - private UserEntity currentUser; - private String callPassword; - private String callUrl; + private UserEntity currentUser; + private String callPassword; + private String callUrl; - private String baseUrl; - private String conversationToken; + private String baseUrl; + private String conversationToken; - private Disposable disposable; + private Disposable disposable; - private Conversation.ConversationType conversationType; - private ArrayList invitedUsers = new ArrayList<>(); - private ArrayList invitedGroups = new ArrayList<>(); + private Conversation.ConversationType conversationType; + private ArrayList invitedUsers = new ArrayList<>(); + private ArrayList invitedGroups = new ArrayList<>(); - private Capabilities serverCapabilities; - private String credentials; - private String conversationName; - - public OperationsMenuController(Bundle args) { - super(); - this.operationCode = args.getInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE()); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_ROOM())) { - this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - } - - this.callPassword = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), ""); - this.callUrl = args.getString(BundleKeys.INSTANCE.getKEY_CALL_URL(), ""); - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS())) { - this.invitedUsers = args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS()); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_GROUP())) { - this.invitedGroups = args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_GROUP()); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())) { - this.conversationType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())) { - this.serverCapabilities = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())); - } - - this.conversationName = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), ""); + private Capabilities serverCapabilities; + private String credentials; + private String conversationName; + public OperationsMenuController(Bundle args) { + super(); + this.operationCode = args.getInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE()); + if (args.containsKey(BundleKeys.INSTANCE.getKEY_ROOM())) { + this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); } - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_operations_menu, container, false); + this.callPassword = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), ""); + this.callUrl = args.getString(BundleKeys.INSTANCE.getKEY_CALL_URL(), ""); + + if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS())) { + this.invitedUsers = + args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS()); } - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - processOperation(); + if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_GROUP())) { + this.invitedGroups = args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_GROUP()); } - private void processOperation() { - currentUser = userUtils.getCurrentUser(); - OperationsObserver operationsObserver = new OperationsObserver(); + if (args.containsKey(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())) { + this.conversationType = + Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())); + } - if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) { - conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1); - if (callUrl.contains("/index.php")) { - baseUrl = callUrl.substring(0, callUrl.indexOf("/index.php")); - } else { - baseUrl = callUrl.substring(0, callUrl.indexOf("/call")); - } - } + if (args.containsKey(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())) { + this.serverCapabilities = + Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())); + } - if (currentUser != null) { - credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); + this.conversationName = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), ""); + } - if (!TextUtils.isEmpty(baseUrl) && !baseUrl.equals(currentUser.getBaseUrl())) { - credentials = null; + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_operations_menu, container, false); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + processOperation(); + } + + private void processOperation() { + currentUser = userUtils.getCurrentUser(); + OperationsObserver operationsObserver = new OperationsObserver(); + + if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) { + conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1); + if (callUrl.contains("/index.php")) { + baseUrl = callUrl.substring(0, callUrl.indexOf("/index.php")); + } else { + baseUrl = callUrl.substring(0, callUrl.indexOf("/call")); + } + } + + if (currentUser != null) { + credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); + + if (!TextUtils.isEmpty(baseUrl) && !baseUrl.equals(currentUser.getBaseUrl())) { + credentials = null; + } + + switch (operationCode) { + case 2: + ncApi.renameRoom(credentials, + ApiUtils.getRoom(currentUser.getBaseUrl(), conversation.getToken()), + conversation.getName()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 3: + ncApi.makeRoomPublic(credentials, + ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation + .getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 4: + case 5: + case 6: + String pass = ""; + if (conversation.getPassword() != null) { + pass = conversation.getPassword(); + } + ncApi.setPassword(credentials, ApiUtils.getUrlForPassword(currentUser.getBaseUrl(), + conversation.getToken()), pass) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 7: + // Operation 7 is sharing, so we handle this differently + break; + case 8: + ncApi.makeRoomPrivate(credentials, + ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation + .getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 10: + ncApi.getRoom(credentials, ApiUtils.getRoom(baseUrl, conversationToken)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + disposable = d; + } + + @Override + public void onNext(RoomOverall roomOverall) { + conversation = roomOverall.getOcs().getData(); + fetchCapabilities(credentials); + } + + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } + + @Override + public void onComplete() { + dispose(); + } + }); + break; + case 11: + RetrofitBucket retrofitBucket; + boolean isGroupCallWorkaround = false; + String invite = null; + + if (invitedGroups.size() > 0) { + invite = invitedGroups.get(0); + } + + if (conversationType.equals(Conversation.ConversationType.ROOM_PUBLIC_CALL) || + !currentUser.hasSpreedFeatureCapability("empty-group-room")) { + retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), + "3", invite, conversationName); + } else { + String roomType = "2"; + if (!currentUser.hasSpreedFeatureCapability("empty-group-room")) { + isGroupCallWorkaround = true; + roomType = "3"; } - switch (operationCode) { - case 2: - ncApi.renameRoom(credentials, ApiUtils.getRoom(currentUser.getBaseUrl(), conversation.getToken()), - conversation.getName()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 3: - ncApi.makeRoomPublic(credentials, ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation - .getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 4: - case 5: - case 6: - String pass = ""; - if (conversation.getPassword() != null) { - pass = conversation.getPassword(); - } - ncApi.setPassword(credentials, ApiUtils.getUrlForPassword(currentUser.getBaseUrl(), - conversation.getToken()), pass) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 7: - // Operation 7 is sharing, so we handle this differently - break; - case 8: - ncApi.makeRoomPrivate(credentials, ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation - .getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 10: - ncApi.getRoom(credentials, ApiUtils.getRoom(baseUrl, conversationToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - disposable = d; - } + retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), + roomType, invite, conversationName); + } - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - fetchCapabilities(credentials); - } + final boolean isGroupCallWorkaroundFinal = isGroupCallWorkaround; + ncApi.createRoom(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } + } - @Override - public void onComplete() { - dispose(); - } - }); - break; - case 11: - RetrofitBucket retrofitBucket; - boolean isGroupCallWorkaround = false; - String invite = null; + @Override + public void onNext(RoomOverall roomOverall) { + conversation = roomOverall.getOcs().getData(); - if (invitedGroups.size() > 0) { - invite = invitedGroups.get(0); - } + ncApi.getRoom(credentials, + ApiUtils.getRoom(currentUser.getBaseUrl(), conversation.getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - if (conversationType.equals(Conversation.ConversationType.ROOM_PUBLIC_CALL) || - !currentUser.hasSpreedFeatureCapability("empty-group-room")) { - retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), - "3", invite, conversationName); - } else { - String roomType = "2"; - if (!currentUser.hasSpreedFeatureCapability("empty-group-room")) { - isGroupCallWorkaround = true; - roomType = "3"; } - retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), - roomType, invite, conversationName); - } + @Override + public void onNext(RoomOverall roomOverall) { + conversation = roomOverall.getOcs().getData(); + if (conversationType.equals( + Conversation.ConversationType.ROOM_PUBLIC_CALL) + && isGroupCallWorkaroundFinal) { + performGroupCallWorkaround(credentials); + } else { + inviteUsersToAConversation(); + } + } - final boolean isGroupCallWorkaroundFinal = isGroupCallWorkaround; - ncApi.createRoom(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } - } + @Override + public void onComplete() { - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); + } + }); + } - ncApi.getRoom(credentials, - ApiUtils.getRoom(currentUser.getBaseUrl(), conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } - } + @Override + public void onComplete() { + dispose(); + } + }); - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - if (conversationType.equals(Conversation.ConversationType.ROOM_PUBLIC_CALL) && isGroupCallWorkaroundFinal) { - performGroupCallWorkaround(credentials); - } else { - inviteUsersToAConversation(); - } - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - - } - }); - - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - - break; - case 97: - case 98: - if (operationCode == 97) { - ncApi.removeConversationFromFavorites(credentials, ApiUtils.getUrlForConversationFavorites(currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - } else { - ncApi.addConversationToFavorites(credentials, ApiUtils.getUrlForConversationFavorites(currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - } - break; - case 99: - ncApi.joinRoom(credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, conversationToken), - callPassword) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - default: - break; - } - } - } - - private void performGroupCallWorkaround(String credentials) { - ncApi.makeRoomPrivate(credentials, ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation.getToken())) + break; + case 97: + case 98: + if (operationCode == 97) { + ncApi.removeConversationFromFavorites(credentials, + ApiUtils.getUrlForConversationFavorites(currentUser.getBaseUrl(), + conversation.getToken())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + .subscribe(operationsObserver); + } else { + ncApi.addConversationToFavorites(credentials, + ApiUtils.getUrlForConversationFavorites(currentUser.getBaseUrl(), + conversation.getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + } + break; + case 99: + ncApi.joinRoom(credentials, + ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, conversationToken), + callPassword) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + default: + break; + } + } + } - } + private void performGroupCallWorkaround(String credentials) { + ncApi.makeRoomPrivate(credentials, + ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation.getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - @Override - public void onNext(GenericOverall genericOverall) { - inviteUsersToAConversation(); - } + } - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } + @Override + public void onNext(GenericOverall genericOverall) { + inviteUsersToAConversation(); + } - @Override - public void onComplete() { - dispose(); - } - }); + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } + @Override + public void onComplete() { + dispose(); + } + }); + } + private void showResultImage(boolean everythingOK, boolean isGuestSupportError) { + progressBar.setVisibility(View.GONE); + + if (everythingOK) { + resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable + .ic_check_circle_black_24dp, R.color.nc_darkGreen)); + } else { + resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable + .ic_cancel_black_24dp, R.color.nc_darkRed)); } - private void showResultImage(boolean everythingOK, boolean isGuestSupportError) { - progressBar.setVisibility(View.GONE); + resultImageView.setVisibility(View.VISIBLE); - if (everythingOK) { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable - .ic_check_circle_black_24dp, R.color.nc_darkGreen)); - } else { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable - .ic_cancel_black_24dp, R.color.nc_darkRed)); - } + if (everythingOK) { + resultsTextView.setText(R.string.nc_all_ok_operation); + } else { + resultsTextView.setTextColor(getResources().getColor(R.color.nc_darkRed)); + if (!isGuestSupportError) { + resultsTextView.setText(R.string.nc_failed_to_perform_operation); + } else { + resultsTextView.setText(R.string.nc_failed_signaling_settings); + webButton.setOnClickListener(v -> { + eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(callUrl)); + startActivity(browserIntent); + new BottomSheetLockEvent(true, 0, false, true); + }); + webButton.setVisibility(View.VISIBLE); + } + } - resultImageView.setVisibility(View.VISIBLE); + resultsTextView.setVisibility(View.VISIBLE); + if (everythingOK) { + eventBus.post(new BottomSheetLockEvent(true, 2500, true, true)); + } else { + resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable + .ic_cancel_black_24dp, R.color.nc_darkRed)); + okButton.setOnClickListener( + v -> eventBus.post(new BottomSheetLockEvent(true, 0, operationCode != 99 + && operationCode != 10, true))); + okButton.setVisibility(View.VISIBLE); + } + } - if (everythingOK) { - resultsTextView.setText(R.string.nc_all_ok_operation); - } else { - resultsTextView.setTextColor(getResources().getColor(R.color.nc_darkRed)); - if (!isGuestSupportError) { - resultsTextView.setText(R.string.nc_failed_to_perform_operation); + private void dispose() { + if (disposable != null && !disposable.isDisposed()) { + disposable.dispose(); + } + + disposable = null; + } + + @Override + public void onDestroy() { + super.onDestroy(); + dispose(); + } + + private void fetchCapabilities(String credentials) { + ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(CapabilitiesOverall capabilitiesOverall) { + if (capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() != null && + capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures() != null && capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures().contains("chat-v2")) { + if (conversation.isHasPassword() && conversation.isGuest()) { + eventBus.post(new BottomSheetLockEvent(true, 0, + true, false)); + Bundle bundle = new Bundle(); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); + bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), + Parcels.wrap(capabilitiesOverall.getOcs().getData().getCapabilities())); + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); + getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else { + initiateConversation(false, capabilitiesOverall.getOcs().getData() + .getCapabilities()); + } + } else if (capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() != null && + capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures() != null && capabilitiesOverall.getOcs().getData() + .getCapabilities().getSpreedCapability() + .getFeatures().contains("guest-signaling")) { + initiateCall(); } else { - resultsTextView.setText(R.string.nc_failed_signaling_settings); - webButton.setOnClickListener(v -> { - eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(callUrl)); - startActivity(browserIntent); - new BottomSheetLockEvent(true, 0, false, true); - }); - webButton.setVisibility(View.VISIBLE); + showResultImage(false, true); } - } + } - resultsTextView.setVisibility(View.VISIBLE); - if (everythingOK) { - eventBus.post(new BottomSheetLockEvent(true, 2500, true, true)); - } else { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable - .ic_cancel_black_24dp, R.color.nc_darkRed)); - okButton.setOnClickListener(v -> eventBus.post(new BottomSheetLockEvent(true, 0, operationCode != 99 - && operationCode != 10, true))); - okButton.setVisibility(View.VISIBLE); - } + @Override + public void onError(Throwable e) { + showResultImage(false, false); + } + + @Override + public void onComplete() { + + } + }); + } + + private void inviteUsersToAConversation() { + RetrofitBucket retrofitBucket; + final ArrayList localInvitedUsers = invitedUsers; + final ArrayList localInvitedGroups = invitedGroups; + if (localInvitedGroups.size() > 0) { + localInvitedGroups.remove(0); } - private void dispose() { - if (disposable != null && !disposable.isDisposed()) { - disposable.dispose(); - } + if (localInvitedUsers.size() > 0 || (localInvitedGroups.size() > 0 + && currentUser.hasSpreedFeatureCapability("invite-groups-and-mails"))) { + if ((localInvitedGroups.size() > 0 && currentUser.hasSpreedFeatureCapability( + "invite-groups-and-mails"))) { + for (int i = 0; i < localInvitedGroups.size(); i++) { + final String groupId = localInvitedGroups.get(i); + retrofitBucket = + ApiUtils.getRetrofitBucketForAddGroupParticipant(currentUser.getBaseUrl(), + conversation.getToken(), + groupId); - disposable = null; + ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(AddParticipantOverall addParticipantOverall) { + } + + @Override + public void onError(Throwable e) { + dispose(); + } + + @Override + public void onComplete() { + synchronized (localInvitedGroups) { + localInvitedGroups.remove(groupId); + } + + if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { + initiateConversation(true, null); + } + dispose(); + } + }); + } + } + + for (int i = 0; i < localInvitedUsers.size(); i++) { + final String userId = invitedUsers.get(i); + retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(currentUser.getBaseUrl(), + conversation.getToken(), + userId); + + ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(AddParticipantOverall addParticipantOverall) { + } + + @Override + public void onError(Throwable e) { + dispose(); + } + + @Override + public void onComplete() { + synchronized (localInvitedUsers) { + localInvitedUsers.remove(userId); + } + + if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { + initiateConversation(true, null); + } + dispose(); + } + }); + } + } else { + if (!currentUser.hasSpreedFeatureCapability("chat-v2")) { + showResultImage(true, false); + } else { + initiateConversation(true, null); + } + } + } + + private void initiateConversation(boolean dismissView, @Nullable Capabilities capabilities) { + Bundle bundle = new Bundle(); + boolean isGuestUser = false; + boolean hasChatCapability; + + if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { + isGuestUser = true; + hasChatCapability = capabilities != null + && capabilities.getSpreedCapability() != null + && capabilities.getSpreedCapability().getFeatures() != null + && capabilities.getSpreedCapability().getFeatures().contains("chat-v2"); + } else { + hasChatCapability = currentUser.hasSpreedFeatureCapability("chat-v2"); + } + + if (hasChatCapability) { + eventBus.post(new BottomSheetLockEvent(true, 0, + true, true, dismissView)); + + Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); + bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), + conversation.getDisplayName()); + UserEntity conversationUser; + if (isGuestUser) { + conversationUser = new UserEntity(); + conversationUser.setBaseUrl(baseUrl); + conversationUser.setUserId("?"); + try { + conversationUser.setCapabilities(LoganSquare.serialize(capabilities)); + } catch (IOException e) { + Log.e("OperationsMenu", "Failed to serialize capabilities"); + } + } else { + conversationUser = currentUser; + } + + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), conversationUser); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), + Parcels.wrap(conversation)); + bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), callPassword); + + conversationIntent.putExtras(bundle); + + if (getParentController() != null) { + ConductorRemapping.INSTANCE.remapChatController(getParentController().getRouter(), + conversationUser.getId(), + conversation.getToken(), bundle, true); + } + } else { + initiateCall(); + } + } + + private void initiateCall() { + eventBus.post(new BottomSheetLockEvent(true, 0, true, true)); + Bundle bundle = new Bundle(); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); + if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { + bundle.putString(BundleKeys.INSTANCE.getKEY_MODIFIED_BASE_URL(), baseUrl); + } + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), + Parcels.wrap(conversation)); + + if (getActivity() != null) { + + Intent callIntent = new Intent(getActivity(), MagicCallActivity.class); + callIntent.putExtras(bundle); + + InputMethodManager imm = + (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); + } + + new Handler().postDelayed(() -> getParentController().getRouter().popCurrentController(), + 100); + startActivity(callIntent); + } + } + + private class OperationsObserver implements Observer { + + @Override + public void onSubscribe(Disposable d) { + disposable = d; } @Override - public void onDestroy() { - super.onDestroy(); - dispose(); + public void onNext(Object o) { + if (operationCode != 99) { + showResultImage(true, false); + } else { + RoomOverall roomOverall = (RoomOverall) o; + conversation = roomOverall.getOcs().getData(); + initiateConversation(true, serverCapabilities); + } } - private void fetchCapabilities(String credentials) { - ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(CapabilitiesOverall capabilitiesOverall) { - if (capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() != null && - capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures().contains("chat-v2")) { - if (conversation.isHasPassword() && conversation.isGuest()) { - eventBus.post(new BottomSheetLockEvent(true, 0, - true, false)); - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), - Parcels.wrap(capabilitiesOverall.getOcs().getData().getCapabilities())); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); - getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - initiateConversation(false, capabilitiesOverall.getOcs().getData() - .getCapabilities()); - } - } else if (capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() != null && - capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures().contains("guest-signaling")) { - initiateCall(); - } else { - showResultImage(false, true); - } - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - } - - @Override - public void onComplete() { - - } - }); - } - - private void inviteUsersToAConversation() { - RetrofitBucket retrofitBucket; - final ArrayList localInvitedUsers = invitedUsers; - final ArrayList localInvitedGroups = invitedGroups; - if (localInvitedGroups.size() > 0) { - localInvitedGroups.remove(0); - } - - if (localInvitedUsers.size() > 0 || (localInvitedGroups.size() > 0 && currentUser.hasSpreedFeatureCapability("invite-groups-and-mails"))) { - if ((localInvitedGroups.size() > 0 && currentUser.hasSpreedFeatureCapability("invite-groups-and-mails"))) { - for (int i = 0; i < localInvitedGroups.size(); i++) { - final String groupId = localInvitedGroups.get(i); - retrofitBucket = ApiUtils.getRetrofitBucketForAddGroupParticipant(currentUser.getBaseUrl(), conversation.getToken(), - groupId); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(AddParticipantOverall addParticipantOverall) { - } - - @Override - public void onError(Throwable e) { - dispose(); - } - - @Override - public void onComplete() { - synchronized (localInvitedGroups) { - localInvitedGroups.remove(groupId); - } - - if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(true, null); - } - dispose(); - } - }); - - } - } - - for (int i = 0; i < localInvitedUsers.size(); i++) { - final String userId = invitedUsers.get(i); - retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(currentUser.getBaseUrl(), conversation.getToken(), - userId); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(AddParticipantOverall addParticipantOverall) { - } - - @Override - public void onError(Throwable e) { - dispose(); - } - - @Override - public void onComplete() { - synchronized (localInvitedUsers) { - localInvitedUsers.remove(userId); - } - - if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(true, null); - } - dispose(); - } - }); - } + @Override + public void onError(Throwable e) { + if (operationCode != 99 || !(e instanceof HttpException)) { + showResultImage(false, false); + } else { + if (((HttpException) e).response().code() == 403) { + eventBus.post(new BottomSheetLockEvent(true, 0, false, + false)); + ApplicationWideMessageHolder.getInstance() + .setMessageType(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG); + getRouter().popCurrentController(); } else { - if (!currentUser.hasSpreedFeatureCapability("chat-v2")) { - showResultImage(true, false); - } else { - initiateConversation(true, null); - } + showResultImage(false, false); } + } + dispose(); } - private void initiateConversation(boolean dismissView, @Nullable Capabilities capabilities) { - Bundle bundle = new Bundle(); - boolean isGuestUser = false; - boolean hasChatCapability; - - if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { - isGuestUser = true; - hasChatCapability = capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getFeatures() != null && capabilities.getSpreedCapability().getFeatures().contains("chat-v2"); - } else { - hasChatCapability = currentUser.hasSpreedFeatureCapability("chat-v2"); - } - - - if (hasChatCapability) { - eventBus.post(new BottomSheetLockEvent(true, 0, - true, true, dismissView)); - - Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), conversation.getDisplayName()); - UserEntity conversationUser; - if (isGuestUser) { - conversationUser = new UserEntity(); - conversationUser.setBaseUrl(baseUrl); - conversationUser.setUserId("?"); - try { - conversationUser.setCapabilities(LoganSquare.serialize(capabilities)); - } catch (IOException e) { - Log.e("OperationsMenu", "Failed to serialize capabilities"); - } - } else { - conversationUser = currentUser; - } - - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), conversationUser); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), callPassword); - - conversationIntent.putExtras(bundle); - - if (getParentController() != null) { - ConductorRemapping.INSTANCE.remapChatController(getParentController().getRouter(), conversationUser.getId(), - conversation.getToken(), bundle, true); - } - } else { - initiateCall(); - } - } - - - private void initiateCall() { - eventBus.post(new BottomSheetLockEvent(true, 0, true, true)); - Bundle bundle = new Bundle(); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); - if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { - bundle.putString(BundleKeys.INSTANCE.getKEY_MODIFIED_BASE_URL(), baseUrl); - } - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation)); - - if (getActivity() != null) { - - Intent callIntent = new Intent(getActivity(), MagicCallActivity.class); - callIntent.putExtras(bundle); - - InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE); - if (imm != null) { - imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); - } - - new Handler().postDelayed(() -> getParentController().getRouter().popCurrentController(), 100); - startActivity(callIntent); - - } - - } - - private class OperationsObserver implements Observer { - - @Override - public void onSubscribe(Disposable d) { - disposable = d; - } - - @Override - public void onNext(Object o) { - if (operationCode != 99) { - showResultImage(true, false); - } else { - RoomOverall roomOverall = (RoomOverall) o; - conversation = roomOverall.getOcs().getData(); - initiateConversation(true, serverCapabilities); - } - } - - @Override - public void onError(Throwable e) { - if (operationCode != 99 || !(e instanceof HttpException)) { - showResultImage(false, false); - } else { - if (((HttpException) e).response().code() == 403) { - eventBus.post(new BottomSheetLockEvent(true, 0, false, - false)); - ApplicationWideMessageHolder.getInstance().setMessageType(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG); - getRouter().popCurrentController(); - } else { - showResultImage(false, false); - } - } - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } + @Override + public void onComplete() { + dispose(); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt index 8d75b08ba..dd3d0e3ae 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt @@ -24,15 +24,16 @@ import android.widget.ImageView import androidx.annotation.DrawableRes interface ListItemWithImage { - val title: String - fun populateIcon(imageView: ImageView) + val title: String + fun populateIcon(imageView: ImageView) } data class BasicListItemWithImage( - @DrawableRes val iconRes: Int, - override val title: String) : ListItemWithImage { + @DrawableRes val iconRes: Int, + override val title: String +) : ListItemWithImage { - override fun populateIcon(imageView: ImageView) { - imageView.setImageResource(iconRes) - } + override fun populateIcon(imageView: ImageView) { + imageView.setImageResource(iconRes) + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt index 40aa475f3..1de10f754 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt @@ -38,112 +38,117 @@ import com.nextcloud.talk.R private const val KEY_ACTIVATED_INDEX = "activated_index" internal class ListItemViewHolder( - itemView: View, - private val adapter: ListIconDialogAdapter<*>) : RecyclerView.ViewHolder(itemView), View.OnClickListener { - init { - itemView.setOnClickListener(this) - } + itemView: View, + private val adapter: ListIconDialogAdapter<*> +) : RecyclerView.ViewHolder(itemView), View.OnClickListener { + init { + itemView.setOnClickListener(this) + } - val iconView: ImageView = itemView.findViewById(R.id.icon) - val titleView: RtlTextView = itemView.findViewById(R.id.title) + val iconView: ImageView = itemView.findViewById(R.id.icon) + val titleView: RtlTextView = itemView.findViewById(R.id.title) - override fun onClick(view: View) = adapter.itemClicked(adapterPosition) + override fun onClick(view: View) = adapter.itemClicked(adapterPosition) } internal class ListIconDialogAdapter( - private var dialog: MaterialDialog, - private var items: List, - disabledItems: IntArray?, - private var waitForPositiveButton: Boolean, - private var selection: ListItemListener) : RecyclerView.Adapter(), DialogAdapter> { + private var dialog: MaterialDialog, + private var items: List, + disabledItems: IntArray?, + private var waitForPositiveButton: Boolean, + private var selection: ListItemListener +) : RecyclerView.Adapter(), DialogAdapter> { - private var disabledIndices: IntArray = disabledItems ?: IntArray(0) + private var disabledIndices: IntArray = disabledItems ?: IntArray(0) - fun itemClicked(index: Int) { - if (waitForPositiveButton && dialog.hasActionButton(WhichButton.POSITIVE)) { - // Wait for positive action button, mark clicked item as activated so that we can call the - // selection listener when the button is pressed. - val lastActivated = dialog.config[KEY_ACTIVATED_INDEX] as? Int - dialog.config[KEY_ACTIVATED_INDEX] = index - if (lastActivated != null) { - notifyItemChanged(lastActivated) - } - notifyItemChanged(index) - } else { - // Don't wait for action buttons, call listener and dismiss if auto dismiss is applicable - this.selection?.invoke(dialog, index, this.items[index]) - if (dialog.autoDismissEnabled && !dialog.hasActionButtons()) { - dialog.dismiss() - } - } + fun itemClicked(index: Int) { + if (waitForPositiveButton && dialog.hasActionButton(WhichButton.POSITIVE)) { + // Wait for positive action button, mark clicked item as activated so that we can call the + // selection listener when the button is pressed. + val lastActivated = dialog.config[KEY_ACTIVATED_INDEX] as? Int + dialog.config[KEY_ACTIVATED_INDEX] = index + if (lastActivated != null) { + notifyItemChanged(lastActivated) + } + notifyItemChanged(index) + } else { + // Don't wait for action buttons, call listener and dismiss if auto dismiss is applicable + this.selection?.invoke(dialog, index, this.items[index]) + if (dialog.autoDismissEnabled && !dialog.hasActionButtons()) { + dialog.dismiss() + } } + } - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int): ListItemViewHolder { - val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet) - val viewHolder = ListItemViewHolder( - itemView = listItemView, - adapter = this - ) - viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content) - return viewHolder + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ListItemViewHolder { + val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet) + val viewHolder = ListItemViewHolder( + itemView = listItemView, + adapter = this + ) + viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content) + return viewHolder + } + + override fun getItemCount() = items.size + + override fun onBindViewHolder( + holder: ListItemViewHolder, + position: Int + ) { + holder.itemView.isEnabled = !disabledIndices.contains(position) + val currentItem = items[position] + + holder.titleView.text = currentItem.title + holder.itemView.background = dialog.getItemSelector() + currentItem.populateIcon(holder.iconView) + + val activatedIndex = dialog.config[KEY_ACTIVATED_INDEX] as? Int + holder.itemView.isActivated = activatedIndex != null && activatedIndex == position + + if (dialog.bodyFont != null) { + holder.titleView.typeface = dialog.bodyFont } + } - override fun getItemCount() = items.size - - override fun onBindViewHolder( - holder: ListItemViewHolder, - position: Int) { - holder.itemView.isEnabled = !disabledIndices.contains(position) - val currentItem = items[position] - - holder.titleView.text = currentItem.title - holder.itemView.background = dialog.getItemSelector() - currentItem.populateIcon(holder.iconView) - - val activatedIndex = dialog.config[KEY_ACTIVATED_INDEX] as? Int - holder.itemView.isActivated = activatedIndex != null && activatedIndex == position - - if (dialog.bodyFont != null) { - holder.titleView.typeface = dialog.bodyFont - } + override fun positiveButtonClicked() { + val activatedIndex = dialog.config[KEY_ACTIVATED_INDEX] as? Int + if (activatedIndex != null) { + selection?.invoke(dialog, activatedIndex, items[activatedIndex]) + dialog.config.remove(KEY_ACTIVATED_INDEX) } + } - override fun positiveButtonClicked() { - val activatedIndex = dialog.config[KEY_ACTIVATED_INDEX] as? Int - if (activatedIndex != null) { - selection?.invoke(dialog, activatedIndex, items[activatedIndex]) - dialog.config.remove(KEY_ACTIVATED_INDEX) - } + override fun replaceItems( + items: List, + listener: ListItemListener + ) { + this.items = items + if (listener != null) { + this.selection = listener } + this.notifyDataSetChanged() + } - override fun replaceItems( - items: List, - listener: ListItemListener) { - this.items = items - if (listener != null) { - this.selection = listener - } - this.notifyDataSetChanged() - } + override fun disableItems(indices: IntArray) { + this.disabledIndices = indices + notifyDataSetChanged() + } - override fun disableItems(indices: IntArray) { - this.disabledIndices = indices - notifyDataSetChanged() - } + override fun checkItems(indices: IntArray) = Unit - override fun checkItems(indices: IntArray) = Unit + override fun uncheckItems(indices: IntArray) = Unit - override fun uncheckItems(indices: IntArray) = Unit + override fun toggleItems(indices: IntArray) = Unit - override fun toggleItems(indices: IntArray) = Unit + override fun checkAllItems() = Unit - override fun checkAllItems() = Unit + override fun uncheckAllItems() = Unit - override fun uncheckAllItems() = Unit + override fun toggleAllChecked() = Unit - override fun toggleAllChecked() = Unit - - override fun isItemChecked(index: Int) = false + override fun isItemChecked(index: Int) = false } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt index ab2be552a..258a40066 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt @@ -28,48 +28,50 @@ import com.afollestad.materialdialogs.list.customListAdapter import com.afollestad.materialdialogs.list.getListAdapter typealias ListItemListener = - ((dialog: MaterialDialog, index: Int, item: IT) -> Unit)? + ((dialog: MaterialDialog, index: Int, item: IT) -> Unit)? @CheckResult fun MaterialDialog.listItemsWithImage( - items: List, - disabledIndices: IntArray? = null, - waitForPositiveButton: Boolean = true, - selection: ListItemListener = null): MaterialDialog { + items: List, + disabledIndices: IntArray? = null, + waitForPositiveButton: Boolean = true, + selection: ListItemListener = null +): MaterialDialog { - if (getListAdapter() != null) { - return updateListItemsWithImage( - items = items, - disabledIndices = disabledIndices - ) - } - - val layoutManager = LinearLayoutManager(windowContext) - return customListAdapter( - adapter = ListIconDialogAdapter( - dialog = this, - items = items, - disabledItems = disabledIndices, - waitForPositiveButton = waitForPositiveButton, - selection = selection - ), - layoutManager = layoutManager + if (getListAdapter() != null) { + return updateListItemsWithImage( + items = items, + disabledIndices = disabledIndices ) + } + + val layoutManager = LinearLayoutManager(windowContext) + return customListAdapter( + adapter = ListIconDialogAdapter( + dialog = this, + items = items, + disabledItems = disabledIndices, + waitForPositiveButton = waitForPositiveButton, + selection = selection + ), + layoutManager = layoutManager + ) } fun MaterialDialog.updateListItemsWithImage( - items: List, - disabledIndices: IntArray? = null): MaterialDialog { - val adapter = getListAdapter() - check(adapter != null) { - "updateGridItems(...) can't be used before you've created a bottom sheet grid dialog." - } - if (adapter is DialogAdapter<*, *>) { - @Suppress("UNCHECKED_CAST") - (adapter as DialogAdapter).replaceItems(items) + items: List, + disabledIndices: IntArray? = null +): MaterialDialog { + val adapter = getListAdapter() + check(adapter != null) { + "updateGridItems(...) can't be used before you've created a bottom sheet grid dialog." + } + if (adapter is DialogAdapter<*, *>) { + @Suppress("UNCHECKED_CAST") + (adapter as DialogAdapter).replaceItems(items) - if (disabledIndices != null) { - adapter.disableItems(disabledIndices) - } + if (disabledIndices != null) { + adapter.disableItems(disabledIndices) } - return this + } + return this } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/BusModule.java b/app/src/main/java/com/nextcloud/talk/dagger/modules/BusModule.java index 4feb5d1db..a582ec4aa 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/BusModule.java +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/BusModule.java @@ -28,9 +28,9 @@ import org.greenrobot.eventbus.EventBus; @Module public class BusModule { - @Provides - @Singleton - public EventBus provideEventBus() { - return EventBus.getDefault(); - } + @Provides + @Singleton + public EventBus provideEventBus() { + return EventBus.getDefault(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ContextModule.java b/app/src/main/java/com/nextcloud/talk/dagger/modules/ContextModule.java index 94e61603c..e56558335 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ContextModule.java +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ContextModule.java @@ -27,14 +27,14 @@ import dagger.Provides; @Module public class ContextModule { - private final Context context; + private final Context context; - public ContextModule(@NonNull final Context context) { - this.context = context; - } + public ContextModule(@NonNull final Context context) { + this.context = context; + } - @Provides - public Context provideContext() { - return context; - } + @Provides + public Context provideContext() { + return context; + } } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java b/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java index 8c38f082d..0121bd77b 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java @@ -39,25 +39,26 @@ import net.orange_box.storebox.StoreBox; @Module public class DatabaseModule { - @Provides - @Singleton - public SqlCipherDatabaseSource provideSqlCipherDatabaseSource(@NonNull final Context context) { - return new SqlCipherDatabaseSource(context, Models.DEFAULT, - context.getResources().getString(R.string.nc_app_name).toLowerCase() - .replace(" ", "_").trim() + ".sqlite", - context.getString(R.string.nc_talk_database_encryption_key), 6); - } + @Provides + @Singleton + public SqlCipherDatabaseSource provideSqlCipherDatabaseSource(@NonNull final Context context) { + return new SqlCipherDatabaseSource(context, Models.DEFAULT, + context.getResources().getString(R.string.nc_app_name).toLowerCase() + .replace(" ", "_").trim() + ".sqlite", + context.getString(R.string.nc_talk_database_encryption_key), 6); + } - @Provides - @Singleton - public ReactiveEntityStore provideDataStore(@NonNull final SqlCipherDatabaseSource sqlCipherDatabaseSource) { - final Configuration configuration = sqlCipherDatabaseSource.getConfiguration(); - return ReactiveSupport.toReactiveStore(new EntityDataStore(configuration)); - } + @Provides + @Singleton + public ReactiveEntityStore provideDataStore( + @NonNull final SqlCipherDatabaseSource sqlCipherDatabaseSource) { + final Configuration configuration = sqlCipherDatabaseSource.getConfiguration(); + return ReactiveSupport.toReactiveStore(new EntityDataStore(configuration)); + } - @Provides - @Singleton - public AppPreferences providePreferences(@NonNull final Context poContext) { - return StoreBox.create(poContext, AppPreferences.class); - } + @Provides + @Singleton + public AppPreferences providePreferences(@NonNull final Context poContext) { + return StoreBox.create(poContext, AppPreferences.class); + } } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java b/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java index 96e375b09..04ca57610 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java @@ -73,250 +73,253 @@ import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; @Module(includes = DatabaseModule.class) public class RestModule { - private static final String TAG = "RestModule"; - private final Context context; + private static final String TAG = "RestModule"; + private final Context context; - public RestModule(Context context) { - this.context = context; + public RestModule(Context context) { + this.context = context; + } + + @Singleton + @Provides + NcApi provideNcApi(Retrofit retrofit) { + return retrofit.create(NcApi.class); + } + + @Singleton + @Provides + Proxy provideProxy(AppPreferences appPreferences) { + if (!TextUtils.isEmpty(appPreferences.getProxyType()) && !"No proxy".equals( + appPreferences.getProxyType()) + && !TextUtils.isEmpty(appPreferences.getProxyHost())) { + GetProxyRunnable getProxyRunnable = new GetProxyRunnable(appPreferences); + Thread getProxyThread = new Thread(getProxyRunnable); + getProxyThread.start(); + try { + getProxyThread.join(); + return getProxyRunnable.getProxyValue(); + } catch (InterruptedException e) { + Log.e(TAG, "Failed to join the thread while getting proxy: " + e.getLocalizedMessage()); + return Proxy.NO_PROXY; + } + } else { + return Proxy.NO_PROXY; + } + } + + @Singleton + @Provides + Retrofit provideRetrofit(OkHttpClient httpClient) { + Retrofit.Builder retrofitBuilder = new Retrofit.Builder() + .client(httpClient) + .baseUrl("https://nextcloud.com") + .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) + .addConverterFactory(LoganSquareConverterFactory.create()); + + return retrofitBuilder.build(); + } + + @Singleton + @Provides + MagicTrustManager provideMagicTrustManager() { + return new MagicTrustManager(); + } + + @Singleton + @Provides + MagicKeyManager provideKeyManager(AppPreferences appPreferences, UserUtils userUtils) { + KeyStore keyStore = null; + try { + keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, null); + X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0]; + return new MagicKeyManager(origKm, userUtils, appPreferences); + } catch (KeyStoreException e) { + Log.e(TAG, "KeyStoreException " + e.getLocalizedMessage()); + } catch (CertificateException e) { + Log.e(TAG, "CertificateException " + e.getLocalizedMessage()); + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, "NoSuchAlgorithmException " + e.getLocalizedMessage()); + } catch (IOException e) { + Log.e(TAG, "IOException " + e.getLocalizedMessage()); + } catch (UnrecoverableKeyException e) { + Log.e(TAG, "UnrecoverableKeyException " + e.getLocalizedMessage()); } - @Singleton - @Provides - NcApi provideNcApi(Retrofit retrofit) { - return retrofit.create(NcApi.class); + return null; + } + + @Singleton + @Provides + SSLSocketFactoryCompat provideSslSocketFactoryCompat(MagicKeyManager keyManager, MagicTrustManager + magicTrustManager) { + return new SSLSocketFactoryCompat(keyManager, magicTrustManager); + } + + @Singleton + @Provides + CookieManager provideCookieManager() { + CookieManager cookieManager = new CookieManager(); + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_NONE); + return cookieManager; + } + + @Singleton + @Provides + Cache provideCache() { + int cacheSize = 128 * 1024 * 1024; // 128 MB + return new Cache(NextcloudTalkApplication.Companion.getSharedApplication().getCacheDir(), + cacheSize); + } + + @Singleton + @Provides + Dispatcher provideDispatcher() { + Dispatcher dispatcher = new Dispatcher(); + dispatcher.setMaxRequestsPerHost(100); + dispatcher.setMaxRequests(100); + return dispatcher; + } + + @Singleton + @Provides + OkHttpClient provideHttpClient(Proxy proxy, AppPreferences appPreferences, + MagicTrustManager magicTrustManager, + SSLSocketFactoryCompat sslSocketFactoryCompat, Cache cache, + CookieManager cookieManager, Dispatcher dispatcher) { + OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); + + httpClient.retryOnConnectionFailure(true); + httpClient.connectTimeout(45, TimeUnit.SECONDS); + httpClient.readTimeout(45, TimeUnit.SECONDS); + httpClient.writeTimeout(45, TimeUnit.SECONDS); + + httpClient.cookieJar(new JavaNetCookieJar(cookieManager)); + httpClient.cache(cache); + + // Trust own CA and all self-signed certs + httpClient.sslSocketFactory(sslSocketFactoryCompat, magicTrustManager); + httpClient.retryOnConnectionFailure(true); + httpClient.hostnameVerifier(magicTrustManager.getHostnameVerifier(OkHostnameVerifier.INSTANCE)); + + httpClient.dispatcher(dispatcher); + if (!Proxy.NO_PROXY.equals(proxy)) { + httpClient.proxy(proxy); + + if (appPreferences.getProxyCredentials() && + !TextUtils.isEmpty(appPreferences.getProxyUsername()) && + !TextUtils.isEmpty(appPreferences.getProxyPassword())) { + httpClient.proxyAuthenticator(new MagicAuthenticator(Credentials.basic( + appPreferences.getProxyUsername(), + appPreferences.getProxyPassword()), "Proxy-Authorization")); + } } - @Singleton - @Provides - Proxy provideProxy(AppPreferences appPreferences) { - if (!TextUtils.isEmpty(appPreferences.getProxyType()) && !"No proxy".equals(appPreferences.getProxyType()) - && !TextUtils.isEmpty(appPreferences.getProxyHost())) { - GetProxyRunnable getProxyRunnable = new GetProxyRunnable(appPreferences); - Thread getProxyThread = new Thread(getProxyRunnable); - getProxyThread.start(); - try { - getProxyThread.join(); - return getProxyRunnable.getProxyValue(); - } catch (InterruptedException e) { - Log.e(TAG, "Failed to join the thread while getting proxy: " + e.getLocalizedMessage()); - return Proxy.NO_PROXY; - } - } else { - return Proxy.NO_PROXY; - } + httpClient.addInterceptor(new HeadersInterceptor()); + + if (BuildConfig.DEBUG && !context.getResources().getBoolean(R.bool.nc_is_debug)) { + HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + loggingInterceptor.redactHeader("Authorization"); + loggingInterceptor.redactHeader("Proxy-Authorization"); + httpClient.addInterceptor(loggingInterceptor); + } else if (context.getResources().getBoolean(R.bool.nc_is_debug)) { + + HttpLoggingInterceptor.Logger fileLogger = + s -> LoggingUtils.INSTANCE.writeLogEntryToFile(context, s); + HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(fileLogger); + loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + loggingInterceptor.redactHeader("Authorization"); + loggingInterceptor.redactHeader("Proxy-Authorization"); + httpClient.addInterceptor(loggingInterceptor); } - @Singleton - @Provides - Retrofit provideRetrofit(OkHttpClient httpClient) { - Retrofit.Builder retrofitBuilder = new Retrofit.Builder() - .client(httpClient) - .baseUrl("https://nextcloud.com") - .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) - .addConverterFactory(LoganSquareConverterFactory.create()); + return httpClient.build(); + } - return retrofitBuilder.build(); + public static class HeadersInterceptor implements Interceptor { + + @NonNull + @Override + public Response intercept(@NonNull Chain chain) throws IOException { + Request original = chain.request(); + Request request = original.newBuilder() + .header("User-Agent", ApiUtils.getUserAgent()) + .header("Accept", "application/json") + .header("OCS-APIRequest", "true") + .method(original.method(), original.body()) + .build(); + + Response response = chain.proceed(request); + + if (request.url().encodedPath().contains("/avatar/")) { + AvatarStatusCodeHolder.getInstance().setStatusCode(response.code()); + } + + return response; + } + } + + public static class MagicAuthenticator implements Authenticator { + + private String credentials; + private String authenticatorType; + + public MagicAuthenticator(@NonNull String credentials, @NonNull String authenticatorType) { + this.credentials = credentials; + this.authenticatorType = authenticatorType; } - @Singleton - @Provides - MagicTrustManager provideMagicTrustManager() { - return new MagicTrustManager(); - } - - @Singleton - @Provides - MagicKeyManager provideKeyManager(AppPreferences appPreferences, UserUtils userUtils) { - KeyStore keyStore = null; - try { - keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(keyStore, null); - X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0]; - return new MagicKeyManager(origKm, userUtils, appPreferences); - } catch (KeyStoreException e) { - Log.e(TAG, "KeyStoreException " + e.getLocalizedMessage()); - } catch (CertificateException e) { - Log.e(TAG, "CertificateException " + e.getLocalizedMessage()); - } catch (NoSuchAlgorithmException e) { - Log.e(TAG, "NoSuchAlgorithmException " + e.getLocalizedMessage()); - } catch (IOException e) { - Log.e(TAG, "IOException " + e.getLocalizedMessage()); - } catch (UnrecoverableKeyException e) { - Log.e(TAG, "UnrecoverableKeyException " + e.getLocalizedMessage()); - } - + @Nullable + @Override + public Request authenticate(@Nullable Route route, @NonNull Response response) { + if (response.request().header(authenticatorType) != null) { return null; - } + } - @Singleton - @Provides - SSLSocketFactoryCompat provideSslSocketFactoryCompat(MagicKeyManager keyManager, MagicTrustManager - magicTrustManager) { - return new SSLSocketFactoryCompat(keyManager, magicTrustManager); - } + Response countedResponse = response; - @Singleton - @Provides - CookieManager provideCookieManager() { - CookieManager cookieManager = new CookieManager(); - cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_NONE); - return cookieManager; - } + int attemptsCount = 0; - @Singleton - @Provides - Cache provideCache() { - int cacheSize = 128 * 1024 * 1024; // 128 MB - return new Cache(NextcloudTalkApplication.Companion.getSharedApplication().getCacheDir(), cacheSize); - } - - @Singleton - @Provides - Dispatcher provideDispatcher() { - Dispatcher dispatcher = new Dispatcher(); - dispatcher.setMaxRequestsPerHost(100); - dispatcher.setMaxRequests(100); - return dispatcher; - } - - @Singleton - @Provides - OkHttpClient provideHttpClient(Proxy proxy, AppPreferences appPreferences, - MagicTrustManager magicTrustManager, - SSLSocketFactoryCompat sslSocketFactoryCompat, Cache cache, - CookieManager cookieManager, Dispatcher dispatcher) { - OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); - - httpClient.retryOnConnectionFailure(true); - httpClient.connectTimeout(45, TimeUnit.SECONDS); - httpClient.readTimeout(45, TimeUnit.SECONDS); - httpClient.writeTimeout(45, TimeUnit.SECONDS); - - httpClient.cookieJar(new JavaNetCookieJar(cookieManager)); - httpClient.cache(cache); - - // Trust own CA and all self-signed certs - httpClient.sslSocketFactory(sslSocketFactoryCompat, magicTrustManager); - httpClient.retryOnConnectionFailure(true); - httpClient.hostnameVerifier(magicTrustManager.getHostnameVerifier(OkHostnameVerifier.INSTANCE)); - - httpClient.dispatcher(dispatcher); - if (!Proxy.NO_PROXY.equals(proxy)) { - httpClient.proxy(proxy); - - if (appPreferences.getProxyCredentials() && - !TextUtils.isEmpty(appPreferences.getProxyUsername()) && - !TextUtils.isEmpty(appPreferences.getProxyPassword())) { - httpClient.proxyAuthenticator(new MagicAuthenticator(Credentials.basic( - appPreferences.getProxyUsername(), - appPreferences.getProxyPassword()), "Proxy-Authorization")); - } + while ((countedResponse = countedResponse.priorResponse()) != null) { + attemptsCount++; + if (attemptsCount == 3) { + return null; } + } - httpClient.addInterceptor(new HeadersInterceptor()); + return response.request().newBuilder() + .header(authenticatorType, credentials) + .build(); + } + } - if (BuildConfig.DEBUG && !context.getResources().getBoolean(R.bool.nc_is_debug)) { - HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); - loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); - loggingInterceptor.redactHeader("Authorization"); - loggingInterceptor.redactHeader("Proxy-Authorization"); - httpClient.addInterceptor(loggingInterceptor); - } else if (context.getResources().getBoolean(R.bool.nc_is_debug)) { + private class GetProxyRunnable implements Runnable { + private volatile Proxy proxy; + private AppPreferences appPreferences; - HttpLoggingInterceptor.Logger fileLogger = - s -> LoggingUtils.INSTANCE.writeLogEntryToFile(context, s); - HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(fileLogger); - loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); - loggingInterceptor.redactHeader("Authorization"); - loggingInterceptor.redactHeader("Proxy-Authorization"); - httpClient.addInterceptor(loggingInterceptor); - } - - return httpClient.build(); + GetProxyRunnable(AppPreferences appPreferences) { + this.appPreferences = appPreferences; } - public static class HeadersInterceptor implements Interceptor { - - @NonNull - @Override - public Response intercept(@NonNull Chain chain) throws IOException { - Request original = chain.request(); - Request request = original.newBuilder() - .header("User-Agent", ApiUtils.getUserAgent()) - .header("Accept", "application/json") - .header("OCS-APIRequest", "true") - .method(original.method(), original.body()) - .build(); - - Response response = chain.proceed(request); - - if (request.url().encodedPath().contains("/avatar/")) { - AvatarStatusCodeHolder.getInstance().setStatusCode(response.code()); - } - - return response; - } + @Override + public void run() { + if (Proxy.Type.SOCKS.equals(Proxy.Type.valueOf(appPreferences.getProxyType()))) { + proxy = new Proxy(Proxy.Type.valueOf(appPreferences.getProxyType()), + InetSocketAddress.createUnresolved(appPreferences.getProxyHost(), Integer.parseInt( + appPreferences.getProxyPort()))); + } else { + proxy = new Proxy(Proxy.Type.valueOf(appPreferences.getProxyType()), + new InetSocketAddress(appPreferences.getProxyHost(), + Integer.parseInt(appPreferences.getProxyPort()))); + } } - public static class MagicAuthenticator implements Authenticator { - - private String credentials; - private String authenticatorType; - - public MagicAuthenticator(@NonNull String credentials, @NonNull String authenticatorType) { - this.credentials = credentials; - this.authenticatorType = authenticatorType; - } - - @Nullable - @Override - public Request authenticate(@Nullable Route route, @NonNull Response response) { - if (response.request().header(authenticatorType) != null) { - return null; - } - - Response countedResponse = response; - - int attemptsCount = 0; - - while ((countedResponse = countedResponse.priorResponse()) != null) { - attemptsCount++; - if (attemptsCount == 3) { - return null; - } - } - - return response.request().newBuilder() - .header(authenticatorType, credentials) - .build(); - } - } - - private class GetProxyRunnable implements Runnable { - private volatile Proxy proxy; - private AppPreferences appPreferences; - - GetProxyRunnable(AppPreferences appPreferences) { - this.appPreferences = appPreferences; - } - - @Override - public void run() { - if (Proxy.Type.SOCKS.equals(Proxy.Type.valueOf(appPreferences.getProxyType()))) { - proxy = new Proxy(Proxy.Type.valueOf(appPreferences.getProxyType()), - InetSocketAddress.createUnresolved(appPreferences.getProxyHost(), Integer.parseInt( - appPreferences.getProxyPort()))); - } else { - proxy = new Proxy(Proxy.Type.valueOf(appPreferences.getProxyType()), - new InetSocketAddress(appPreferences.getProxyHost(), - Integer.parseInt(appPreferences.getProxyPort()))); - } - } - - Proxy getProxyValue() { - return proxy; - } + Proxy getProxyValue() { + return proxy; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/BottomSheetLockEvent.java b/app/src/main/java/com/nextcloud/talk/events/BottomSheetLockEvent.java index 299cb02f6..c1ef8bbd1 100644 --- a/app/src/main/java/com/nextcloud/talk/events/BottomSheetLockEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/BottomSheetLockEvent.java @@ -24,27 +24,28 @@ import lombok.Data; @Data public class BottomSheetLockEvent { - private final boolean cancelable; - private final int delay; - private final boolean shouldRefreshData; - private final boolean cancel; - private boolean dismissView; + private final boolean cancelable; + private final int delay; + private final boolean shouldRefreshData; + private final boolean cancel; + private boolean dismissView; - public BottomSheetLockEvent(boolean cancelable, int delay, boolean shouldRefreshData, boolean cancel) { - this.cancelable = cancelable; - this.delay = delay; - this.shouldRefreshData = shouldRefreshData; - this.cancel = cancel; - this.dismissView = true; - } - - public BottomSheetLockEvent(boolean cancelable, int delay, boolean shouldRefreshData, boolean cancel, boolean - dismissView) { - this.cancelable = cancelable; - this.delay = delay; - this.shouldRefreshData = shouldRefreshData; - this.cancel = cancel; - this.dismissView = dismissView; - } + public BottomSheetLockEvent(boolean cancelable, int delay, boolean shouldRefreshData, + boolean cancel) { + this.cancelable = cancelable; + this.delay = delay; + this.shouldRefreshData = shouldRefreshData; + this.cancel = cancel; + this.dismissView = true; + } + public BottomSheetLockEvent(boolean cancelable, int delay, boolean shouldRefreshData, + boolean cancel, boolean + dismissView) { + this.cancelable = cancelable; + this.delay = delay; + this.shouldRefreshData = shouldRefreshData; + this.cancel = cancel; + this.dismissView = dismissView; + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/CertificateEvent.java b/app/src/main/java/com/nextcloud/talk/events/CertificateEvent.java index c7af5c84d..bd597c32a 100644 --- a/app/src/main/java/com/nextcloud/talk/events/CertificateEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/CertificateEvent.java @@ -26,28 +26,28 @@ import com.nextcloud.talk.utils.ssl.MagicTrustManager; import java.security.cert.X509Certificate; public class CertificateEvent { - private final X509Certificate x509Certificate; - private final MagicTrustManager magicTrustManager; - @Nullable - private final SslErrorHandler sslErrorHandler; + private final X509Certificate x509Certificate; + private final MagicTrustManager magicTrustManager; + @Nullable + private final SslErrorHandler sslErrorHandler; - public CertificateEvent(X509Certificate x509Certificate, MagicTrustManager magicTrustManager, - @Nullable SslErrorHandler sslErrorHandler) { - this.x509Certificate = x509Certificate; - this.magicTrustManager = magicTrustManager; - this.sslErrorHandler = sslErrorHandler; - } + public CertificateEvent(X509Certificate x509Certificate, MagicTrustManager magicTrustManager, + @Nullable SslErrorHandler sslErrorHandler) { + this.x509Certificate = x509Certificate; + this.magicTrustManager = magicTrustManager; + this.sslErrorHandler = sslErrorHandler; + } - @Nullable - public SslErrorHandler getSslErrorHandler() { - return sslErrorHandler; - } + @Nullable + public SslErrorHandler getSslErrorHandler() { + return sslErrorHandler; + } - public X509Certificate getX509Certificate() { - return x509Certificate; - } + public X509Certificate getX509Certificate() { + return x509Certificate; + } - public MagicTrustManager getMagicTrustManager() { - return magicTrustManager; - } + public MagicTrustManager getMagicTrustManager() { + return magicTrustManager; + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/EventStatus.java b/app/src/main/java/com/nextcloud/talk/events/EventStatus.java index ef3e72b15..d2a60f35d 100644 --- a/app/src/main/java/com/nextcloud/talk/events/EventStatus.java +++ b/app/src/main/java/com/nextcloud/talk/events/EventStatus.java @@ -24,18 +24,17 @@ import lombok.Data; @Data public class EventStatus { - private long userId; - private EventType eventType; - private boolean allGood; + private long userId; + private EventType eventType; + private boolean allGood; - public EventStatus(long userId, EventType eventType, boolean allGood) { - this.userId = userId; - this.eventType = eventType; - this.allGood = allGood; - } - - public enum EventType { - PUSH_REGISTRATION, CAPABILITIES_FETCH, SIGNALING_SETTINGS, CONVERSATION_UPDATE, PARTICIPANTS_UPDATE - } + public EventStatus(long userId, EventType eventType, boolean allGood) { + this.userId = userId; + this.eventType = eventType; + this.allGood = allGood; + } + public enum EventType { + PUSH_REGISTRATION, CAPABILITIES_FETCH, SIGNALING_SETTINGS, CONVERSATION_UPDATE, PARTICIPANTS_UPDATE + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/MediaStreamEvent.java b/app/src/main/java/com/nextcloud/talk/events/MediaStreamEvent.java index 3b9afaad7..e6c75f4a8 100644 --- a/app/src/main/java/com/nextcloud/talk/events/MediaStreamEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/MediaStreamEvent.java @@ -26,13 +26,14 @@ import org.webrtc.MediaStream; @Data public class MediaStreamEvent { - private final MediaStream mediaStream; - private final String session; - private final String videoStreamType; + private final MediaStream mediaStream; + private final String session; + private final String videoStreamType; - public MediaStreamEvent(@Nullable MediaStream mediaStream, String session, String videoStreamType) { - this.mediaStream = mediaStream; - this.session = session; - this.videoStreamType = videoStreamType; - } + public MediaStreamEvent(@Nullable MediaStream mediaStream, String session, + String videoStreamType) { + this.mediaStream = mediaStream; + this.session = session; + this.videoStreamType = videoStreamType; + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java b/app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java index bca88d9e4..cd74cb5f0 100644 --- a/app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java @@ -25,9 +25,9 @@ import lombok.Data; @Data public class MoreMenuClickEvent { - private final Conversation conversation; + private final Conversation conversation; - public MoreMenuClickEvent(Conversation conversation) { - this.conversation = conversation; - } + public MoreMenuClickEvent(Conversation conversation) { + this.conversation = conversation; + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/NetworkEvent.java b/app/src/main/java/com/nextcloud/talk/events/NetworkEvent.java index 76406661d..46fa1da3e 100644 --- a/app/src/main/java/com/nextcloud/talk/events/NetworkEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/NetworkEvent.java @@ -24,13 +24,13 @@ import lombok.Data; @Data public class NetworkEvent { - public enum NetworkConnectionEvent { - NETWORK_CONNECTED, NETWORK_DISCONNECTED - } + private final NetworkConnectionEvent networkConnectionEvent; - private final NetworkConnectionEvent networkConnectionEvent; + public NetworkEvent(NetworkConnectionEvent networkConnectionEvent) { + this.networkConnectionEvent = networkConnectionEvent; + } - public NetworkEvent(NetworkConnectionEvent networkConnectionEvent) { - this.networkConnectionEvent = networkConnectionEvent; - } + public enum NetworkConnectionEvent { + NETWORK_CONNECTED, NETWORK_DISCONNECTED + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/PeerConnectionEvent.java b/app/src/main/java/com/nextcloud/talk/events/PeerConnectionEvent.java index c6ef6e0e8..459a9338f 100644 --- a/app/src/main/java/com/nextcloud/talk/events/PeerConnectionEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/PeerConnectionEvent.java @@ -25,22 +25,23 @@ import lombok.Data; @Data public class PeerConnectionEvent { - private final PeerConnectionEventType peerConnectionEventType; - private final String sessionId; - private final String nick; - private final Boolean changeValue; - private final String videoStreamType; + private final PeerConnectionEventType peerConnectionEventType; + private final String sessionId; + private final String nick; + private final Boolean changeValue; + private final String videoStreamType; - public PeerConnectionEvent(PeerConnectionEventType peerConnectionEventType, @Nullable String sessionId, - @Nullable String nick, Boolean changeValue, @Nullable String videoStreamType) { - this.peerConnectionEventType = peerConnectionEventType; - this.nick = nick; - this.changeValue = changeValue; - this.sessionId = sessionId; - this.videoStreamType = videoStreamType; - } + public PeerConnectionEvent(PeerConnectionEventType peerConnectionEventType, + @Nullable String sessionId, + @Nullable String nick, Boolean changeValue, @Nullable String videoStreamType) { + this.peerConnectionEventType = peerConnectionEventType; + this.nick = nick; + this.changeValue = changeValue; + this.sessionId = sessionId; + this.videoStreamType = videoStreamType; + } - public enum PeerConnectionEventType { - PEER_CONNECTED, PEER_CLOSED, SENSOR_FAR, SENSOR_NEAR, NICK_CHANGE, AUDIO_CHANGE, VIDEO_CHANGE, PUBLISHER_FAILED - } + public enum PeerConnectionEventType { + PEER_CONNECTED, PEER_CLOSED, SENSOR_FAR, SENSOR_NEAR, NICK_CHANGE, AUDIO_CHANGE, VIDEO_CHANGE, PUBLISHER_FAILED + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/SessionDescriptionSendEvent.java b/app/src/main/java/com/nextcloud/talk/events/SessionDescriptionSendEvent.java index 4fd81563f..1e8290c89 100644 --- a/app/src/main/java/com/nextcloud/talk/events/SessionDescriptionSendEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/SessionDescriptionSendEvent.java @@ -27,20 +27,21 @@ import org.webrtc.SessionDescription; @Data public class SessionDescriptionSendEvent { - @Nullable - private final SessionDescription sessionDescription; - private final String peerId; - private final String type; - @Nullable - private final NCIceCandidate ncIceCandidate; - private final String videoStreamType; + @Nullable + private final SessionDescription sessionDescription; + private final String peerId; + private final String type; + @Nullable + private final NCIceCandidate ncIceCandidate; + private final String videoStreamType; - public SessionDescriptionSendEvent(@Nullable SessionDescription sessionDescription, String peerId, String type, - @Nullable NCIceCandidate ncIceCandidate, @Nullable String videoStreamType) { - this.sessionDescription = sessionDescription; - this.peerId = peerId; - this.type = type; - this.ncIceCandidate = ncIceCandidate; - this.videoStreamType = videoStreamType; - } + public SessionDescriptionSendEvent(@Nullable SessionDescription sessionDescription, String peerId, + String type, + @Nullable NCIceCandidate ncIceCandidate, @Nullable String videoStreamType) { + this.sessionDescription = sessionDescription; + this.peerId = peerId; + this.type = type; + this.ncIceCandidate = ncIceCandidate; + this.videoStreamType = videoStreamType; + } } diff --git a/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java b/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java index 351fa39e0..5ab37f8b4 100644 --- a/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java @@ -24,5 +24,5 @@ import lombok.Data; @Data public class UserMentionClickEvent { - public final String userId; + public final String userId; } diff --git a/app/src/main/java/com/nextcloud/talk/events/WebSocketCommunicationEvent.java b/app/src/main/java/com/nextcloud/talk/events/WebSocketCommunicationEvent.java index d6a36a8a7..738c91572 100644 --- a/app/src/main/java/com/nextcloud/talk/events/WebSocketCommunicationEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/WebSocketCommunicationEvent.java @@ -26,7 +26,7 @@ import lombok.Data; @Data public class WebSocketCommunicationEvent { - public final String type; - @Nullable - public final HashMap hashMap; + public final String type; + @Nullable + public final HashMap hashMap; } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt b/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt index 8e678d5e1..1273593d2 100644 --- a/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt @@ -22,6 +22,6 @@ package com.nextcloud.talk.interfaces interface ClosedInterface { - val isGooglePlayServicesAvailable: Boolean - fun providerInstallerInstallIfNeededAsync() + val isGooglePlayServicesAvailable: Boolean + fun providerInstallerInstallIfNeededAsync() } diff --git a/app/src/main/java/com/nextcloud/talk/interfaces/ConversationMenuInterface.kt b/app/src/main/java/com/nextcloud/talk/interfaces/ConversationMenuInterface.kt index 9f6afdaf1..94a1f77b1 100644 --- a/app/src/main/java/com/nextcloud/talk/interfaces/ConversationMenuInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/interfaces/ConversationMenuInterface.kt @@ -23,5 +23,8 @@ package com.nextcloud.talk.interfaces import android.os.Bundle interface ConversationMenuInterface { - fun openLovelyDialogWithIdAndBundle(dialogId: Int, bundle: Bundle) + fun openLovelyDialogWithIdAndBundle( + dialogId: Int, + bundle: Bundle + ) } diff --git a/app/src/main/java/com/nextcloud/talk/interfaces/SelectionInterface.kt b/app/src/main/java/com/nextcloud/talk/interfaces/SelectionInterface.kt index 4766c5534..0316f7dd8 100644 --- a/app/src/main/java/com/nextcloud/talk/interfaces/SelectionInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/interfaces/SelectionInterface.kt @@ -21,7 +21,7 @@ package com.nextcloud.talk.interfaces interface SelectionInterface { - fun toggleBrowserItemSelection(path: String) + fun toggleBrowserItemSelection(path: String) - fun isPathSelected(path: String): Boolean + fun isPathSelected(path: String): Boolean } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java index 1cb795a4a..7dcceec43 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java @@ -30,12 +30,12 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import autodagger.AutoInjector; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.json.generic.GenericOverall; -import com.nextcloud.talk.models.json.push.PushConfigurationState; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.generic.GenericOverall; +import com.nextcloud.talk.models.json.push.PushConfigurationState; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -54,196 +54,205 @@ import retrofit2.Retrofit; @AutoInjector(NextcloudTalkApplication.class) public class AccountRemovalWorker extends Worker { - public static final String TAG = "AccountRemovalWorker"; + public static final String TAG = "AccountRemovalWorker"; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - ArbitraryStorageUtils arbitraryStorageUtils; + @Inject + ArbitraryStorageUtils arbitraryStorageUtils; - @Inject - Retrofit retrofit; + @Inject + Retrofit retrofit; - @Inject - OkHttpClient okHttpClient; + @Inject + OkHttpClient okHttpClient; - NcApi ncApi; + NcApi ncApi; - public AccountRemovalWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - } + public AccountRemovalWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } - @NonNull - @Override - public Result doWork() { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + @NonNull + @Override + public Result doWork() { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - PushConfigurationState pushConfigurationState; - String credentials; - for (Object userEntityObject : userUtils.getUsersScheduledForDeletion()) { - UserEntity userEntity = (UserEntity) userEntityObject; - try { - if (!TextUtils.isEmpty(userEntity.getPushConfigurationState())) { - pushConfigurationState = LoganSquare.parse(userEntity.getPushConfigurationState(), - PushConfigurationState.class); - PushConfigurationState finalPushConfigurationState = pushConfigurationState; + PushConfigurationState pushConfigurationState; + String credentials; + for (Object userEntityObject : userUtils.getUsersScheduledForDeletion()) { + UserEntity userEntity = (UserEntity) userEntityObject; + try { + if (!TextUtils.isEmpty(userEntity.getPushConfigurationState())) { + pushConfigurationState = LoganSquare.parse(userEntity.getPushConfigurationState(), + PushConfigurationState.class); + PushConfigurationState finalPushConfigurationState = pushConfigurationState; - credentials = ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()); + credentials = ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()); - ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new - JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); + ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new + JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); - String finalCredentials = credentials; - ncApi.unregisterDeviceForNotificationsWithNextcloud(credentials, ApiUtils.getUrlNextcloudPush(userEntity - .getBaseUrl())) - .blockingSubscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + String finalCredentials = credentials; + ncApi.unregisterDeviceForNotificationsWithNextcloud(credentials, + ApiUtils.getUrlNextcloudPush(userEntity + .getBaseUrl())) + .blockingSubscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(GenericOverall genericOverall) { - if (genericOverall.getOcs().getMeta().getStatusCode() == 200 - || genericOverall.getOcs().getMeta().getStatusCode() == 202) { - HashMap queryMap = new HashMap<>(); - queryMap.put("deviceIdentifier", finalPushConfigurationState.getDeviceIdentifier()); - queryMap.put("userPublicKey", finalPushConfigurationState.getUserPublicKey()); - queryMap.put("deviceIdentifierSignature", - finalPushConfigurationState.getDeviceIdentifierSignature()); - - ncApi.unregisterDeviceForNotificationsWithProxy - (ApiUtils.getUrlPushProxy(), queryMap) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(Void aVoid) { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - String groupName = String.format(getApplicationContext().getResources() - .getString(R.string - .nc_notification_channel), userEntity.getUserId(), userEntity.getBaseUrl()); - CRC32 crc32 = new CRC32(); - crc32.update(groupName.getBytes()); - NotificationManager notificationManager = - (NotificationManager) getApplicationContext().getSystemService - (Context.NOTIFICATION_SERVICE); - - if (notificationManager != null) { - notificationManager.deleteNotificationChannelGroup(Long - .toString(crc32.getValue())); - } - } - - WebSocketConnectionHelper.deleteExternalSignalingInstanceForUserEntity(userEntity.getId()); - - arbitraryStorageUtils.deleteAllEntriesForAccountIdentifier(userEntity.getId()).subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(Object o) { - userUtils.deleteUser(userEntity.getId()).subscribe(new CompletableObserver() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onComplete() { - - } - - @Override - public void onError(Throwable e) { - - } - }); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } else { - userUtils.deleteUser(userEntity.getId()) - .subscribe(new CompletableObserver() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onComplete() { - - } - - @Override - public void onError(Throwable e) { - - } - }); } - } catch (IOException e) { - Log.d(TAG, "Something went wrong while removing job at parsing PushConfigurationState"); - userUtils.deleteUser(userEntity.getId()) - .subscribe(new CompletableObserver() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void onNext(GenericOverall genericOverall) { + if (genericOverall.getOcs().getMeta().getStatusCode() == 200 + || genericOverall.getOcs().getMeta().getStatusCode() == 202) { + HashMap queryMap = new HashMap<>(); + queryMap.put("deviceIdentifier", + finalPushConfigurationState.getDeviceIdentifier()); + queryMap.put("userPublicKey", finalPushConfigurationState.getUserPublicKey()); + queryMap.put("deviceIdentifierSignature", + finalPushConfigurationState.getDeviceIdentifierSignature()); + + ncApi.unregisterDeviceForNotificationsWithProxy + (ApiUtils.getUrlPushProxy(), queryMap) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(Void aVoid) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + String groupName = + String.format(getApplicationContext().getResources() + .getString(R.string + .nc_notification_channel), userEntity.getUserId(), + userEntity.getBaseUrl()); + CRC32 crc32 = new CRC32(); + crc32.update(groupName.getBytes()); + NotificationManager notificationManager = + (NotificationManager) getApplicationContext().getSystemService + (Context.NOTIFICATION_SERVICE); + + if (notificationManager != null) { + notificationManager.deleteNotificationChannelGroup(Long + .toString(crc32.getValue())); + } } - @Override - public void onComplete() { + WebSocketConnectionHelper.deleteExternalSignalingInstanceForUserEntity( + userEntity.getId()); - } + arbitraryStorageUtils.deleteAllEntriesForAccountIdentifier( + userEntity.getId()).subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - @Override - public void onError(Throwable e) { + } - } + @Override + public void onNext(Object o) { + userUtils.deleteUser(userEntity.getId()) + .subscribe(new CompletableObserver() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onComplete() { + + } + + @Override + public void onError(Throwable e) { + + } + }); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } }); - } - } + } + } - return Result.success(); + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } else { + userUtils.deleteUser(userEntity.getId()) + .subscribe(new CompletableObserver() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onComplete() { + + } + + @Override + public void onError(Throwable e) { + + } + }); + } + } catch (IOException e) { + Log.d(TAG, "Something went wrong while removing job at parsing PushConfigurationState"); + userUtils.deleteUser(userEntity.getId()) + .subscribe(new CompletableObserver() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onComplete() { + + } + + @Override + public void onError(Throwable e) { + + } + }); + } } + + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java b/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java index ff1f538b2..0d29d4c45 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java @@ -26,10 +26,10 @@ import androidx.work.Data; import androidx.work.Worker; import androidx.work.WorkerParameters; import autodagger.AutoInjector; -import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.EventStatus; +import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; @@ -40,50 +40,56 @@ import org.greenrobot.eventbus.EventBus; @AutoInjector(NextcloudTalkApplication.class) public class AddParticipantsToConversation extends Worker { - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - public AddParticipantsToConversation(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public AddParticipantsToConversation(@NonNull Context context, + @NonNull WorkerParameters workerParams) { + super(context, workerParams); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } + + @NonNull + @Override + public Result doWork() { + Data data = getInputData(); + String[] selectedUserIds = data.getStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_USERS()); + String[] selectedGroupIds = data.getStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_GROUPS()); + UserEntity user = userUtils.getUserWithInternalId( + data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1)); + String conversationToken = data.getString(BundleKeys.INSTANCE.getKEY_TOKEN()); + String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); + + RetrofitBucket retrofitBucket; + for (String userId : selectedUserIds) { + retrofitBucket = + ApiUtils.getRetrofitBucketForAddParticipant(user.getBaseUrl(), conversationToken, + userId); + + ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .blockingSubscribe(); } - @NonNull - @Override - public Result doWork() { - Data data = getInputData(); - String[] selectedUserIds = data.getStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_USERS()); - String[] selectedGroupIds = data.getStringArray(BundleKeys.INSTANCE.getKEY_SELECTED_GROUPS()); - UserEntity user = userUtils.getUserWithInternalId(data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1)); - String conversationToken = data.getString(BundleKeys.INSTANCE.getKEY_TOKEN()); - String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); + for (String groupId : selectedGroupIds) { + retrofitBucket = + ApiUtils.getRetrofitBucketForAddGroupParticipant(user.getBaseUrl(), conversationToken, + groupId); - RetrofitBucket retrofitBucket; - for (String userId : selectedUserIds) { - retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(user.getBaseUrl(), conversationToken, - userId); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .blockingSubscribe(); - } - - for (String groupId : selectedGroupIds) { - retrofitBucket = ApiUtils.getRetrofitBucketForAddGroupParticipant(user.getBaseUrl(), conversationToken, - groupId); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .blockingSubscribe(); - } - - eventBus.post(new EventStatus(user.getId(), EventStatus.EventType.PARTICIPANTS_UPDATE, true)); - return Result.success(); + ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .blockingSubscribe(); } + + eventBus.post(new EventStatus(user.getId(), EventStatus.EventType.PARTICIPANTS_UPDATE, true)); + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.java index 8ce0e32fa..9e86808e7 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.java @@ -28,11 +28,11 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import autodagger.AutoInjector; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -50,114 +50,116 @@ import retrofit2.Retrofit; @AutoInjector(NextcloudTalkApplication.class) public class CapabilitiesWorker extends Worker { - public static final String TAG = "CapabilitiesWorker"; + public static final String TAG = "CapabilitiesWorker"; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - Retrofit retrofit; + @Inject + Retrofit retrofit; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - @Inject - OkHttpClient okHttpClient; + @Inject + OkHttpClient okHttpClient; - NcApi ncApi; + NcApi ncApi; - public CapabilitiesWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); + public CapabilitiesWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + private void updateUser(CapabilitiesOverall capabilitiesOverall, UserEntity internalUserEntity) { + try { + userUtils.createOrUpdateUser(null, null, + null, null, + null, null, null, internalUserEntity.getId(), + LoganSquare.serialize(capabilitiesOverall.getOcs().getData().getCapabilities()), null, + null) + .blockingSubscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(UserEntity userEntity) { + eventBus.post(new EventStatus(userEntity.getId(), + EventStatus.EventType.CAPABILITIES_FETCH, true)); + } + + @Override + public void onError(Throwable e) { + eventBus.post(new EventStatus(internalUserEntity.getId(), + EventStatus.EventType.CAPABILITIES_FETCH, false)); + } + + @Override + public void onComplete() { + + } + }); + } catch (IOException e) { + Log.e(TAG, "Failed to create or update user"); + } + } + + @NonNull + @Override + public Result doWork() { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + Data data = getInputData(); + + long internalUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); + + UserEntity userEntity; + List userEntityObjectList = new ArrayList(); + + if (internalUserId == -1 + || (userEntity = userUtils.getUserWithInternalId(internalUserId)) == null) { + userEntityObjectList = userUtils.getUsers(); + } else { + userEntityObjectList.add(userEntity); } - private void updateUser(CapabilitiesOverall capabilitiesOverall, UserEntity internalUserEntity) { - try { - userUtils.createOrUpdateUser(null, null, - null, null, - null, null, null, internalUserEntity.getId(), - LoganSquare.serialize(capabilitiesOverall.getOcs().getData().getCapabilities()), null, null) - .blockingSubscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + for (Object userEntityObject : userEntityObjectList) { + UserEntity internalUserEntity = (UserEntity) userEntityObject; - } + ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new + JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); - @Override - public void onNext(UserEntity userEntity) { - eventBus.post(new EventStatus(userEntity.getId(), - EventStatus.EventType.CAPABILITIES_FETCH, true)); - } + ncApi.getCapabilities(ApiUtils.getCredentials(internalUserEntity.getUsername(), + internalUserEntity.getToken()), + ApiUtils.getUrlForCapabilities(internalUserEntity.getBaseUrl())) + .retry(3) + .blockingSubscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - @Override - public void onError(Throwable e) { - eventBus.post(new EventStatus(internalUserEntity.getId(), - EventStatus.EventType.CAPABILITIES_FETCH, false)); - } + } - @Override - public void onComplete() { + @Override + public void onNext(CapabilitiesOverall capabilitiesOverall) { + updateUser(capabilitiesOverall, internalUserEntity); + } - } - }); - } catch (IOException e) { - Log.e(TAG, "Failed to create or update user"); - } + @Override + public void onError(Throwable e) { + eventBus.post(new EventStatus(internalUserEntity.getId(), + EventStatus.EventType.CAPABILITIES_FETCH, false)); + } + @Override + public void onComplete() { + + } + }); } - @NonNull - @Override - public Result doWork() { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - Data data = getInputData(); - - long internalUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); - - UserEntity userEntity; - List userEntityObjectList = new ArrayList(); - - if (internalUserId == -1 || (userEntity = userUtils.getUserWithInternalId(internalUserId)) == null) { - userEntityObjectList = userUtils.getUsers(); - } else { - userEntityObjectList.add(userEntity); - } - - for (Object userEntityObject : userEntityObjectList) { - UserEntity internalUserEntity = (UserEntity) userEntityObject; - - ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new - JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); - - ncApi.getCapabilities(ApiUtils.getCredentials(internalUserEntity.getUsername(), - internalUserEntity.getToken()), ApiUtils.getUrlForCapabilities(internalUserEntity.getBaseUrl())) - .retry(3) - .blockingSubscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(CapabilitiesOverall capabilitiesOverall) { - updateUser(capabilitiesOverall, internalUserEntity); - } - - @Override - public void onError(Throwable e) { - eventBus.post(new EventStatus(internalUserEntity.getId(), - EventStatus.EventType.CAPABILITIES_FETCH, false)); - - } - - @Override - public void onComplete() { - - } - }); - } - - return Result.success(); - } + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java index 593d0717b..e7049cecb 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java @@ -26,11 +26,11 @@ import androidx.work.Data; import androidx.work.Worker; import androidx.work.WorkerParameters; import autodagger.AutoInjector; -import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -46,70 +46,72 @@ import retrofit2.Retrofit; @AutoInjector(NextcloudTalkApplication.class) public class DeleteConversationWorker extends Worker { - @Inject - Retrofit retrofit; + @Inject + Retrofit retrofit; - @Inject - OkHttpClient okHttpClient; + @Inject + OkHttpClient okHttpClient; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - NcApi ncApi; + NcApi ncApi; - public DeleteConversationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public DeleteConversationWorker(@NonNull Context context, + @NonNull WorkerParameters workerParams) { + super(context, workerParams); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } + + @NonNull + @Override + public Result doWork() { + Data data = getInputData(); + long operationUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); + String conversationToken = data.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); + UserEntity operationUser = userUtils.getUserWithId(operationUserId); + + if (operationUser != null) { + String credentials = + ApiUtils.getCredentials(operationUser.getUsername(), operationUser.getToken()); + ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new + JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); + + EventStatus eventStatus = new EventStatus(operationUser.getId(), + EventStatus.EventType.CONVERSATION_UPDATE, true); + + ncApi.deleteRoom(credentials, ApiUtils.getRoom(operationUser.getBaseUrl(), conversationToken)) + .subscribeOn(Schedulers.io()) + .blockingSubscribe(new Observer() { + Disposable disposable; + + @Override + public void onSubscribe(Disposable d) { + disposable = d; + } + + @Override + public void onNext(GenericOverall genericOverall) { + eventBus.postSticky(eventStatus); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + disposable.dispose(); + } + }); } - @NonNull - @Override - public Result doWork() { - Data data = getInputData(); - long operationUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); - String conversationToken = data.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); - UserEntity operationUser = userUtils.getUserWithId(operationUserId); - - if (operationUser != null) { - String credentials = ApiUtils.getCredentials(operationUser.getUsername(), operationUser.getToken()); - ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new - JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); - - EventStatus eventStatus = new EventStatus(operationUser.getId(), - EventStatus.EventType.CONVERSATION_UPDATE, true); - - ncApi.deleteRoom(credentials, ApiUtils.getRoom(operationUser.getBaseUrl(), conversationToken)) - .subscribeOn(Schedulers.io()) - .blockingSubscribe(new Observer() { - Disposable disposable; - - @Override - public void onSubscribe(Disposable d) { - disposable = d; - - } - - @Override - public void onNext(GenericOverall genericOverall) { - eventBus.postSticky(eventStatus); - - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - disposable.dispose(); - } - }); - } - - return Result.success(); - } + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java index bee508c0c..3ef237196 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java @@ -26,11 +26,11 @@ import androidx.work.Data; import androidx.work.Worker; import androidx.work.WorkerParameters; import autodagger.AutoInjector; -import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -46,70 +46,72 @@ import retrofit2.Retrofit; @AutoInjector(NextcloudTalkApplication.class) public class LeaveConversationWorker extends Worker { - @Inject - Retrofit retrofit; + @Inject + Retrofit retrofit; - @Inject - OkHttpClient okHttpClient; + @Inject + OkHttpClient okHttpClient; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - NcApi ncApi; + NcApi ncApi; - public LeaveConversationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public LeaveConversationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } + + @NonNull + @Override + public Result doWork() { + Data data = getInputData(); + long operationUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); + String conversationToken = data.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); + UserEntity operationUser = userUtils.getUserWithId(operationUserId); + + if (operationUser != null) { + String credentials = + ApiUtils.getCredentials(operationUser.getUsername(), operationUser.getToken()); + ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new + JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); + + EventStatus eventStatus = new EventStatus(operationUser.getId(), + EventStatus.EventType.CONVERSATION_UPDATE, true); + + ncApi.removeSelfFromRoom(credentials, + ApiUtils.getUrlForRemoveSelfFromRoom(operationUser.getBaseUrl(), conversationToken)) + .subscribeOn(Schedulers.io()) + .blockingSubscribe(new Observer() { + Disposable disposable; + + @Override + public void onSubscribe(Disposable d) { + disposable = d; + } + + @Override + public void onNext(GenericOverall genericOverall) { + eventBus.postSticky(eventStatus); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + disposable.dispose(); + } + }); } - @NonNull - @Override - public Result doWork() { - Data data = getInputData(); - long operationUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); - String conversationToken = data.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); - UserEntity operationUser = userUtils.getUserWithId(operationUserId); - - if (operationUser != null) { - String credentials = ApiUtils.getCredentials(operationUser.getUsername(), operationUser.getToken()); - ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new - JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class); - - EventStatus eventStatus = new EventStatus(operationUser.getId(), - EventStatus.EventType.CONVERSATION_UPDATE, true); - - ncApi.removeSelfFromRoom(credentials, ApiUtils.getUrlForRemoveSelfFromRoom(operationUser.getBaseUrl(), conversationToken)) - .subscribeOn(Schedulers.io()) - .blockingSubscribe(new Observer() { - Disposable disposable; - - @Override - public void onSubscribe(Disposable d) { - disposable = d; - - } - - @Override - public void onNext(GenericOverall genericOverall) { - eventBus.postSticky(eventStatus); - - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - disposable.dispose(); - } - }); - } - - return Result.success(); - } + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java index ae6ccc3af..8a1be0c8b 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java @@ -27,19 +27,19 @@ import androidx.work.WorkerParameters; import com.nextcloud.talk.utils.PushUtils; public class PushRegistrationWorker extends Worker { - public static final String TAG = "PushRegistrationWorker"; + public static final String TAG = "PushRegistrationWorker"; - public PushRegistrationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - } + public PushRegistrationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } - @NonNull - @Override - public Result doWork() { - PushUtils pushUtils = new PushUtils(); - pushUtils.generateRsa2048KeyPair(); - pushUtils.pushRegistrationToServer(); + @NonNull + @Override + public Result doWork() { + PushUtils pushUtils = new PushUtils(); + pushUtils.generateRsa2048KeyPair(); + pushUtils.pushRegistrationToServer(); - return Result.success(); - } + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.java index 44b45113d..7913e87c2 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.java @@ -42,63 +42,64 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class ShareOperationWorker extends Worker { - @Inject - UserUtils userUtils; - @Inject - NcApi ncApi; - private long userId; - private UserEntity operationsUser; - private String roomToken; - private List filesArray = new ArrayList<>(); - private String credentials; - private String baseUrl; + @Inject + UserUtils userUtils; + @Inject + NcApi ncApi; + private long userId; + private UserEntity operationsUser; + private String roomToken; + private List filesArray = new ArrayList<>(); + private String credentials; + private String baseUrl; - public ShareOperationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - Data data = workerParams.getInputData(); - userId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), 0); - roomToken = data.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); - Collections.addAll(filesArray, data.getStringArray(BundleKeys.INSTANCE.getKEY_FILE_PATHS())); - operationsUser = userUtils.getUserWithId(userId); - credentials = ApiUtils.getCredentials(operationsUser.getUsername(), operationsUser.getToken()); - baseUrl = operationsUser.getBaseUrl(); + public ShareOperationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + Data data = workerParams.getInputData(); + userId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), 0); + roomToken = data.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); + Collections.addAll(filesArray, data.getStringArray(BundleKeys.INSTANCE.getKEY_FILE_PATHS())); + operationsUser = userUtils.getUserWithId(userId); + credentials = ApiUtils.getCredentials(operationsUser.getUsername(), operationsUser.getToken()); + baseUrl = operationsUser.getBaseUrl(); + } + + @NonNull + @Override + public Result doWork() { + for (int i = 0; i < filesArray.size(); i++) { + ncApi.createRemoteShare(credentials, + ApiUtils.getSharingUrl(baseUrl), + filesArray.get(i), + roomToken, + "10") + .subscribeOn(Schedulers.io()) + .blockingSubscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(Void aVoid) { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); } - - @NonNull - @Override - public Result doWork() { - for (int i = 0; i < filesArray.size(); i++) { - ncApi.createRemoteShare(credentials, - ApiUtils.getSharingUrl(baseUrl), - filesArray.get(i), - roomToken, - "10") - .subscribeOn(Schedulers.io()) - .blockingSubscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(Void aVoid) { - - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - return Result.success(); - } + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java index 893e7fcf6..3c5553a0e 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java @@ -30,12 +30,12 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import autodagger.AutoInjector; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.ExternalSignalingServer; -import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.EventStatus; +import com.nextcloud.talk.models.ExternalSignalingServer; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -49,100 +49,112 @@ import org.greenrobot.eventbus.EventBus; @AutoInjector(NextcloudTalkApplication.class) public class SignalingSettingsWorker extends Worker { - private static final String TAG = "SignalingSettingsJob"; + private static final String TAG = "SignalingSettingsJob"; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - public SignalingSettingsWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); + public SignalingSettingsWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + Data data = getInputData(); + + long internalUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); + + List userEntityList = new ArrayList<>(); + UserEntity userEntity; + if (internalUserId == -1 + || (userEntity = userUtils.getUserWithInternalId(internalUserId)) == null) { + userEntityList = userUtils.getUsers(); + } else { + userEntityList.add(userEntity); } - @NonNull - @Override - public Result doWork() { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + for (int i = 0; i < userEntityList.size(); i++) { + userEntity = userEntityList.get(i); + UserEntity finalUserEntity = userEntity; + ncApi.getSignalingSettings( + ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()), + ApiUtils.getUrlForSignalingSettings(userEntity.getBaseUrl())) + .blockingSubscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - Data data = getInputData(); + } - long internalUserId = data.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1); + @Override + public void onNext(SignalingSettingsOverall signalingSettingsOverall) { + ExternalSignalingServer externalSignalingServer; + externalSignalingServer = new ExternalSignalingServer(); + externalSignalingServer.setExternalSignalingServer( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()); + externalSignalingServer.setExternalSignalingTicket( + signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket()); - List userEntityList = new ArrayList<>(); - UserEntity userEntity; - if (internalUserId == -1 || (userEntity = userUtils.getUserWithInternalId(internalUserId)) == null) { - userEntityList = userUtils.getUsers(); - } else { - userEntityList.add(userEntity); - } + try { + userUtils.createOrUpdateUser(null, null, null, null, null, + null, null, finalUserEntity.getId(), null, null, + LoganSquare.serialize(externalSignalingServer)) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - for (int i = 0; i < userEntityList.size(); i++) { - userEntity = userEntityList.get(i); - UserEntity finalUserEntity = userEntity; - ncApi.getSignalingSettings(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()), - ApiUtils.getUrlForSignalingSettings(userEntity.getBaseUrl())) - .blockingSubscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + } - } + @Override + public void onNext(UserEntity userEntity) { + eventBus.post(new EventStatus(finalUserEntity.getId(), + EventStatus.EventType.SIGNALING_SETTINGS, true)); + } - @Override - public void onNext(SignalingSettingsOverall signalingSettingsOverall) { - ExternalSignalingServer externalSignalingServer; - externalSignalingServer = new ExternalSignalingServer(); - externalSignalingServer.setExternalSignalingServer(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()); - externalSignalingServer.setExternalSignalingTicket(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket()); + @Override + public void onError(Throwable e) { + eventBus.post(new EventStatus(finalUserEntity.getId(), + EventStatus.EventType.SIGNALING_SETTINGS, false)); + } - try { - userUtils.createOrUpdateUser(null, null, null, null, null, - null, null, finalUserEntity.getId(), null, null, LoganSquare.serialize(externalSignalingServer)) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void onComplete() { - } - - @Override - public void onNext(UserEntity userEntity) { - eventBus.post(new EventStatus(finalUserEntity.getId(), EventStatus.EventType.SIGNALING_SETTINGS, true)); - } - - @Override - public void onError(Throwable e) { - eventBus.post(new EventStatus(finalUserEntity.getId(), EventStatus.EventType.SIGNALING_SETTINGS, false)); - } - - @Override - public void onComplete() { - - } - }); - } catch (IOException e) { - Log.e(TAG, "Failed to serialize external signaling server"); - } - } - - @Override - public void onError(Throwable e) { - eventBus.post(new EventStatus(finalUserEntity.getId(), EventStatus.EventType.SIGNALING_SETTINGS, false)); - } - - @Override - public void onComplete() { - - } + } }); - } + } catch (IOException e) { + Log.e(TAG, "Failed to serialize external signaling server"); + } + } - OneTimeWorkRequest websocketConnectionsWorker = new OneTimeWorkRequest.Builder(WebsocketConnectionsWorker.class).build(); - WorkManager.getInstance().enqueue(websocketConnectionsWorker); + @Override + public void onError(Throwable e) { + eventBus.post( + new EventStatus(finalUserEntity.getId(), EventStatus.EventType.SIGNALING_SETTINGS, + false)); + } - return Result.success(); + @Override + public void onComplete() { + + } + }); } + + OneTimeWorkRequest websocketConnectionsWorker = + new OneTimeWorkRequest.Builder(WebsocketConnectionsWorker.class).build(); + WorkManager.getInstance().enqueue(websocketConnectionsWorker); + + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java index 832c1c5cf..adfdfab05 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java @@ -29,8 +29,8 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import autodagger.AutoInjector; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.ExternalSignalingServer; import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.models.ExternalSignalingServer; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.utils.database.user.UserUtils; import com.nextcloud.talk.webrtc.WebSocketConnectionHelper; @@ -41,43 +41,47 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class WebsocketConnectionsWorker extends Worker { - private static final String TAG = "WebsocketConnectionsWorker"; + private static final String TAG = "WebsocketConnectionsWorker"; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - public WebsocketConnectionsWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - } + public WebsocketConnectionsWorker(@NonNull Context context, + @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } - @SuppressLint("LongLogTag") - @NonNull - @Override - public Result doWork() { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + @SuppressLint("LongLogTag") + @NonNull + @Override + public Result doWork() { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - List userEntityList = userUtils.getUsers(); - UserEntity userEntity; - ExternalSignalingServer externalSignalingServer; - WebSocketConnectionHelper webSocketConnectionHelper = new WebSocketConnectionHelper(); - for (int i = 0; i < userEntityList.size(); i++) { - userEntity = userEntityList.get(i); - if (!TextUtils.isEmpty(userEntity.getExternalSignalingServer())) { - try { - externalSignalingServer = LoganSquare.parse(userEntity.getExternalSignalingServer(), ExternalSignalingServer.class); - if (!TextUtils.isEmpty(externalSignalingServer.getExternalSignalingServer()) && - !TextUtils.isEmpty(externalSignalingServer.getExternalSignalingTicket())) { - WebSocketConnectionHelper.getExternalSignalingInstanceForServer( - externalSignalingServer.getExternalSignalingServer(), - userEntity, externalSignalingServer.getExternalSignalingTicket(), - false); - } - } catch (IOException e) { - Log.e(TAG, "Failed to parse external signaling server"); - } - } + List userEntityList = userUtils.getUsers(); + UserEntity userEntity; + ExternalSignalingServer externalSignalingServer; + WebSocketConnectionHelper webSocketConnectionHelper = new WebSocketConnectionHelper(); + for (int i = 0; i < userEntityList.size(); i++) { + userEntity = userEntityList.get(i); + if (!TextUtils.isEmpty(userEntity.getExternalSignalingServer())) { + try { + externalSignalingServer = LoganSquare.parse(userEntity.getExternalSignalingServer(), + ExternalSignalingServer.class); + if (!TextUtils.isEmpty(externalSignalingServer.getExternalSignalingServer()) && + !TextUtils.isEmpty(externalSignalingServer.getExternalSignalingTicket())) { + WebSocketConnectionHelper.getExternalSignalingInstanceForServer( + externalSignalingServer.getExternalSignalingServer(), + userEntity, externalSignalingServer.getExternalSignalingTicket(), + false); + } + } catch (IOException e) { + Log.e(TAG, "Failed to parse external signaling server"); } - - return Result.success(); + } } + + return Result.success(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/ExternalSignalingServer.java b/app/src/main/java/com/nextcloud/talk/models/ExternalSignalingServer.java index 4871421b2..9ff9f865e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/ExternalSignalingServer.java +++ b/app/src/main/java/com/nextcloud/talk/models/ExternalSignalingServer.java @@ -29,8 +29,8 @@ import org.parceler.Parcel; @Parcel @JsonObject public class ExternalSignalingServer { - @JsonField(name = "externalSignalingServer") - String externalSignalingServer; - @JsonField(name = "externalSignalingTicket") - String externalSignalingTicket; + @JsonField(name = "externalSignalingServer") + String externalSignalingServer; + @JsonField(name = "externalSignalingTicket") + String externalSignalingTicket; } diff --git a/app/src/main/java/com/nextcloud/talk/models/ImportAccount.java b/app/src/main/java/com/nextcloud/talk/models/ImportAccount.java index b2f40f90e..1e6d8523e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/ImportAccount.java +++ b/app/src/main/java/com/nextcloud/talk/models/ImportAccount.java @@ -25,14 +25,14 @@ import lombok.Data; @Data public class ImportAccount { - public String username; - @Nullable - public String token; - public String baseUrl; + public String username; + @Nullable + public String token; + public String baseUrl; - public ImportAccount(String username, @Nullable String token, String baseUrl) { - this.username = username; - this.token = token; - this.baseUrl = baseUrl; - } + public ImportAccount(String username, @Nullable String token, String baseUrl) { + this.username = username; + this.token = token; + this.baseUrl = baseUrl; + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/LoginData.java b/app/src/main/java/com/nextcloud/talk/models/LoginData.java index 74b4b56e9..ce8fa8b38 100644 --- a/app/src/main/java/com/nextcloud/talk/models/LoginData.java +++ b/app/src/main/java/com/nextcloud/talk/models/LoginData.java @@ -26,7 +26,7 @@ import org.parceler.Parcel; @Parcel @Data public class LoginData { - String serverUrl; - String username; - String token; + String serverUrl; + String username; + String token; } diff --git a/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.java b/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.java index b36ab95b5..992576a5a 100644 --- a/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.java +++ b/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.java @@ -26,6 +26,6 @@ import org.parceler.Parcel; @Parcel @Data public class RetrofitBucket { - public String url; - public Map queryMap; + public String url; + public Map queryMap; } diff --git a/app/src/main/java/com/nextcloud/talk/models/RingtoneSettings.java b/app/src/main/java/com/nextcloud/talk/models/RingtoneSettings.java index 029948ef2..c3a5b13af 100644 --- a/app/src/main/java/com/nextcloud/talk/models/RingtoneSettings.java +++ b/app/src/main/java/com/nextcloud/talk/models/RingtoneSettings.java @@ -32,9 +32,9 @@ import org.parceler.Parcel; @JsonObject @Data public class RingtoneSettings { - @JsonField(name = "ringtoneUri", typeConverter = UriTypeConverter.class) - @Nullable - Uri ringtoneUri; - @JsonField(name = "ringtoneName") - String ringtoneName; + @JsonField(name = "ringtoneUri", typeConverter = UriTypeConverter.class) + @Nullable + Uri ringtoneUri; + @JsonField(name = "ringtoneName") + String ringtoneName; } diff --git a/app/src/main/java/com/nextcloud/talk/models/SignatureVerification.java b/app/src/main/java/com/nextcloud/talk/models/SignatureVerification.java index 8a979c604..4f25304d9 100644 --- a/app/src/main/java/com/nextcloud/talk/models/SignatureVerification.java +++ b/app/src/main/java/com/nextcloud/talk/models/SignatureVerification.java @@ -20,7 +20,6 @@ package com.nextcloud.talk.models; - import com.nextcloud.talk.models.database.UserEntity; import lombok.Data; import org.parceler.Parcel; @@ -28,6 +27,6 @@ import org.parceler.Parcel; @Data @Parcel public class SignatureVerification { - boolean signatureValid; - UserEntity userEntity; + boolean signatureValid; + UserEntity userEntity; } diff --git a/app/src/main/java/com/nextcloud/talk/models/database/ArbitraryStorage.java b/app/src/main/java/com/nextcloud/talk/models/database/ArbitraryStorage.java index 5a02d350f..83bc16061 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/ArbitraryStorage.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/ArbitraryStorage.java @@ -28,12 +28,12 @@ import java.io.Serializable; @Entity public interface ArbitraryStorage extends Parcelable, Persistable, Serializable { - @Key - long getAccountIdentifier(); + @Key + long getAccountIdentifier(); - String getKey(); + String getKey(); - String getObject(); + String getObject(); - String getValue(); + String getValue(); } diff --git a/app/src/main/java/com/nextcloud/talk/models/database/User.java b/app/src/main/java/com/nextcloud/talk/models/database/User.java index 42915dd3e..98b3411d7 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/User.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/User.java @@ -33,98 +33,103 @@ import java.util.HashMap; @Entity public interface User extends Parcelable, Persistable, Serializable { - String TAG = "UserEntity"; + String TAG = "UserEntity"; - @Key - @Generated - long getId(); + @Key + @Generated + long getId(); - String getUserId(); + String getUserId(); - String getUsername(); + String getUsername(); - String getBaseUrl(); + String getBaseUrl(); - String getToken(); + String getToken(); - String getDisplayName(); + String getDisplayName(); - String getPushConfigurationState(); + String getPushConfigurationState(); - String getCapabilities(); + String getCapabilities(); - String getClientCertificate(); + String getClientCertificate(); - String getExternalSignalingServer(); + String getExternalSignalingServer(); - boolean getCurrent(); + boolean getCurrent(); - boolean getScheduledForDeletion(); + boolean getScheduledForDeletion(); - default boolean hasNotificationsCapability(String capabilityName) { - if (getCapabilities() != null) { - try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities.getNotificationsCapability() != null && capabilities.getNotificationsCapability().getFeatures() != null) { - return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); - } - } catch (IOException e) { - Log.e(TAG, "Failed to get capabilities for the user"); - } + default boolean hasNotificationsCapability(String capabilityName) { + if (getCapabilities() != null) { + try { + Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + if (capabilities.getNotificationsCapability() != null + && capabilities.getNotificationsCapability().getFeatures() != null) { + return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); } - return false; + } catch (IOException e) { + Log.e(TAG, "Failed to get capabilities for the user"); + } } + return false; + } - default boolean hasExternalCapability(String capabilityName) { - if (getCapabilities() != null) { - try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities.getExternalCapability() != null && capabilities.getExternalCapability().containsKey("v1")) { - return capabilities.getExternalCapability().get("v1").contains("capabilityName"); - } - } catch (IOException e) { - Log.e(TAG, "Failed to get capabilities for the user"); - } + default boolean hasExternalCapability(String capabilityName) { + if (getCapabilities() != null) { + try { + Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + if (capabilities.getExternalCapability() != null && capabilities.getExternalCapability() + .containsKey("v1")) { + return capabilities.getExternalCapability().get("v1").contains("capabilityName"); } - return false; + } catch (IOException e) { + Log.e(TAG, "Failed to get capabilities for the user"); + } } + return false; + } - default boolean hasSpreedFeatureCapability(String capabilityName) { - if (getCapabilities() != null) { - try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities != null && capabilities.getSpreedCapability() != null && - capabilities.getSpreedCapability().getFeatures() != null) { - return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); - } - } catch (IOException e) { - Log.e(TAG, "Failed to get capabilities for the user"); - } + default boolean hasSpreedFeatureCapability(String capabilityName) { + if (getCapabilities() != null) { + try { + Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + if (capabilities != null && capabilities.getSpreedCapability() != null && + capabilities.getSpreedCapability().getFeatures() != null) { + return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); } - return false; + } catch (IOException e) { + Log.e(TAG, "Failed to get capabilities for the user"); + } } + return false; + } - default int getMessageMaxLength() { - if (getCapabilities() != null) { - Capabilities capabilities = null; - try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getConfig() != null - && capabilities.getSpreedCapability().getConfig().containsKey("chat")) { - HashMap chatConfigHashMap = capabilities.getSpreedCapability().getConfig().get("chat"); - if (chatConfigHashMap != null && chatConfigHashMap.containsKey("max-length")) { - int chatSize = Integer.parseInt(chatConfigHashMap.get("max-length")); - if (chatSize > 0) { - return chatSize; - } else { - return 1000; - } - } - } - } catch (IOException e) { - e.printStackTrace(); + default int getMessageMaxLength() { + if (getCapabilities() != null) { + Capabilities capabilities = null; + try { + capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + if (capabilities != null + && capabilities.getSpreedCapability() != null + && capabilities.getSpreedCapability().getConfig() != null + && capabilities.getSpreedCapability().getConfig().containsKey("chat")) { + HashMap chatConfigHashMap = + capabilities.getSpreedCapability().getConfig().get("chat"); + if (chatConfigHashMap != null && chatConfigHashMap.containsKey("max-length")) { + int chatSize = Integer.parseInt(chatConfigHashMap.get("max-length")); + if (chatSize > 0) { + return chatSize; + } else { + return 1000; } + } } - return 1000; + } catch (IOException e) { + e.printStackTrace(); + } } + return 1000; + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOCS.java index e75e8fd98..19c7e1d89 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOCS.java @@ -31,6 +31,6 @@ import org.parceler.Parcel; @Data @JsonObject public class AutocompleteOCS extends GenericOCS { - @JsonField(name = "data") - List data; + @JsonField(name = "data") + List data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOverall.java index eaeb87aca..5e7b301bf 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class AutocompleteOverall { - @JsonField(name = "ocs") - AutocompleteOCS ocs; + @JsonField(name = "ocs") + AutocompleteOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteUser.java b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteUser.java index 271563233..9ad897c02 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteUser.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/autocomplete/AutocompleteUser.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @Data @JsonObject public class AutocompleteUser { - @JsonField(name = "id") - String id; + @JsonField(name = "id") + String id; - @JsonField(name = "label") - String label; + @JsonField(name = "label") + String label; - @JsonField(name = "source") - String source; + @JsonField(name = "source") + String source; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java index fb93a7aef..dd44dab5a 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java @@ -31,15 +31,15 @@ import org.parceler.Parcel; @Data @JsonObject public class Capabilities { - @JsonField(name = "spreed") - SpreedCapability spreedCapability; + @JsonField(name = "spreed") + SpreedCapability spreedCapability; - @JsonField(name = "notifications") - NotificationsCapability notificationsCapability; + @JsonField(name = "notifications") + NotificationsCapability notificationsCapability; - @JsonField(name = "theming") - ThemingCapability themingCapability; + @JsonField(name = "theming") + ThemingCapability themingCapability; - @JsonField(name = "external") - HashMap> externalCapability; + @JsonField(name = "external") + HashMap> externalCapability; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesList.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesList.java index 056610225..9be731be0 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesList.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesList.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class CapabilitiesList { - @JsonField(name = "capabilities") - Capabilities capabilities; + @JsonField(name = "capabilities") + Capabilities capabilities; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOCS.java index c2a43fd0a..ebe422f25 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOCS.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class CapabilitiesOCS extends GenericOCS { - @JsonField(name = "data") - CapabilitiesList data; + @JsonField(name = "data") + CapabilitiesList data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOverall.java index 673aebbdf..c00305224 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/CapabilitiesOverall.java @@ -28,6 +28,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class CapabilitiesOverall { - @JsonField(name = "ocs") - CapabilitiesOCS ocs; + @JsonField(name = "ocs") + CapabilitiesOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java index 04716c0ba..4788f0ab2 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Data @JsonObject public class NotificationsCapability { - @JsonField(name = "ocs-endpoints") - List features; + @JsonField(name = "ocs-endpoints") + List features; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/SpreedCapability.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/SpreedCapability.java index 10764c0fc..c515508ea 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/SpreedCapability.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/SpreedCapability.java @@ -31,9 +31,9 @@ import org.parceler.Parcel; @Data @JsonObject public class SpreedCapability { - @JsonField(name = "features") - List features; + @JsonField(name = "features") + List features; - @JsonField(name = "config") - HashMap> config; + @JsonField(name = "config") + HashMap> config; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/ThemingCapability.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/ThemingCapability.java index f21a4b15c..e3df3f317 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/ThemingCapability.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/ThemingCapability.java @@ -29,33 +29,33 @@ import org.parceler.Parcel; @Data @JsonObject class ThemingCapability { - @JsonField(name = "name") - String name; + @JsonField(name = "name") + String name; - @JsonField(name = "url") - String url; + @JsonField(name = "url") + String url; - @JsonField(name = "slogan") - String slogan; + @JsonField(name = "slogan") + String slogan; - @JsonField(name = "color") - String color; + @JsonField(name = "color") + String color; - @JsonField(name = "color-text") - String colorText; + @JsonField(name = "color-text") + String colorText; - @JsonField(name = "color-element") - String colorElement; + @JsonField(name = "color-element") + String colorElement; - @JsonField(name = "logo") - String logo; + @JsonField(name = "logo") + String logo; - @JsonField(name = "background") - String background; + @JsonField(name = "background") + String background; - @JsonField(name = "background-plain") - boolean backgroundPlain; + @JsonField(name = "background-plain") + boolean backgroundPlain; - @JsonField(name = "background-default") - boolean backgroundDefault; + @JsonField(name = "background-default") + boolean backgroundDefault; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java index 73654e79f..6e3d31af3 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java @@ -45,233 +45,269 @@ import org.parceler.Parcel; @Data @JsonObject public class ChatMessage implements IMessage, MessageContentType, MessageContentType.Image { - @JsonIgnore - public boolean isGrouped; - @JsonIgnore - public boolean isOneToOneConversation; - @JsonIgnore - public UserEntity activeUser; - @JsonIgnore - public Map selectedIndividualHashMap; - @JsonIgnore - public boolean isLinkPreviewAllowed; - List messageTypesToIgnore = Arrays.asList(MessageType.REGULAR_TEXT_MESSAGE, - MessageType.SYSTEM_MESSAGE, MessageType.SINGLE_LINK_VIDEO_MESSAGE, - MessageType.SINGLE_LINK_AUDIO_MESSAGE, MessageType.SINGLE_LINK_MESSAGE); - @JsonField(name = "id") - public int jsonMessageId; - @JsonField(name = "token") - public String token; - // guests or users - @JsonField(name = "actorType") - public String actorType; - @JsonField(name = "actorId") - public String actorId; - // send when crafting a message - @JsonField(name = "actorDisplayName") - public String actorDisplayName; - @JsonField(name = "timestamp") - public long timestamp; - // send when crafting a message, max 1000 lines - @JsonField(name = "message") - public String message; - @JsonField(name = "messageParameters") - public HashMap> messageParameters; - @JsonField(name = "systemMessage", typeConverter = EnumSystemMessageTypeConverter.class) - public SystemMessageType systemMessageType; + @JsonIgnore + public boolean isGrouped; + @JsonIgnore + public boolean isOneToOneConversation; + @JsonIgnore + public UserEntity activeUser; + @JsonIgnore + public Map selectedIndividualHashMap; + @JsonIgnore + public boolean isLinkPreviewAllowed; + @JsonField(name = "id") + public int jsonMessageId; + @JsonField(name = "token") + public String token; + // guests or users + @JsonField(name = "actorType") + public String actorType; + @JsonField(name = "actorId") + public String actorId; + // send when crafting a message + @JsonField(name = "actorDisplayName") + public String actorDisplayName; + @JsonField(name = "timestamp") + public long timestamp; + // send when crafting a message, max 1000 lines + @JsonField(name = "message") + public String message; + @JsonField(name = "messageParameters") + public HashMap> messageParameters; + @JsonField(name = "systemMessage", typeConverter = EnumSystemMessageTypeConverter.class) + public SystemMessageType systemMessageType; + List messageTypesToIgnore = Arrays.asList(MessageType.REGULAR_TEXT_MESSAGE, + MessageType.SYSTEM_MESSAGE, MessageType.SINGLE_LINK_VIDEO_MESSAGE, + MessageType.SINGLE_LINK_AUDIO_MESSAGE, MessageType.SINGLE_LINK_MESSAGE); - private boolean hasFileAttachment() { - if (messageParameters != null && messageParameters.size() > 0) { - for (String key : messageParameters.keySet()) { - Map individualHashMap = messageParameters.get(key); - if (individualHashMap.get("type").equals("file")) { - return true; - } - } + private boolean hasFileAttachment() { + if (messageParameters != null && messageParameters.size() > 0) { + for (String key : messageParameters.keySet()) { + Map individualHashMap = messageParameters.get(key); + if (individualHashMap.get("type").equals("file")) { + return true; } - - return false; + } } - @Nullable - @Override - public String getImageUrl() { - if (messageParameters != null && messageParameters.size() > 0) { - for (String key : messageParameters.keySet()) { - Map individualHashMap = messageParameters.get(key); - if (individualHashMap.get("type").equals("file")) { - selectedIndividualHashMap = individualHashMap; - return (ApiUtils.getUrlForFilePreviewWithFileId(getActiveUser().getBaseUrl(), - individualHashMap.get("id"), NextcloudTalkApplication.Companion.getSharedApplication().getResources().getDimensionPixelSize(R.dimen.maximum_file_preview_size))); - } - } + return false; + } + + @Nullable + @Override + public String getImageUrl() { + if (messageParameters != null && messageParameters.size() > 0) { + for (String key : messageParameters.keySet()) { + Map individualHashMap = messageParameters.get(key); + if (individualHashMap.get("type").equals("file")) { + selectedIndividualHashMap = individualHashMap; + return (ApiUtils.getUrlForFilePreviewWithFileId(getActiveUser().getBaseUrl(), + individualHashMap.get("id"), NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getDimensionPixelSize(R.dimen.maximum_file_preview_size))); } - - if (!messageTypesToIgnore.contains(getMessageType()) && isLinkPreviewAllowed) { - return getMessage().trim(); - } - - return null; + } } - public MessageType getMessageType() { - if (!TextUtils.isEmpty(getSystemMessage())) { - return MessageType.SYSTEM_MESSAGE; - } - - if (hasFileAttachment()) { - return MessageType.SINGLE_NC_ATTACHMENT_MESSAGE; - } - - return TextMatchers.getMessageTypeFromString(getText()); + if (!messageTypesToIgnore.contains(getMessageType()) && isLinkPreviewAllowed) { + return getMessage().trim(); } - public Map getSelectedIndividualHashMap() { - return selectedIndividualHashMap; + return null; + } + + public MessageType getMessageType() { + if (!TextUtils.isEmpty(getSystemMessage())) { + return MessageType.SYSTEM_MESSAGE; } - public void setSelectedIndividualHashMap(Map selectedIndividualHashMap) { - this.selectedIndividualHashMap = selectedIndividualHashMap; + if (hasFileAttachment()) { + return MessageType.SINGLE_NC_ATTACHMENT_MESSAGE; } - @Override - public String getId() { - return Integer.toString(jsonMessageId); - } + return TextMatchers.getMessageTypeFromString(getText()); + } - @Override - public String getText() { - return ChatUtils.getParsedMessage(getMessage(), getMessageParameters()); - } + public Map getSelectedIndividualHashMap() { + return selectedIndividualHashMap; + } - public String getLastMessageDisplayText() { - if (getMessageType().equals(MessageType.REGULAR_TEXT_MESSAGE) || getMessageType().equals(MessageType.SYSTEM_MESSAGE)) { - return getText(); + public void setSelectedIndividualHashMap(Map selectedIndividualHashMap) { + this.selectedIndividualHashMap = selectedIndividualHashMap; + } + + @Override + public String getId() { + return Integer.toString(jsonMessageId); + } + + @Override + public String getText() { + return ChatUtils.getParsedMessage(getMessage(), getMessageParameters()); + } + + public String getLastMessageDisplayText() { + if (getMessageType().equals(MessageType.REGULAR_TEXT_MESSAGE) || getMessageType().equals( + MessageType.SYSTEM_MESSAGE)) { + return getText(); + } else { + if (getMessageType().equals(MessageType.SINGLE_LINK_GIPHY_MESSAGE) + || getMessageType().equals(MessageType.SINGLE_LINK_TENOR_MESSAGE) + || getMessageType().equals(MessageType.SINGLE_LINK_GIF_MESSAGE)) { + if (getActorId().equals(getActiveUser().getUserId())) { + return (NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_sent_a_gif_you)); } else { - if (getMessageType().equals(MessageType.SINGLE_LINK_GIPHY_MESSAGE) - || getMessageType().equals(MessageType.SINGLE_LINK_TENOR_MESSAGE) - || getMessageType().equals(MessageType.SINGLE_LINK_GIF_MESSAGE)) { - if (getActorId().equals(getActiveUser().getUserId())) { - return (NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_sent_a_gif_you)); - } else { - return (String.format(NextcloudTalkApplication.Companion.getSharedApplication().getResources().getString(R.string.nc_sent_a_gif), - !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest))); - } - } else if (getMessageType().equals(MessageType.SINGLE_NC_ATTACHMENT_MESSAGE)) { - if (getActorId().equals(getActiveUser().getUserId())) { - return (NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_sent_an_attachment_you)); - } else { - return (String.format(NextcloudTalkApplication.Companion.getSharedApplication().getResources().getString(R.string.nc_sent_an_attachment), - !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest))); - } - } else if (getMessageType().equals(MessageType.SINGLE_LINK_MESSAGE)) { - if (getActorId().equals(getActiveUser().getUserId())) { - return (NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_sent_a_link_you)); - } else { - return (String.format(NextcloudTalkApplication.Companion.getSharedApplication().getResources().getString(R.string.nc_sent_a_link), - !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest))); - } - } else if (getMessageType().equals(MessageType.SINGLE_LINK_AUDIO_MESSAGE)) { - if (getActorId().equals(getActiveUser().getUserId())) { - return (NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_sent_an_audio_you)); - } else { - return (String.format(NextcloudTalkApplication.Companion.getSharedApplication().getResources().getString(R.string.nc_sent_an_audio), - !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest))); - } - } else if (getMessageType().equals(MessageType.SINGLE_LINK_VIDEO_MESSAGE)) { - if (getActorId().equals(getActiveUser().getUserId())) { - return (NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_sent_a_video_you)); - } else { - return (String.format(NextcloudTalkApplication.Companion.getSharedApplication().getResources().getString(R.string.nc_sent_a_video), - !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest))); - } - } else if (getMessageType().equals(MessageType.SINGLE_LINK_IMAGE_MESSAGE)) { - if (getActorId().equals(getActiveUser().getUserId())) { - return (NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_sent_an_image_you)); - } else { - return (String.format(NextcloudTalkApplication.Companion.getSharedApplication().getResources().getString(R.string.nc_sent_an_image), - !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest))); - } - } + return (String.format(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getString(R.string.nc_sent_a_gif), + !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() + : NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest))); } - - return ""; + } else if (getMessageType().equals(MessageType.SINGLE_NC_ATTACHMENT_MESSAGE)) { + if (getActorId().equals(getActiveUser().getUserId())) { + return (NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_sent_an_attachment_you)); + } else { + return (String.format(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getString(R.string.nc_sent_an_attachment), + !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() + : NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest))); + } + } else if (getMessageType().equals(MessageType.SINGLE_LINK_MESSAGE)) { + if (getActorId().equals(getActiveUser().getUserId())) { + return (NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_sent_a_link_you)); + } else { + return (String.format(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getString(R.string.nc_sent_a_link), + !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() + : NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest))); + } + } else if (getMessageType().equals(MessageType.SINGLE_LINK_AUDIO_MESSAGE)) { + if (getActorId().equals(getActiveUser().getUserId())) { + return (NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_sent_an_audio_you)); + } else { + return (String.format(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getString(R.string.nc_sent_an_audio), + !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() + : NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest))); + } + } else if (getMessageType().equals(MessageType.SINGLE_LINK_VIDEO_MESSAGE)) { + if (getActorId().equals(getActiveUser().getUserId())) { + return (NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_sent_a_video_you)); + } else { + return (String.format(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getString(R.string.nc_sent_a_video), + !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() + : NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest))); + } + } else if (getMessageType().equals(MessageType.SINGLE_LINK_IMAGE_MESSAGE)) { + if (getActorId().equals(getActiveUser().getUserId())) { + return (NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_sent_an_image_you)); + } else { + return (String.format(NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getString(R.string.nc_sent_an_image), + !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() + : NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest))); + } + } } - @Override - public IUser getUser() { - return new IUser() { - @Override - public String getId() { - return actorId; - } + return ""; + } - @Override - public String getName() { - return actorDisplayName; - } + @Override + public IUser getUser() { + return new IUser() { + @Override + public String getId() { + return actorId; + } - @Override - public String getAvatar() { - if (getActorType().equals("users")) { - return ApiUtils.getUrlForAvatarWithName(getActiveUser().getBaseUrl(), actorId, R.dimen.avatar_size); - } else if (getActorType().equals("guests")) { - String apiId = - NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest); + @Override + public String getName() { + return actorDisplayName; + } - if (!TextUtils.isEmpty(getActorDisplayName())) { - apiId = getActorDisplayName(); - } - return ApiUtils.getUrlForAvatarWithNameForGuests(getActiveUser().getBaseUrl(), apiId, R.dimen.avatar_size); - } else { - return null; - } - } - }; - } + @Override + public String getAvatar() { + if (getActorType().equals("users")) { + return ApiUtils.getUrlForAvatarWithName(getActiveUser().getBaseUrl(), actorId, + R.dimen.avatar_size); + } else if (getActorType().equals("guests")) { + String apiId = + NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_guest); - @Override - public Date getCreatedAt() { - return new Date(timestamp * 1000L); - } + if (!TextUtils.isEmpty(getActorDisplayName())) { + apiId = getActorDisplayName(); + } + return ApiUtils.getUrlForAvatarWithNameForGuests(getActiveUser().getBaseUrl(), apiId, + R.dimen.avatar_size); + } else { + return null; + } + } + }; + } - @Override - public String getSystemMessage() { - return new EnumSystemMessageTypeConverter().convertToString(getSystemMessageType()); - } + @Override + public Date getCreatedAt() { + return new Date(timestamp * 1000L); + } - public enum MessageType { - REGULAR_TEXT_MESSAGE, - SYSTEM_MESSAGE, - SINGLE_LINK_GIPHY_MESSAGE, - SINGLE_LINK_TENOR_MESSAGE, - SINGLE_LINK_GIF_MESSAGE, - SINGLE_LINK_MESSAGE, - SINGLE_LINK_VIDEO_MESSAGE, - SINGLE_LINK_IMAGE_MESSAGE, - SINGLE_LINK_AUDIO_MESSAGE, - SINGLE_NC_ATTACHMENT_MESSAGE, - } + @Override + public String getSystemMessage() { + return new EnumSystemMessageTypeConverter().convertToString(getSystemMessageType()); + } - public enum SystemMessageType { - DUMMY, - CONVERSATION_CREATED, - CONVERSATION_RENAMED, - CALL_STARTED, - CALL_JOINED, - CALL_LEFT, - CALL_ENDED, - GUESTS_ALLOWED, - GUESTS_DISALLOWED, - PASSWORD_SET, - PASSWORD_REMOVED, - USER_ADDED, - USER_REMOVED, - MODERATOR_PROMOTED, - MODERATOR_DEMOTED, - FILE_SHARED, - LOBBY_NONE, - LOBBY_NON_MODERATORS, - LOBBY_OPEN_TO_EVERYONE - } + public enum MessageType { + REGULAR_TEXT_MESSAGE, + SYSTEM_MESSAGE, + SINGLE_LINK_GIPHY_MESSAGE, + SINGLE_LINK_TENOR_MESSAGE, + SINGLE_LINK_GIF_MESSAGE, + SINGLE_LINK_MESSAGE, + SINGLE_LINK_VIDEO_MESSAGE, + SINGLE_LINK_IMAGE_MESSAGE, + SINGLE_LINK_AUDIO_MESSAGE, + SINGLE_NC_ATTACHMENT_MESSAGE, + } + + public enum SystemMessageType { + DUMMY, + CONVERSATION_CREATED, + CONVERSATION_RENAMED, + CALL_STARTED, + CALL_JOINED, + CALL_LEFT, + CALL_ENDED, + GUESTS_ALLOWED, + GUESTS_DISALLOWED, + PASSWORD_SET, + PASSWORD_REMOVED, + USER_ADDED, + USER_REMOVED, + MODERATOR_PROMOTED, + MODERATOR_DEMOTED, + FILE_SHARED, + LOBBY_NONE, + LOBBY_NON_MODERATORS, + LOBBY_OPEN_TO_EVERYONE + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOCS.java index c43ae2780..c868a6f2f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOCS.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class ChatOCS extends GenericOCS { - @JsonField(name = "data") - public List data; + @JsonField(name = "data") + public List data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOverall.java index b89831f76..3c12a0a15 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class ChatOverall { - @JsonField(name = "ocs") - public ChatOCS ocs; + @JsonField(name = "ocs") + public ChatOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java index 92d7b2f05..92daef9f1 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java @@ -24,20 +24,21 @@ import java.util.HashMap; public class ChatUtils { - public static String getParsedMessage(String message, HashMap> messageParameters) { - if (messageParameters != null && messageParameters.size() > 0) { - for (String key : messageParameters.keySet()) { - HashMap individualHashMap = messageParameters.get(key); - if (individualHashMap.get("type").equals("user") || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call")) { - message = message.replaceAll("\\{" + key + "\\}", "@" + - messageParameters.get(key).get("name")); - } else if (individualHashMap.get("type").equals("file")) { - message = message.replaceAll("\\{" + key + "\\}", messageParameters.get(key).get("name")); - } - } + public static String getParsedMessage(String message, + HashMap> messageParameters) { + if (messageParameters != null && messageParameters.size() > 0) { + for (String key : messageParameters.keySet()) { + HashMap individualHashMap = messageParameters.get(key); + if (individualHashMap.get("type").equals("user") || individualHashMap.get("type") + .equals("guest") || individualHashMap.get("type").equals("call")) { + message = message.replaceAll("\\{" + key + "\\}", "@" + + messageParameters.get(key).get("name")); + } else if (individualHashMap.get("type").equals("file")) { + message = message.replaceAll("\\{" + key + "\\}", messageParameters.get(key).get("name")); } - - - return message; + } } + + return message; + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java index f974a32e6..2e8ee91d3 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java @@ -41,127 +41,131 @@ import org.parceler.Parcel; @Data @JsonObject public class Conversation { - @JsonField(name = "id") - public String roomId; - @JsonField(name = "token") - public String token; - @JsonField(name = "name") - public String name; - @JsonField(name = "displayName") - public String displayName; - @JsonField(name = "type", typeConverter = EnumRoomTypeConverter.class) - public ConversationType type; - @JsonField(name = "count") - public long count; - @JsonField(name = "lastPing") - public long lastPing; - @JsonField(name = "numGuests") - public long numberOfGuests; - @JsonField(name = "guestList") - public HashMap> guestList; - @JsonField(name = "participants") - public HashMap> participants; - @JsonField(name = "participantType", typeConverter = EnumParticipantTypeConverter.class) - public Participant.ParticipantType participantType; - @JsonField(name = "hasPassword") - public boolean hasPassword; - @JsonField(name = "sessionId") - public String sessionId; - public String password; - @JsonField(name = "isFavorite") - public boolean isFavorite; - @JsonField(name = "lastActivity") - public long lastActivity; - @JsonField(name = "unreadMessages") - public int unreadMessages; - @JsonField(name = "unreadMention") - public boolean unreadMention; - @JsonField(name = "lastMessage") - public ChatMessage lastMessage; - @JsonField(name = "objectType") - public String objectType; - @JsonField(name = "notificationLevel", typeConverter = EnumNotificationLevelConverter.class) - public NotificationLevel notificationLevel; - @JsonField(name = "readOnly", typeConverter = EnumReadOnlyConversationConverter.class) - public ConversationReadOnlyState conversationReadOnlyState; - @JsonField(name = "lobbyState", typeConverter = EnumLobbyStateConverter.class) - public LobbyState lobbyState; - @JsonField(name = "lobbyTimer") - public Long lobbyTimer; - @JsonField(name = "lastReadMessage") - public int lastReadMessage; + @JsonField(name = "id") + public String roomId; + @JsonField(name = "token") + public String token; + @JsonField(name = "name") + public String name; + @JsonField(name = "displayName") + public String displayName; + @JsonField(name = "type", typeConverter = EnumRoomTypeConverter.class) + public ConversationType type; + @JsonField(name = "count") + public long count; + @JsonField(name = "lastPing") + public long lastPing; + @JsonField(name = "numGuests") + public long numberOfGuests; + @JsonField(name = "guestList") + public HashMap> guestList; + @JsonField(name = "participants") + public HashMap> participants; + @JsonField(name = "participantType", typeConverter = EnumParticipantTypeConverter.class) + public Participant.ParticipantType participantType; + @JsonField(name = "hasPassword") + public boolean hasPassword; + @JsonField(name = "sessionId") + public String sessionId; + public String password; + @JsonField(name = "isFavorite") + public boolean isFavorite; + @JsonField(name = "lastActivity") + public long lastActivity; + @JsonField(name = "unreadMessages") + public int unreadMessages; + @JsonField(name = "unreadMention") + public boolean unreadMention; + @JsonField(name = "lastMessage") + public ChatMessage lastMessage; + @JsonField(name = "objectType") + public String objectType; + @JsonField(name = "notificationLevel", typeConverter = EnumNotificationLevelConverter.class) + public NotificationLevel notificationLevel; + @JsonField(name = "readOnly", typeConverter = EnumReadOnlyConversationConverter.class) + public ConversationReadOnlyState conversationReadOnlyState; + @JsonField(name = "lobbyState", typeConverter = EnumLobbyStateConverter.class) + public LobbyState lobbyState; + @JsonField(name = "lobbyTimer") + public Long lobbyTimer; + @JsonField(name = "lastReadMessage") + public int lastReadMessage; - public boolean isPublic() { - return (ConversationType.ROOM_PUBLIC_CALL.equals(type)); + public boolean isPublic() { + return (ConversationType.ROOM_PUBLIC_CALL.equals(type)); + } + + public boolean isGuest() { + return (Participant.ParticipantType.GUEST.equals(participantType) || + Participant.ParticipantType.USER_FOLLOWING_LINK.equals(participantType)); + } + + private boolean isLockedOneToOne(UserEntity conversationUser) { + return (getType() == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + && conversationUser.hasSpreedFeatureCapability("locked-one-to-one-rooms")); + } + + public boolean canModerate(UserEntity conversationUser) { + return ((Participant.ParticipantType.OWNER.equals(participantType) + || Participant.ParticipantType.MODERATOR.equals(participantType)) && !isLockedOneToOne( + conversationUser)); + } + + public boolean shouldShowLobby(UserEntity conversationUser) { + return LobbyState.LOBBY_STATE_MODERATORS_ONLY.equals(getLobbyState()) && !canModerate( + conversationUser); + } + + public boolean isLobbyViewApplicable(UserEntity conversationUser) { + return !canModerate(conversationUser) && (getType() == ConversationType.ROOM_GROUP_CALL + || getType() == ConversationType.ROOM_PUBLIC_CALL); + } + + public boolean isNameEditable(UserEntity conversationUser) { + return (canModerate(conversationUser) && !ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL.equals( + type)); + } + + public boolean canLeave(UserEntity conversationUser) { + return !canModerate(conversationUser) || (getType() + != ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && getParticipants().size() > 1); + } + + public String getDeleteWarningMessage() { + Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); + if (getType() == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { + return String.format(resources.getString(R.string.nc_delete_conversation_one2one), + getDisplayName()); + } else if (getParticipants().size() > 1) { + return resources.getString(R.string.nc_delete_conversation_more); } - public boolean isGuest() { - return (Participant.ParticipantType.GUEST.equals(participantType) || - Participant.ParticipantType.USER_FOLLOWING_LINK.equals(participantType)); - } + return resources.getString(R.string.nc_delete_conversation_default); + } + public enum NotificationLevel { + DEFAULT, + ALWAYS, + MENTION, + NEVER + } - private boolean isLockedOneToOne(UserEntity conversationUser) { - return (getType() == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && conversationUser.hasSpreedFeatureCapability("locked-one-to-one-rooms")); - } + public enum LobbyState { + LOBBY_STATE_ALL_PARTICIPANTS, + LOBBY_STATE_MODERATORS_ONLY + } - public boolean canModerate(UserEntity conversationUser) { - return ((Participant.ParticipantType.OWNER.equals(participantType) - || Participant.ParticipantType.MODERATOR.equals(participantType)) && !isLockedOneToOne(conversationUser)); - } - - public boolean shouldShowLobby(UserEntity conversationUser) { - return LobbyState.LOBBY_STATE_MODERATORS_ONLY.equals(getLobbyState()) && !canModerate(conversationUser); - } - - public boolean isLobbyViewApplicable(UserEntity conversationUser) { - return !canModerate(conversationUser) && (getType() == ConversationType.ROOM_GROUP_CALL || getType() == ConversationType.ROOM_PUBLIC_CALL); - } - public boolean isNameEditable(UserEntity conversationUser) { - return (canModerate(conversationUser) && !ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL.equals(type)); - } - - public boolean canLeave(UserEntity conversationUser) { - return !canModerate(conversationUser) || (getType() != ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && getParticipants().size() > 1); - - } - - public String getDeleteWarningMessage() { - Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); - if (getType() == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { - return String.format(resources.getString(R.string.nc_delete_conversation_one2one), - getDisplayName()); - } else if (getParticipants().size() > 1) { - return resources.getString(R.string.nc_delete_conversation_more); - } - - return resources.getString(R.string.nc_delete_conversation_default); - } - - public enum NotificationLevel { - DEFAULT, - ALWAYS, - MENTION, - NEVER - } - - public enum LobbyState { - LOBBY_STATE_ALL_PARTICIPANTS, - LOBBY_STATE_MODERATORS_ONLY - } - - public enum ConversationReadOnlyState { - CONVERSATION_READ_WRITE, - CONVERSATION_READ_ONLY - } - - @Parcel - public enum ConversationType { - DUMMY, - ROOM_TYPE_ONE_TO_ONE_CALL, - ROOM_GROUP_CALL, - ROOM_PUBLIC_CALL, - ROOM_SYSTEM - } + public enum ConversationReadOnlyState { + CONVERSATION_READ_WRITE, + CONVERSATION_READ_ONLY + } + @Parcel + public enum ConversationType { + DUMMY, + ROOM_TYPE_ONE_TO_ONE_CALL, + ROOM_GROUP_CALL, + ROOM_PUBLIC_CALL, + ROOM_SYSTEM + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOCS.java index b57698357..769fd0bc2 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOCS.java @@ -28,6 +28,6 @@ import lombok.Data; @Data @JsonObject public class RoomOCS extends GenericOCS { - @JsonField(name = "data") - public Conversation data; + @JsonField(name = "data") + public Conversation data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOverall.java index ef0a0e720..850147d1a 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomOverall.java @@ -27,6 +27,6 @@ import lombok.Data; @Data @JsonObject public class RoomOverall { - @JsonField(name = "ocs") - public RoomOCS ocs; + @JsonField(name = "ocs") + public RoomOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOCS.java index 070242718..30117a84e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOCS.java @@ -31,6 +31,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class RoomsOCS extends GenericOCS { - @JsonField(name = "data") - public List data; + @JsonField(name = "data") + public List data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOverall.java index db70df2f9..e79c760d1 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/RoomsOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class RoomsOverall { - @JsonField(name = "ocs") - public RoomsOCS ocs; + @JsonField(name = "ocs") + public RoomsOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumLobbyStateConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumLobbyStateConverter.java index 99eaa4dc5..e85d4490a 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumLobbyStateConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumLobbyStateConverter.java @@ -24,27 +24,27 @@ import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter; import com.nextcloud.talk.models.json.conversations.Conversation; public class EnumLobbyStateConverter extends IntBasedTypeConverter { - @Override - public Conversation.LobbyState getFromInt(int i) { - switch (i) { - case 0: - return Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS; - case 1: - return Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY; - default: - return Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS; - } + @Override + public Conversation.LobbyState getFromInt(int i) { + switch (i) { + case 0: + return Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS; + case 1: + return Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY; + default: + return Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS; } + } - @Override - public int convertToInt(Conversation.LobbyState object) { - switch (object) { - case LOBBY_STATE_ALL_PARTICIPANTS: - return 0; - case LOBBY_STATE_MODERATORS_ONLY: - return 1; - default: - return 0; - } + @Override + public int convertToInt(Conversation.LobbyState object) { + switch (object) { + case LOBBY_STATE_ALL_PARTICIPANTS: + return 0; + case LOBBY_STATE_MODERATORS_ONLY: + return 1; + default: + return 0; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumNotificationLevelConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumNotificationLevelConverter.java index 1772b4370..c9eb11e5f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumNotificationLevelConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumNotificationLevelConverter.java @@ -23,37 +23,37 @@ package com.nextcloud.talk.models.json.converters; import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter; import com.nextcloud.talk.models.json.conversations.Conversation; -public class EnumNotificationLevelConverter extends IntBasedTypeConverter { - @Override - public Conversation.NotificationLevel getFromInt(int i) { - switch (i) { - case 0: - return Conversation.NotificationLevel.DEFAULT; - case 1: - return Conversation.NotificationLevel.ALWAYS; - case 2: - return Conversation.NotificationLevel.MENTION; - case 3: - return Conversation.NotificationLevel.NEVER; - default: - return Conversation.NotificationLevel.DEFAULT; - } +public class EnumNotificationLevelConverter + extends IntBasedTypeConverter { + @Override + public Conversation.NotificationLevel getFromInt(int i) { + switch (i) { + case 0: + return Conversation.NotificationLevel.DEFAULT; + case 1: + return Conversation.NotificationLevel.ALWAYS; + case 2: + return Conversation.NotificationLevel.MENTION; + case 3: + return Conversation.NotificationLevel.NEVER; + default: + return Conversation.NotificationLevel.DEFAULT; } + } - @Override - public int convertToInt(Conversation.NotificationLevel object) { - switch (object) { - case DEFAULT: - return 0; - case ALWAYS: - return 1; - case MENTION: - return 2; - case NEVER: - return 3; - default: - return 0; - } + @Override + public int convertToInt(Conversation.NotificationLevel object) { + switch (object) { + case DEFAULT: + return 0; + case ALWAYS: + return 1; + case MENTION: + return 2; + case NEVER: + return 3; + default: + return 0; } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumParticipantTypeConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumParticipantTypeConverter.java index 86a0639c3..a66507f90 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumParticipantTypeConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumParticipantTypeConverter.java @@ -23,43 +23,43 @@ package com.nextcloud.talk.models.json.converters; import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter; import com.nextcloud.talk.models.json.participants.Participant; -public class EnumParticipantTypeConverter extends IntBasedTypeConverter { - @Override - public Participant.ParticipantType getFromInt(int i) { - switch (i) { - case 1: - return Participant.ParticipantType.OWNER; - case 2: - return Participant.ParticipantType.MODERATOR; - case 3: - return Participant.ParticipantType.USER; - case 4: - return Participant.ParticipantType.GUEST; - case 5: - return Participant.ParticipantType.USER_FOLLOWING_LINK; - default: - return Participant.ParticipantType.DUMMY; - } - +public class EnumParticipantTypeConverter + extends IntBasedTypeConverter { + @Override + public Participant.ParticipantType getFromInt(int i) { + switch (i) { + case 1: + return Participant.ParticipantType.OWNER; + case 2: + return Participant.ParticipantType.MODERATOR; + case 3: + return Participant.ParticipantType.USER; + case 4: + return Participant.ParticipantType.GUEST; + case 5: + return Participant.ParticipantType.USER_FOLLOWING_LINK; + default: + return Participant.ParticipantType.DUMMY; } + } - @Override - public int convertToInt(Participant.ParticipantType object) { - switch (object) { - case DUMMY: - return 0; - case OWNER: - return 1; - case MODERATOR: - return 2; - case USER: - return 3; - case GUEST: - return 4; - case USER_FOLLOWING_LINK: - return 5; - default: - return 0; - } + @Override + public int convertToInt(Participant.ParticipantType object) { + switch (object) { + case DUMMY: + return 0; + case OWNER: + return 1; + case MODERATOR: + return 2; + case USER: + return 3; + case GUEST: + return 4; + case USER_FOLLOWING_LINK: + return 5; + default: + return 0; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumReadOnlyConversationConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumReadOnlyConversationConverter.java index 2d4ab61c7..877b79207 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumReadOnlyConversationConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumReadOnlyConversationConverter.java @@ -23,29 +23,29 @@ package com.nextcloud.talk.models.json.converters; import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter; import com.nextcloud.talk.models.json.conversations.Conversation; -public class EnumReadOnlyConversationConverter extends IntBasedTypeConverter { - @Override - public Conversation.ConversationReadOnlyState getFromInt(int i) { - switch (i) { - case 0: - return Conversation.ConversationReadOnlyState.CONVERSATION_READ_WRITE; - case 1: - return Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY; - default: - return Conversation.ConversationReadOnlyState.CONVERSATION_READ_WRITE; - } +public class EnumReadOnlyConversationConverter + extends IntBasedTypeConverter { + @Override + public Conversation.ConversationReadOnlyState getFromInt(int i) { + switch (i) { + case 0: + return Conversation.ConversationReadOnlyState.CONVERSATION_READ_WRITE; + case 1: + return Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY; + default: + return Conversation.ConversationReadOnlyState.CONVERSATION_READ_WRITE; } + } - @Override - public int convertToInt(Conversation.ConversationReadOnlyState object) { - switch (object) { - case CONVERSATION_READ_WRITE: - return 0; - case CONVERSATION_READ_ONLY: - return 1; - default: - return 0; - } + @Override + public int convertToInt(Conversation.ConversationReadOnlyState object) { + switch (object) { + case CONVERSATION_READ_WRITE: + return 0; + case CONVERSATION_READ_ONLY: + return 1; + default: + return 0; } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java index ed4e02513..7c1744a88 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java @@ -24,37 +24,37 @@ import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter; import com.nextcloud.talk.models.json.conversations.Conversation; public class EnumRoomTypeConverter extends IntBasedTypeConverter { - @Override - public Conversation.ConversationType getFromInt(int i) { - switch (i) { - case 1: - return Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL; - case 2: - return Conversation.ConversationType.ROOM_GROUP_CALL; - case 3: - return Conversation.ConversationType.ROOM_PUBLIC_CALL; - case 4: - return Conversation.ConversationType.ROOM_SYSTEM; - default: - return Conversation.ConversationType.DUMMY; - } + @Override + public Conversation.ConversationType getFromInt(int i) { + switch (i) { + case 1: + return Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL; + case 2: + return Conversation.ConversationType.ROOM_GROUP_CALL; + case 3: + return Conversation.ConversationType.ROOM_PUBLIC_CALL; + case 4: + return Conversation.ConversationType.ROOM_SYSTEM; + default: + return Conversation.ConversationType.DUMMY; } + } - @Override - public int convertToInt(Conversation.ConversationType object) { - switch (object) { - case DUMMY: - return 0; - case ROOM_TYPE_ONE_TO_ONE_CALL: - return 1; - case ROOM_GROUP_CALL: - return 2; - case ROOM_PUBLIC_CALL: - return 3; - case ROOM_SYSTEM: - return 4; - default: - return 0; - } + @Override + public int convertToInt(Conversation.ConversationType object) { + switch (object) { + case DUMMY: + return 0; + case ROOM_TYPE_ONE_TO_ONE_CALL: + return 1; + case ROOM_GROUP_CALL: + return 2; + case ROOM_PUBLIC_CALL: + return 3; + case ROOM_SYSTEM: + return 4; + default: + return 0; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt index bca1a8066..0256c3710 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt @@ -60,56 +60,56 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.USER_RE */ class EnumSystemMessageTypeConverter : StringBasedTypeConverter() { - override fun getFromString(string: String): ChatMessage.SystemMessageType { - when (string) { - "conversation_created" -> return CONVERSATION_CREATED - "conversation_renamed" -> return CONVERSATION_RENAMED - "call_started" -> return CALL_STARTED - "call_joined" -> return CALL_JOINED - "call_left" -> return CALL_LEFT - "call_ended" -> return CALL_ENDED - "guests_allowed" -> return GUESTS_ALLOWED - "guests_disallowed" -> return GUESTS_DISALLOWED - "password_set" -> return PASSWORD_SET - "password_removed" -> return PASSWORD_REMOVED - "user_added" -> return USER_ADDED - "user_removed" -> return USER_REMOVED - "moderator_promoted" -> return MODERATOR_PROMOTED - "moderator_demoted" -> return MODERATOR_DEMOTED - "file_shared" -> return FILE_SHARED - "lobby_none" -> return LOBBY_NONE - "lobby_non_moderators" -> return LOBBY_NON_MODERATORS - "lobby_timer_reached" -> return LOBBY_OPEN_TO_EVERYONE - else -> return DUMMY - } + override fun getFromString(string: String): ChatMessage.SystemMessageType { + when (string) { + "conversation_created" -> return CONVERSATION_CREATED + "conversation_renamed" -> return CONVERSATION_RENAMED + "call_started" -> return CALL_STARTED + "call_joined" -> return CALL_JOINED + "call_left" -> return CALL_LEFT + "call_ended" -> return CALL_ENDED + "guests_allowed" -> return GUESTS_ALLOWED + "guests_disallowed" -> return GUESTS_DISALLOWED + "password_set" -> return PASSWORD_SET + "password_removed" -> return PASSWORD_REMOVED + "user_added" -> return USER_ADDED + "user_removed" -> return USER_REMOVED + "moderator_promoted" -> return MODERATOR_PROMOTED + "moderator_demoted" -> return MODERATOR_DEMOTED + "file_shared" -> return FILE_SHARED + "lobby_none" -> return LOBBY_NONE + "lobby_non_moderators" -> return LOBBY_NON_MODERATORS + "lobby_timer_reached" -> return LOBBY_OPEN_TO_EVERYONE + else -> return DUMMY + } + } + + override fun convertToString(`object`: ChatMessage.SystemMessageType?): String { + + if (`object` == null) { + return "" } - override fun convertToString(`object`: ChatMessage.SystemMessageType?): String { - - if (`object` == null) { - return "" - } - - when (`object`) { - CONVERSATION_CREATED -> return "conversation_created" - CONVERSATION_RENAMED -> return "conversation_renamed" - CALL_STARTED -> return "call_started" - CALL_JOINED -> return "call_joined" - CALL_LEFT -> return "call_left" - CALL_ENDED -> return "call_ended" - GUESTS_ALLOWED -> return "guests_allowed" - GUESTS_DISALLOWED -> return "guests_disallowed" - PASSWORD_SET -> return "password_set" - PASSWORD_REMOVED -> return "password_removed" - USER_ADDED -> return "user_added" - USER_REMOVED -> return "user_removed" - MODERATOR_PROMOTED -> return "moderator_promoted" - MODERATOR_DEMOTED -> return "moderator_demoted" - FILE_SHARED -> return "file_shared" - LOBBY_NONE -> return "lobby_none" - LOBBY_NON_MODERATORS -> return "lobby_non_moderators" - LOBBY_OPEN_TO_EVERYONE -> return "lobby_timer_reached" - else -> return "" - } + when (`object`) { + CONVERSATION_CREATED -> return "conversation_created" + CONVERSATION_RENAMED -> return "conversation_renamed" + CALL_STARTED -> return "call_started" + CALL_JOINED -> return "call_joined" + CALL_LEFT -> return "call_left" + CALL_ENDED -> return "call_ended" + GUESTS_ALLOWED -> return "guests_allowed" + GUESTS_DISALLOWED -> return "guests_disallowed" + PASSWORD_SET -> return "password_set" + PASSWORD_REMOVED -> return "password_removed" + USER_ADDED -> return "user_added" + USER_REMOVED -> return "user_removed" + MODERATOR_PROMOTED -> return "moderator_promoted" + MODERATOR_DEMOTED -> return "moderator_demoted" + FILE_SHARED -> return "file_shared" + LOBBY_NONE -> return "lobby_none" + LOBBY_NON_MODERATORS -> return "lobby_non_moderators" + LOBBY_OPEN_TO_EVERYONE -> return "lobby_timer_reached" + else -> return "" } + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/LoganSquareJodaTimeConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/LoganSquareJodaTimeConverter.java index 9e8709224..62eef3ad9 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/LoganSquareJodaTimeConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/LoganSquareJodaTimeConverter.java @@ -28,26 +28,27 @@ import org.joda.time.DateTime; public class LoganSquareJodaTimeConverter implements TypeConverter { - @Override - public DateTime parse(JsonParser jsonParser) throws IOException { - final String dateString = jsonParser.getValueAsString(); - if (dateString == null) { - return null; - } - try { - return DateTime.parse(dateString); - } catch (final RuntimeException exception) { - Log.e("NC", exception.getLocalizedMessage()); - } - return null; + @Override + public DateTime parse(JsonParser jsonParser) throws IOException { + final String dateString = jsonParser.getValueAsString(); + if (dateString == null) { + return null; } + try { + return DateTime.parse(dateString); + } catch (final RuntimeException exception) { + Log.e("NC", exception.getLocalizedMessage()); + } + return null; + } - @Override - public void serialize(DateTime object, String fieldName, boolean writeFieldNameForObject, JsonGenerator jsonGenerator) throws IOException { - if (fieldName != null) { - jsonGenerator.writeStringField(fieldName, object != null ? object.toString() : null); - } else { - jsonGenerator.writeString(object != null ? object.toString() : null); - } + @Override + public void serialize(DateTime object, String fieldName, boolean writeFieldNameForObject, + JsonGenerator jsonGenerator) throws IOException { + if (fieldName != null) { + jsonGenerator.writeStringField(fieldName, object != null ? object.toString() : null); + } else { + jsonGenerator.writeString(object != null ? object.toString() : null); } + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/ObjectParcelConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/ObjectParcelConverter.java index 99f42075b..ea4066064 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/ObjectParcelConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/ObjectParcelConverter.java @@ -25,13 +25,13 @@ import org.parceler.ParcelConverter; import org.parceler.Parcels; public class ObjectParcelConverter implements ParcelConverter { - @Override - public void toParcel(Object input, Parcel parcel) { - parcel.writeParcelable(Parcels.wrap(input), 0); - } + @Override + public void toParcel(Object input, Parcel parcel) { + parcel.writeParcelable(Parcels.wrap(input), 0); + } - @Override - public Object fromParcel(Parcel parcel) { - return parcel.readParcelable(Object.class.getClassLoader()); - } + @Override + public Object fromParcel(Parcel parcel) { + return parcel.readParcelable(Object.class.getClassLoader()); + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/UriTypeConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/UriTypeConverter.java index 7bfd2e962..765119aa0 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/UriTypeConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/UriTypeConverter.java @@ -25,21 +25,21 @@ import android.text.TextUtils; import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter; public class UriTypeConverter extends StringBasedTypeConverter { - @Override - public Uri getFromString(String string) { - if (!TextUtils.isEmpty(string)) { - return Uri.parse(string); - } else { - return null; - } + @Override + public Uri getFromString(String string) { + if (!TextUtils.isEmpty(string)) { + return Uri.parse(string); + } else { + return null; } + } - @Override - public String convertToString(Uri object) { - if (object != null) { - return object.toString(); - } else { - return null; - } + @Override + public String convertToString(Uri object) { + if (object != null) { + return object.toString(); + } else { + return null; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericMeta.java b/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericMeta.java index 490bf7acc..fe2d7d142 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericMeta.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericMeta.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @Data @JsonObject(serializeNullObjects = true) public class GenericMeta { - @JsonField(name = "status") - public String status; + @JsonField(name = "status") + public String status; - @JsonField(name = "statuscode") - public int statusCode; + @JsonField(name = "statuscode") + public int statusCode; - @JsonField(name = "message") - public String message; + @JsonField(name = "message") + public String message; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOCS.java index 335ed52cf..900c0ee60 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOCS.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class GenericOCS { - @JsonField(name = "meta") - public GenericMeta meta; + @JsonField(name = "meta") + public GenericMeta meta; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOverall.java index d750a6255..7fb0f017d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/generic/GenericOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class GenericOverall { - @JsonField(name = "ocs") - public GenericOCS ocs; + @JsonField(name = "ocs") + public GenericOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.java b/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.java index 11904b76d..33ed35d43 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.java @@ -29,24 +29,24 @@ import org.parceler.Parcel; @Data @JsonObject public class Status { - @JsonField(name = "installed") - public boolean installed; + @JsonField(name = "installed") + public boolean installed; - @JsonField(name = "maintenance") - public boolean maintenance; + @JsonField(name = "maintenance") + public boolean maintenance; - @JsonField(name = "upgrade") - public boolean needsUpgrade; + @JsonField(name = "upgrade") + public boolean needsUpgrade; - @JsonField(name = "version") - public String version; + @JsonField(name = "version") + public String version; - @JsonField(name = "versionstring") - public String versionString; + @JsonField(name = "versionstring") + public String versionString; - @JsonField(name = "edition") - public String edition; + @JsonField(name = "edition") + public String edition; - @JsonField(name = "productname") - public String productName; + @JsonField(name = "productname") + public String productName; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java index 23a4d65f2..6197b323d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java @@ -28,13 +28,13 @@ import org.parceler.Parcel; @Data @JsonObject public class Mention { - @JsonField(name = "id") - String id; + @JsonField(name = "id") + String id; - @JsonField(name = "label") - String label; + @JsonField(name = "label") + String label; - // type of user (guests or users or calls) - @JsonField(name = "source") - String source; + // type of user (guests or users or calls) + @JsonField(name = "source") + String source; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOCS.java index 7883e44ef..7bce06012 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOCS.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class MentionOCS extends GenericOCS { - @JsonField(name = "data") - List data; + @JsonField(name = "data") + List data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOverall.java index beecb50d3..a7e6a1cf1 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/mention/MentionOverall.java @@ -28,6 +28,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class MentionOverall { - @JsonField(name = "ocs") - MentionOCS ocs; + @JsonField(name = "ocs") + MentionOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/notifications/Notification.java b/app/src/main/java/com/nextcloud/talk/models/json/notifications/Notification.java index ff4b23ca8..7420539c7 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/notifications/Notification.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/notifications/Notification.java @@ -33,34 +33,34 @@ import org.parceler.Parcel; @Parcel @JsonObject public class Notification { - @JsonField(name = "icon") - public String icon; - @JsonField(name = "notification_id") - int notificationId; - @JsonField(name = "app") - String app; - @JsonField(name = "user") - String user; - @JsonField(name = "datetime", typeConverter = LoganSquareJodaTimeConverter.class) - DateTime datetime; - @JsonField(name = "object_type") - String objectType; - @JsonField(name = "object_id") - String objectId; - @JsonField(name = "subject") - String subject; - @JsonField(name = "subjectRich") - String subjectRich; - @JsonField(name = "subjectRichParameters") - HashMap> subjectRichParameters; - @JsonField(name = "message") - String message; - @JsonField(name = "messageRich") - String messageRich; - @JsonField(name = "messageRichParameters") - HashMap> messageRichParameters; - @JsonField(name = "link") - String link; - @JsonField(name = "actions") - List actions; + @JsonField(name = "icon") + public String icon; + @JsonField(name = "notification_id") + int notificationId; + @JsonField(name = "app") + String app; + @JsonField(name = "user") + String user; + @JsonField(name = "datetime", typeConverter = LoganSquareJodaTimeConverter.class) + DateTime datetime; + @JsonField(name = "object_type") + String objectType; + @JsonField(name = "object_id") + String objectId; + @JsonField(name = "subject") + String subject; + @JsonField(name = "subjectRich") + String subjectRich; + @JsonField(name = "subjectRichParameters") + HashMap> subjectRichParameters; + @JsonField(name = "message") + String message; + @JsonField(name = "messageRich") + String messageRich; + @JsonField(name = "messageRichParameters") + HashMap> messageRichParameters; + @JsonField(name = "link") + String link; + @JsonField(name = "actions") + List actions; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationAction.java b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationAction.java index dcca6db0a..bebf4d69d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationAction.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationAction.java @@ -29,15 +29,15 @@ import org.parceler.Parcel; @Parcel @JsonObject public class NotificationAction { - @JsonField(name = "label") - String label; + @JsonField(name = "label") + String label; - @JsonField(name = "link") - String link; + @JsonField(name = "link") + String link; - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @JsonField(name = "primary") - boolean primary; + @JsonField(name = "primary") + boolean primary; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationOCS.java index 1a20d2f72..c80bedbcc 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationOCS.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class NotificationOCS extends GenericOCS { - @JsonField(name = "data") - Notification notification; + @JsonField(name = "data") + Notification notification; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationRichObject.java b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationRichObject.java index 6c42ece1b..ab1efc99d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationRichObject.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationRichObject.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @Parcel @JsonObject public class NotificationRichObject { - @JsonField(name = "id") - String label; + @JsonField(name = "id") + String label; - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @JsonField(name = "name") - String name; + @JsonField(name = "name") + String name; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOCS.java index fa7956dc2..01ebc5c14 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOCS.java @@ -31,6 +31,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class NotificationsOCS extends GenericOCS { - @JsonField(name = "data") - List notificationsList; + @JsonField(name = "data") + List notificationsList; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOverall.java index 88a77c51d..c8df427ab 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/notifications/NotificationsOverall.java @@ -26,6 +26,6 @@ import lombok.Data; @Data @JsonObject public class NotificationsOverall { - @JsonField(name = "ocs") - NotificationsOCS ocs; + @JsonField(name = "ocs") + NotificationsOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.java index 8402b75e9..0bf073e7d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.java @@ -27,9 +27,9 @@ import com.nextcloud.talk.models.json.generic.GenericOCS; @JsonObject public class AddParticipantOCS extends GenericOCS { - /* - Returned room will have only type set, and sometimes even that will be null - */ - @JsonField(name = "data") - Conversation data; + /* + Returned room will have only type set, and sometimes even that will be null + */ + @JsonField(name = "data") + Conversation data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOverall.java index d7bb38b7d..07b08b6f8 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOverall.java @@ -28,6 +28,6 @@ import lombok.Data; @Data @JsonObject public class AddParticipantOverall { - @JsonField(name = "ocs") - RoomsOCS ocs; + @JsonField(name = "ocs") + RoomsOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java b/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java index 4d49f4dc3..a9013ebbb 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.java @@ -32,93 +32,93 @@ import org.parceler.ParcelPropertyConverter; @Data @JsonObject public class Participant { - @JsonField(name = "userId") - public String userId; + @JsonField(name = "userId") + public String userId; - @JsonField(name = {"type", "participantType"}, typeConverter = EnumParticipantTypeConverter.class) - public ParticipantType type; + @JsonField(name = { + "type", "participantType" + }, typeConverter = EnumParticipantTypeConverter.class) + public ParticipantType type; - @JsonField(name = "name") - public String name; + @JsonField(name = "name") + public String name; - @JsonField(name = "displayName") - public String displayName; + @JsonField(name = "displayName") + public String displayName; - @JsonField(name = "lastPing") - public long lastPing; + @JsonField(name = "lastPing") + public long lastPing; - @JsonField(name = "sessionId") - public String sessionId; + @JsonField(name = "sessionId") + public String sessionId; - @JsonField(name = "roomId") - public long roomId; + @JsonField(name = "roomId") + public long roomId; - @ParcelPropertyConverter(ObjectParcelConverter.class) - @JsonField(name = "inCall") - public Object inCall; - public String source; + @ParcelPropertyConverter(ObjectParcelConverter.class) + @JsonField(name = "inCall") + public Object inCall; + public String source; - public boolean selected; + public boolean selected; - - public ParticipantFlags getParticipantFlags() { - ParticipantFlags participantFlags = ParticipantFlags.NOT_IN_CALL; - if (inCall != null) { - if (inCall instanceof Long) { - participantFlags = ParticipantFlags.fromValue((Long) inCall); - } else if (inCall instanceof Boolean) { - if ((boolean) inCall) { - participantFlags = ParticipantFlags.IN_CALL; - } else { - participantFlags = ParticipantFlags.NOT_IN_CALL; - } - } + public ParticipantFlags getParticipantFlags() { + ParticipantFlags participantFlags = ParticipantFlags.NOT_IN_CALL; + if (inCall != null) { + if (inCall instanceof Long) { + participantFlags = ParticipantFlags.fromValue((Long) inCall); + } else if (inCall instanceof Boolean) { + if ((boolean) inCall) { + participantFlags = ParticipantFlags.IN_CALL; + } else { + participantFlags = ParticipantFlags.NOT_IN_CALL; } - - return participantFlags; + } } - public enum ParticipantType { - DUMMY, - OWNER, - MODERATOR, - USER, - GUEST, - USER_FOLLOWING_LINK + return participantFlags; + } + + public enum ParticipantType { + DUMMY, + OWNER, + MODERATOR, + USER, + GUEST, + USER_FOLLOWING_LINK + } + + public enum ParticipantFlags { + NOT_IN_CALL(0), + IN_CALL(1), + IN_CALL_WITH_AUDIO(3), + IN_CALL_WITH_VIDEO(5), + IN_CALL_WITH_AUDIO_AND_VIDEO(7); + + private long value; + + ParticipantFlags(long value) { + this.value = value; } - public enum ParticipantFlags { - NOT_IN_CALL(0), - IN_CALL(1), - IN_CALL_WITH_AUDIO(3), - IN_CALL_WITH_VIDEO(5), - IN_CALL_WITH_AUDIO_AND_VIDEO(7); - - private long value; - - ParticipantFlags(long value) { - this.value = value; - } - - public static ParticipantFlags fromValue(long value) { - if (value == 0) { - return NOT_IN_CALL; - } else if (value == 1) { - return IN_CALL; - } else if (value == 3) { - return IN_CALL_WITH_AUDIO; - } else if (value == 5) { - return IN_CALL_WITH_VIDEO; - } else if (value == 7) { - return IN_CALL_WITH_AUDIO_AND_VIDEO; - } else { - return NOT_IN_CALL; - } - } - - public long getValue() { - return value; - } - + public static ParticipantFlags fromValue(long value) { + if (value == 0) { + return NOT_IN_CALL; + } else if (value == 1) { + return IN_CALL; + } else if (value == 3) { + return IN_CALL_WITH_AUDIO; + } else if (value == 5) { + return IN_CALL_WITH_VIDEO; + } else if (value == 7) { + return IN_CALL_WITH_AUDIO_AND_VIDEO; + } else { + return NOT_IN_CALL; + } } + + public long getValue() { + return value; + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOCS.java index b1d5a2add..4c6d08729 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOCS.java @@ -31,6 +31,6 @@ import org.parceler.Parcel; @Data @JsonObject public class ParticipantsOCS extends GenericOCS { - @JsonField(name = "data") - public List data; + @JsonField(name = "data") + public List data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOverall.java index 6ab2b97a5..91c951a82 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/ParticipantsOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class ParticipantsOverall { - @JsonField(name = "ocs") - public ParticipantsOCS ocs; + @JsonField(name = "ocs") + public ParticipantsOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.java index c65033069..54f108b57 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.java @@ -30,33 +30,33 @@ import org.parceler.Parcel; @Parcel @JsonObject public class DecryptedPushMessage { - @JsonField(name = "app") - String app; + @JsonField(name = "app") + String app; - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @JsonField(name = "subject") - String subject; + @JsonField(name = "subject") + String subject; - @JsonField(name = "id") - String id; + @JsonField(name = "id") + String id; - @JsonField(name = "nid") - long notificationId; + @JsonField(name = "nid") + long notificationId; - @JsonField(name = "delete") - boolean delete; + @JsonField(name = "delete") + boolean delete; - @JsonField(name = "delete-all") - boolean deleteAll; + @JsonField(name = "delete-all") + boolean deleteAll; - @JsonIgnore - NotificationUser notificationUser; + @JsonIgnore + NotificationUser notificationUser; - @JsonIgnore - String text; + @JsonIgnore + String text; - @JsonIgnore - long timestamp; + @JsonIgnore + long timestamp; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.java b/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.java index 5b78951b0..29cc3dc59 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.java @@ -29,18 +29,18 @@ import org.parceler.Parcel; @Data @JsonObject public class PushConfigurationState { - @JsonField(name = "pushToken") - public String pushToken; + @JsonField(name = "pushToken") + public String pushToken; - @JsonField(name = "deviceIdentifier") - public String deviceIdentifier; + @JsonField(name = "deviceIdentifier") + public String deviceIdentifier; - @JsonField(name = "deviceIdentifierSignature") - public String deviceIdentifierSignature; + @JsonField(name = "deviceIdentifierSignature") + public String deviceIdentifierSignature; - @JsonField(name = "userPublicKey") - public String userPublicKey; + @JsonField(name = "userPublicKey") + public String userPublicKey; - @JsonField(name = "usesRegularPass") - public boolean usesRegularPass; + @JsonField(name = "usesRegularPass") + public boolean usesRegularPass; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.java b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.java index 0d494ebff..e38580186 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.java @@ -20,7 +20,6 @@ package com.nextcloud.talk.models.json.push; - import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonObject; import lombok.Data; @@ -30,13 +29,13 @@ import org.parceler.Parcel; @Parcel @JsonObject public class PushRegistration { - @JsonField(name = "publicKey") - String publicKey; + @JsonField(name = "publicKey") + String publicKey; - @JsonField(name = "deviceIdentifier") - String deviceIdentifier; + @JsonField(name = "deviceIdentifier") + String deviceIdentifier; - @JsonField(name = "signature") - String signature; + @JsonField(name = "signature") + String signature; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOCS.java index e16eae402..517ba3361 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOCS.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class PushRegistrationOCS extends GenericOCS { - @JsonField(name = "data") - PushRegistration data; + @JsonField(name = "data") + PushRegistration data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOverall.java index 6e33d175c..36e7c9103 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistrationOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class PushRegistrationOverall { - @JsonField(name = "ocs") - PushRegistrationOCS ocs; + @JsonField(name = "ocs") + PushRegistrationOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/sharees/ExactSharees.java b/app/src/main/java/com/nextcloud/talk/models/json/sharees/ExactSharees.java index b96cda6af..9ad2e4e81 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/sharees/ExactSharees.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/sharees/ExactSharees.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Data @JsonObject public class ExactSharees { - @JsonField(name = "users") - List exactSharees; + @JsonField(name = "users") + List exactSharees; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/sharees/Sharee.java b/app/src/main/java/com/nextcloud/talk/models/json/sharees/Sharee.java index 550117f5c..699a12efa 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/sharees/Sharee.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/sharees/Sharee.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @Data @JsonObject public class Sharee { - @JsonField(name = "id") - String id; + @JsonField(name = "id") + String id; - @JsonField(name = "value") - Value value; + @JsonField(name = "value") + Value value; - @JsonField(name = "label") - String label; + @JsonField(name = "label") + String label; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOCS.java index f20bcc6b9..97511751e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOCS.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class ShareesOCS extends GenericOCS { - @JsonField(name = "data") - SharesData data; + @JsonField(name = "data") + SharesData data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOverall.java index 59f0c3a02..cbfc66f2f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/sharees/ShareesOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class ShareesOverall { - @JsonField(name = "ocs") - ShareesOCS ocs; + @JsonField(name = "ocs") + ShareesOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/sharees/SharesData.java b/app/src/main/java/com/nextcloud/talk/models/json/sharees/SharesData.java index 952b235ab..af7f8105e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/sharees/SharesData.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/sharees/SharesData.java @@ -30,10 +30,9 @@ import org.parceler.Parcel; @Data @JsonObject public class SharesData { - @JsonField(name = "users") - List users; - - @JsonField(name = "exact") - ExactSharees exactUsers; + @JsonField(name = "users") + List users; + @JsonField(name = "exact") + ExactSharees exactUsers; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/sharees/Value.java b/app/src/main/java/com/nextcloud/talk/models/json/sharees/Value.java index 25a5f933d..6bed5af57 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/sharees/Value.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/sharees/Value.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class Value { - @JsonField(name = "shareWith") - String shareWith; + @JsonField(name = "shareWith") + String shareWith; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessage.java index 4f54832e5..59c18313e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessage.java @@ -29,17 +29,17 @@ import org.parceler.ParcelPropertyConverter; @Data @JsonObject public class DataChannelMessage { - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @ParcelPropertyConverter(ObjectParcelConverter.class) - @JsonField(name = "payload") - Object payload; + @ParcelPropertyConverter(ObjectParcelConverter.class) + @JsonField(name = "payload") + Object payload; - public DataChannelMessage(String type) { - this.type = type; - } + public DataChannelMessage(String type) { + this.type = type; + } - public DataChannelMessage() { - } + public DataChannelMessage() { + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessageNick.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessageNick.java index 12e67a60b..09d40a610 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessageNick.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/DataChannelMessageNick.java @@ -30,17 +30,17 @@ import org.parceler.ParcelPropertyConverter; @Data @JsonObject public class DataChannelMessageNick { - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @ParcelPropertyConverter(ObjectParcelConverter.class) - @JsonField(name = "payload") - HashMap payload; + @ParcelPropertyConverter(ObjectParcelConverter.class) + @JsonField(name = "payload") + HashMap payload; - public DataChannelMessageNick(String type) { - this.type = type; - } + public DataChannelMessageNick(String type) { + this.type = type; + } - public DataChannelMessageNick() { - } + public DataChannelMessageNick() { + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCIceCandidate.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCIceCandidate.java index c92e8ebc8..d343fd95d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCIceCandidate.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCIceCandidate.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @JsonObject @Parcel public class NCIceCandidate { - @JsonField(name = "sdpMLineIndex") - int sdpMLineIndex; + @JsonField(name = "sdpMLineIndex") + int sdpMLineIndex; - @JsonField(name = "sdpMid") - String sdpMid; + @JsonField(name = "sdpMid") + String sdpMid; - @JsonField(name = "candidate") - String candidate; + @JsonField(name = "candidate") + String candidate; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.java index 7db2e7ccd..f5e7b125d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.java @@ -29,18 +29,18 @@ import org.parceler.Parcel; @JsonObject @Parcel public class NCMessagePayload { - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @JsonField(name = "sdp") - String sdp; + @JsonField(name = "sdp") + String sdp; - @JsonField(name = "nick") - String nick; + @JsonField(name = "nick") + String nick; - @JsonField(name = "candidate") - NCIceCandidate iceCandidate; + @JsonField(name = "candidate") + NCIceCandidate iceCandidate; - @JsonField(name = "name") - String name; + @JsonField(name = "name") + String name; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessageWrapper.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessageWrapper.java index b40093bf6..5648fe3c1 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessageWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessageWrapper.java @@ -29,13 +29,13 @@ import org.parceler.Parcel; @JsonObject @Parcel public class NCMessageWrapper { - @JsonField(name = "fn") - NCSignalingMessage signalingMessage; + @JsonField(name = "fn") + NCSignalingMessage signalingMessage; - // always a "message" - @JsonField(name = "ev") - String ev; + // always a "message" + @JsonField(name = "ev") + String ev; - @JsonField(name = "sessionId") - String sessionId; + @JsonField(name = "sessionId") + String sessionId; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCSignalingMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCSignalingMessage.java index d4e92503b..adf327717 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCSignalingMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCSignalingMessage.java @@ -29,18 +29,18 @@ import org.parceler.Parcel; @JsonObject @Parcel public class NCSignalingMessage { - @JsonField(name = "from") - String from; - @JsonField(name = "to") - String to; - @JsonField(name = "type") - String type; - @JsonField(name = "payload") - NCMessagePayload payload; - @JsonField(name = "roomType") - String roomType; - @JsonField(name = "sid") - String sid; - @JsonField(name = "prefix") - String prefix; + @JsonField(name = "from") + String from; + @JsonField(name = "to") + String to; + @JsonField(name = "type") + String type; + @JsonField(name = "payload") + NCMessagePayload payload; + @JsonField(name = "roomType") + String roomType; + @JsonField(name = "sid") + String sid; + @JsonField(name = "prefix") + String prefix; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/Signaling.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/Signaling.java index 82f19549d..77586cc56 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/Signaling.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/Signaling.java @@ -31,9 +31,9 @@ import lombok.Data; @Data @JsonObject public class Signaling { - @JsonField(name = "type") - String type; - //can be NCMessageWrapper or List> - @JsonField(name = "data") - Object messageWrapper; + @JsonField(name = "type") + String type; + //can be NCMessageWrapper or List> + @JsonField(name = "data") + Object messageWrapper; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOCS.java index 619fa12cf..29d7f7115 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOCS.java @@ -29,6 +29,6 @@ import lombok.Data; @Data @JsonObject public class SignalingOCS extends GenericOCS { - @JsonField(name = "data") - List signalings; + @JsonField(name = "data") + List signalings; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOverall.java index fc9bfc916..e4df03217 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/SignalingOverall.java @@ -27,6 +27,6 @@ import lombok.Data; @JsonObject @Data public class SignalingOverall { - @JsonField(name = "ocs") - SignalingOCS ocs; + @JsonField(name = "ocs") + SignalingOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.java index 6b793a543..f8f96156f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/IceServer.java @@ -28,15 +28,15 @@ import lombok.Data; @Data @JsonObject public class IceServer { - @JsonField(name = "url") - String url; + @JsonField(name = "url") + String url; - @JsonField(name = "urls") - List urls; + @JsonField(name = "urls") + List urls; - @JsonField(name = "username") - String username; + @JsonField(name = "username") + String username; - @JsonField(name = "credential") - String credential; + @JsonField(name = "credential") + String credential; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/Settings.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/Settings.java index a723d4c9d..6004959cf 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/Settings.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/Settings.java @@ -28,15 +28,15 @@ import lombok.Data; @Data @JsonObject public class Settings { - @JsonField(name = "stunservers") - List stunServers; + @JsonField(name = "stunservers") + List stunServers; - @JsonField(name = "turnservers") - List turnServers; + @JsonField(name = "turnservers") + List turnServers; - @JsonField(name = "server") - String externalSignalingServer; + @JsonField(name = "server") + String externalSignalingServer; - @JsonField(name = "ticket") - String externalSignalingTicket; + @JsonField(name = "ticket") + String externalSignalingTicket; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOcs.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOcs.java index d2db1314f..2aa60d1b6 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOcs.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOcs.java @@ -28,6 +28,6 @@ import lombok.Data; @Data @JsonObject public class SignalingSettingsOcs extends GenericOCS { - @JsonField(name = "data") - Settings settings; + @JsonField(name = "data") + Settings settings; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOverall.java index 10d680c4e..99b947603 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/settings/SignalingSettingsOverall.java @@ -27,6 +27,6 @@ import lombok.Data; @Data @JsonObject public class SignalingSettingsOverall { - @JsonField(name = "ocs") - SignalingSettingsOcs ocs; + @JsonField(name = "ocs") + SignalingSettingsOcs ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java index fa2978617..6c19f6088 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @Data @JsonObject() public class UserProfileData { - @JsonField(name = "display-name") - String displayName; + @JsonField(name = "display-name") + String displayName; - @JsonField(name = "displayname") - String displayNameAlt; + @JsonField(name = "displayname") + String displayNameAlt; - @JsonField(name = "id") - String userId; + @JsonField(name = "id") + String userId; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOCS.java index ca58bcd09..20f080191 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOCS.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Data @JsonObject public class UserProfileOCS extends GenericOCS { - @JsonField(name = "data") - UserProfileData data; + @JsonField(name = "data") + UserProfileData data; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOverall.java index 4edd9c531..9f4fe2b94 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileOverall.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Data @JsonObject public class UserProfileOverall { - @JsonField(name = "ocs") - UserProfileOCS ocs; + @JsonField(name = "ocs") + UserProfileOCS ocs; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ActorWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ActorWebSocketMessage.java index 2784f98fb..a18220fda 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ActorWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ActorWebSocketMessage.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @JsonObject @Parcel public class ActorWebSocketMessage { - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @JsonField(name = "sessionid") - String sessionId; + @JsonField(name = "sessionid") + String sessionId; - @JsonField(name = "userid") - String userid; + @JsonField(name = "userid") + String userid; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthParametersWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthParametersWebSocketMessage.java index cdf53b8c5..b0ae7ca2a 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthParametersWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthParametersWebSocketMessage.java @@ -29,9 +29,9 @@ import org.parceler.Parcel; @JsonObject @Parcel public class AuthParametersWebSocketMessage { - @JsonField(name = "userid") - String userid; + @JsonField(name = "userid") + String userid; - @JsonField(name = "ticket") - String ticket; + @JsonField(name = "ticket") + String ticket; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthWebSocketMessage.java index 578e68d38..caa406f87 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/AuthWebSocketMessage.java @@ -29,9 +29,9 @@ import org.parceler.Parcel; @JsonObject @Parcel public class AuthWebSocketMessage { - @JsonField(name = "url") - String url; + @JsonField(name = "url") + String url; - @JsonField(name = "params") - AuthParametersWebSocketMessage authParametersWebSocketMessage; + @JsonField(name = "params") + AuthParametersWebSocketMessage authParametersWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.java index b950d6cba..4cf15fe19 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @JsonObject @Parcel public class BaseWebSocketMessage { - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ByeWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ByeWebSocketMessage.java index a534031ef..f740dada1 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ByeWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ByeWebSocketMessage.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Parcel @Data public class ByeWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "bye") - HashMap bye; + @JsonField(name = "bye") + HashMap bye; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.java index 39b887b80..45b1ebe13 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @JsonObject @Parcel public class CallOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "message") - CallWebSocketMessage callWebSocketMessage; + @JsonField(name = "message") + CallWebSocketMessage callWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallWebSocketMessage.java index 7ff5d9880..3cbe01abd 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallWebSocketMessage.java @@ -30,12 +30,12 @@ import org.parceler.Parcel; @JsonObject @Parcel public class CallWebSocketMessage { - @JsonField(name = "recipient") - ActorWebSocketMessage recipientWebSocketMessage; + @JsonField(name = "recipient") + ActorWebSocketMessage recipientWebSocketMessage; - @JsonField(name = "sender") - ActorWebSocketMessage senderWebSocketMessage; + @JsonField(name = "sender") + ActorWebSocketMessage senderWebSocketMessage; - @JsonField(name = "data") - NCSignalingMessage ncSignalingMessage; + @JsonField(name = "data") + NCSignalingMessage ncSignalingMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.java index 0d8fee0ad..dc1b56ee1 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @Parcel @JsonObject public class ErrorOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "error") - ErrorWebSocketMessage errorWebSocketMessage; + @JsonField(name = "error") + ErrorWebSocketMessage errorWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorWebSocketMessage.java index 14c11b9e8..e7ca6d98f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorWebSocketMessage.java @@ -29,9 +29,9 @@ import org.parceler.Parcel; @Parcel @JsonObject public class ErrorWebSocketMessage { - @JsonField(name = "code") - String code; + @JsonField(name = "code") + String code; - @JsonField(name = "message") - String message; + @JsonField(name = "message") + String message; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.java index d61f6de9b..74c2f3ebd 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.java @@ -30,8 +30,8 @@ import org.parceler.Parcel; @Parcel @JsonObject public class EventOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "type") - String type; - @JsonField(name = "event") - HashMap eventMap; + @JsonField(name = "type") + String type; + @JsonField(name = "event") + HashMap eventMap; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.java index f5d9a40fb..ce209ea42 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @JsonObject @Parcel public class HelloOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "hello") - HelloWebSocketMessage helloWebSocketMessage; + @JsonField(name = "hello") + HelloWebSocketMessage helloWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.java index aabf0aab7..81693df98 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @JsonObject @Parcel public class HelloResponseOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "hello") - HelloResponseWebSocketMessage helloResponseWebSocketMessage; + @JsonField(name = "hello") + HelloResponseWebSocketMessage helloResponseWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseWebSocketMessage.java index 2ea35db88..5d39a6541 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseWebSocketMessage.java @@ -29,17 +29,18 @@ import org.parceler.Parcel; @JsonObject @Parcel public class HelloResponseWebSocketMessage { - @JsonField(name = "resumeid") - String resumeId; + @JsonField(name = "resumeid") + String resumeId; - @JsonField(name = "sessionid") - String sessionId; + @JsonField(name = "sessionid") + String sessionId; - @JsonField(name = "server") - ServerHelloResponseFeaturesWebSocketMessage serverHelloResponseFeaturesWebSocketMessage; + @JsonField(name = "server") + ServerHelloResponseFeaturesWebSocketMessage serverHelloResponseFeaturesWebSocketMessage; - public boolean serverHasMCUSupport() { - return serverHelloResponseFeaturesWebSocketMessage != null && serverHelloResponseFeaturesWebSocketMessage.getFeatures() != null - && serverHelloResponseFeaturesWebSocketMessage.getFeatures().contains("mcu"); - } + public boolean serverHasMCUSupport() { + return serverHelloResponseFeaturesWebSocketMessage != null + && serverHelloResponseFeaturesWebSocketMessage.getFeatures() != null + && serverHelloResponseFeaturesWebSocketMessage.getFeatures().contains("mcu"); + } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloWebSocketMessage.java index 1d4f2ebca..b619b1245 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloWebSocketMessage.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @JsonObject @Parcel public class HelloWebSocketMessage { - @JsonField(name = "version") - String version; + @JsonField(name = "version") + String version; - @JsonField(name = "resumeid") - String resumeid; + @JsonField(name = "resumeid") + String resumeid; - @JsonField(name = "auth") - AuthWebSocketMessage authWebSocketMessage; + @JsonField(name = "auth") + AuthWebSocketMessage authWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.java index cebdc2af7..c5822897f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @JsonObject @Parcel public class JoinedRoomOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "room") - RoomWebSocketMessage roomWebSocketMessage; + @JsonField(name = "room") + RoomWebSocketMessage roomWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferOverallWebSocketMessage.java index aed7d2a95..1a1e38212 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferOverallWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @JsonObject @Parcel public class RequestOfferOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "message") - RequestOfferSignalingMessage requestOfferOverallWebSocketMessage; + @JsonField(name = "message") + RequestOfferSignalingMessage requestOfferOverallWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferSignalingMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferSignalingMessage.java index 2aeddb94e..c18cf2dff 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferSignalingMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RequestOfferSignalingMessage.java @@ -29,9 +29,9 @@ import org.parceler.Parcel; @JsonObject @Parcel public class RequestOfferSignalingMessage { - @JsonField(name = "recipient") - ActorWebSocketMessage actorWebSocketMessage; + @JsonField(name = "recipient") + ActorWebSocketMessage actorWebSocketMessage; - @JsonField(name = "data") - SignalingDataWebSocketMessageForOffer signalingDataWebSocketMessageForOffer; + @JsonField(name = "data") + SignalingDataWebSocketMessageForOffer signalingDataWebSocketMessageForOffer; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.java index 0a076ecfb..7ca845104 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.java @@ -29,6 +29,6 @@ import org.parceler.Parcel; @JsonObject @Parcel public class RoomOverallWebSocketMessage extends BaseWebSocketMessage { - @JsonField(name = "room") - RoomWebSocketMessage roomWebSocketMessage; + @JsonField(name = "room") + RoomWebSocketMessage roomWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java index 15d5b1b15..aed8d4071 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java @@ -31,9 +31,9 @@ import org.parceler.Parcel; @Parcel @JsonObject public class RoomPropertiesWebSocketMessage { - @JsonField(name = "name") - String name; + @JsonField(name = "name") + String name; - @JsonField(name = "type", typeConverter = EnumRoomTypeConverter.class) - Conversation.ConversationType roomType; + @JsonField(name = "type", typeConverter = EnumRoomTypeConverter.class) + Conversation.ConversationType roomType; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomWebSocketMessage.java index e2b499038..0dbd1f9ea 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomWebSocketMessage.java @@ -29,12 +29,12 @@ import org.parceler.Parcel; @JsonObject @Parcel public class RoomWebSocketMessage { - @JsonField(name = "roomid") - String roomId; + @JsonField(name = "roomid") + String roomId; - @JsonField(name = "sessionid") - String sessiondId; + @JsonField(name = "sessionid") + String sessiondId; - @JsonField(name = "properties") - RoomPropertiesWebSocketMessage roomPropertiesWebSocketMessage; + @JsonField(name = "properties") + RoomPropertiesWebSocketMessage roomPropertiesWebSocketMessage; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ServerHelloResponseFeaturesWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ServerHelloResponseFeaturesWebSocketMessage.java index 67b3ce3b6..2dbebdce7 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ServerHelloResponseFeaturesWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ServerHelloResponseFeaturesWebSocketMessage.java @@ -30,6 +30,6 @@ import org.parceler.Parcel; @Parcel @Data public class ServerHelloResponseFeaturesWebSocketMessage { - @JsonField(name = "features") - List features; + @JsonField(name = "features") + List features; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/SignalingDataWebSocketMessageForOffer.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/SignalingDataWebSocketMessageForOffer.java index 4553ef5c6..805bb0f69 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/SignalingDataWebSocketMessageForOffer.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/SignalingDataWebSocketMessageForOffer.java @@ -29,9 +29,9 @@ import org.parceler.Parcel; @JsonObject @Parcel public class SignalingDataWebSocketMessageForOffer { - @JsonField(name = "type") - String type; + @JsonField(name = "type") + String type; - @JsonField(name = "roomType") - String roomType; + @JsonField(name = "roomType") + String roomType; } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt index b8fd09b3d..405ed0eab 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt @@ -21,7 +21,6 @@ package com.nextcloud.talk.newarch.features.conversationsList import android.app.Application -import android.content.res.Resources import android.graphics.Bitmap import android.graphics.drawable.Drawable import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory diff --git a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java index 3e429a69b..b211c213a 100644 --- a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java +++ b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java @@ -25,12 +25,12 @@ import android.view.View; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import autodagger.AutoInjector; -import com.nextcloud.talk.models.json.mention.Mention; -import com.nextcloud.talk.models.json.mention.MentionOverall; import com.nextcloud.talk.adapters.items.MentionAutocompleteItem; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.mention.Mention; +import com.nextcloud.talk.models.json.mention.MentionOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.database.user.UserUtils; import com.otaliastudios.autocomplete.RecyclerViewPresenter; @@ -45,109 +45,115 @@ import java.util.List; import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) -public class MentionAutocompletePresenter extends RecyclerViewPresenter implements FlexibleAdapter.OnItemClickListener { - @Inject - NcApi ncApi; - @Inject - UserUtils userUtils; - private UserEntity currentUser; - private FlexibleAdapter adapter; - private Context context; +public class MentionAutocompletePresenter extends RecyclerViewPresenter + implements FlexibleAdapter.OnItemClickListener { + @Inject + NcApi ncApi; + @Inject + UserUtils userUtils; + private UserEntity currentUser; + private FlexibleAdapter adapter; + private Context context; - private String roomToken; + private String roomToken; - private List abstractFlexibleItemList = new ArrayList<>(); + private List abstractFlexibleItemList = new ArrayList<>(); - public MentionAutocompletePresenter(Context context) { - super(context); - this.context = context; - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - currentUser = userUtils.getCurrentUser(); + public MentionAutocompletePresenter(Context context) { + super(context); + this.context = context; + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + currentUser = userUtils.getCurrentUser(); + } + + public MentionAutocompletePresenter(Context context, String roomToken) { + super(context); + this.roomToken = roomToken; + this.context = context; + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + currentUser = userUtils.getCurrentUser(); + } + + @Override + protected RecyclerView.Adapter instantiateAdapter() { + adapter = new FlexibleAdapter<>(abstractFlexibleItemList, context, false); + adapter.addListener(this); + return adapter; + } + + @Override + protected void onQuery(@Nullable CharSequence query) { + + String queryString; + if (query != null && query.length() > 1) { + queryString = String.valueOf(query.subSequence(1, query.length())); + } else { + queryString = ""; } - public MentionAutocompletePresenter(Context context, String roomToken) { - super(context); - this.roomToken = roomToken; - this.context = context; - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - currentUser = userUtils.getCurrentUser(); - } - - @Override - protected RecyclerView.Adapter instantiateAdapter() { - adapter = new FlexibleAdapter<>(abstractFlexibleItemList, context, false); - adapter.addListener(this); - return adapter; - } - - @Override - protected void onQuery(@Nullable CharSequence query) { - - String queryString; - if (query != null && query.length() > 1) { - queryString = String.valueOf(query.subSequence(1, query.length())); - } else { - queryString = ""; - } - - adapter.setFilter(queryString); - ncApi.getMentionAutocompleteSuggestions(ApiUtils.getCredentials(currentUser.getUsername(), currentUser - .getToken()), ApiUtils.getUrlForMentionSuggestions(currentUser.getBaseUrl(), roomToken), - queryString, 5) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(3) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(MentionOverall mentionOverall) { - List mentionsList = mentionOverall.getOcs().getData(); - - if (mentionsList.size() == 0) { - adapter.clear(); - } else { - List internalAbstractFlexibleItemList = new ArrayList<>(); - for (Mention mention : mentionsList) { - internalAbstractFlexibleItemList.add( - new MentionAutocompleteItem(mention.getId(), - mention.getLabel(), mention.getSource(), - currentUser)); - } - - if (adapter.getItemCount() != 0) { - adapter.clear(); - } - - adapter.updateDataSet(internalAbstractFlexibleItemList); - } - } - - @Override - public void onError(Throwable e) { - adapter.clear(); - } - - @Override - public void onComplete() { - - } - }); - } - - - @Override - public boolean onItemClick(View view, int position) { - Mention mention = new Mention(); - MentionAutocompleteItem mentionAutocompleteItem = (MentionAutocompleteItem) adapter.getItem(position); - if (mentionAutocompleteItem != null) { - mention.setId(mentionAutocompleteItem.getObjectId()); - mention.setLabel(mentionAutocompleteItem.getDisplayName()); - mention.setSource(mentionAutocompleteItem.getSource()); - dispatchClick(mention); - } - return true; + adapter.setFilter(queryString); + ncApi.getMentionAutocompleteSuggestions( + ApiUtils.getCredentials(currentUser.getUsername(), currentUser + .getToken()), ApiUtils.getUrlForMentionSuggestions(currentUser.getBaseUrl(), roomToken), + queryString, 5) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(3) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(MentionOverall mentionOverall) { + List mentionsList = mentionOverall.getOcs().getData(); + + if (mentionsList.size() == 0) { + adapter.clear(); + } else { + List internalAbstractFlexibleItemList = new ArrayList<>(); + for (Mention mention : mentionsList) { + internalAbstractFlexibleItemList.add( + new MentionAutocompleteItem(mention.getId(), + mention.getLabel(), mention.getSource(), + currentUser)); + } + + if (adapter.getItemCount() != 0) { + adapter.clear(); + } + + adapter.updateDataSet(internalAbstractFlexibleItemList); + } + } + + @Override + public void onError(Throwable e) { + adapter.clear(); + } + + @Override + public void onComplete() { + + } + }); + } + + @Override + public boolean onItemClick(View view, int position) { + Mention mention = new Mention(); + MentionAutocompleteItem mentionAutocompleteItem = + (MentionAutocompleteItem) adapter.getItem(position); + if (mentionAutocompleteItem != null) { + mention.setId(mentionAutocompleteItem.getObjectId()); + mention.setLabel(mentionAutocompleteItem.getDisplayName()); + mention.setSource(mentionAutocompleteItem.getSource()); + dispatchClick(mention); } + return true; + } } diff --git a/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt index a832554d2..396439ef8 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt @@ -37,52 +37,66 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PackageReplacedReceiver : BroadcastReceiver() { - @Inject - lateinit var userUtils: UserUtils + @Inject + lateinit var userUtils: UserUtils - @Inject - lateinit var appPreferences: AppPreferences + @Inject + lateinit var appPreferences: AppPreferences - override fun onReceive(context: Context, intent: Intent?) { - NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + override fun onReceive( + context: Context, + intent: Intent? + ) { + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - if (intent != null && intent.action != null && - intent.action == "android.intent.action.MY_PACKAGE_REPLACED") { - try { - val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) - if (packageInfo.versionCode > 43 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val notificationManager = context.getSystemService(Context - .NOTIFICATION_SERVICE) as NotificationManager + if (intent != null && intent.action != null && + intent.action == "android.intent.action.MY_PACKAGE_REPLACED" + ) { + try { + val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) + if (packageInfo.versionCode > 43 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val notificationManager = context.getSystemService( + Context + .NOTIFICATION_SERVICE + ) as NotificationManager - if (notificationManager != null) { - if (!appPreferences.isNotificationChannelUpgradedToV2) { - for (notificationChannelGroup in notificationManager - .notificationChannelGroups) { - notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id) - } + if (notificationManager != null) { + if (!appPreferences.isNotificationChannelUpgradedToV2) { + for (notificationChannelGroup in notificationManager + .notificationChannelGroups) { + notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id) + } - notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS) - notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES) + notificationManager.deleteNotificationChannel( + NotificationUtils.NOTIFICATION_CHANNEL_CALLS + ) + notificationManager.deleteNotificationChannel( + NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES + ) - appPreferences.setNotificationChannelIsUpgradedToV2(true) - } - - if (!appPreferences.isNotificationChannelUpgradedToV3 && packageInfo.versionCode > 51) { - notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V2) - notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V2) - appPreferences.setNotificationChannelIsUpgradedToV3(true) - } - } - - } - } catch (e: PackageManager.NameNotFoundException) { - Log.e(TAG, "Failed to fetch package info") + appPreferences.setNotificationChannelIsUpgradedToV2(true) } - } - } + if (!appPreferences.isNotificationChannelUpgradedToV3 && packageInfo.versionCode > 51) { + notificationManager.deleteNotificationChannel( + NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V2 + ) + notificationManager.deleteNotificationChannel( + NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V2 + ) + appPreferences.setNotificationChannelIsUpgradedToV3(true) + } + } + + } + } catch (e: PackageManager.NameNotFoundException) { + Log.e(TAG, "Failed to fetch package info") + } - companion object { - private val TAG = "PackageReplacedReceiver" } + } + + companion object { + private val TAG = "PackageReplacedReceiver" + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt index b8ce1cd12..84c4c9a37 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt @@ -28,123 +28,139 @@ import android.accounts.AccountManager import android.content.Context import android.content.pm.PackageManager import android.util.Log -import com.nextcloud.talk.models.ImportAccount import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.models.ImportAccount import com.nextcloud.talk.models.database.UserEntity import java.util.ArrayList import java.util.Arrays object AccountUtils { - private val TAG = "AccountUtils" + private val TAG = "AccountUtils" - fun findAccounts(userEntitiesList: List): List { - val context = NextcloudTalkApplication.sharedApplication!!.applicationContext - val accMgr = AccountManager.get(context) - val accounts = accMgr.getAccountsByType(context.getString(R.string.nc_import_account_type)) + fun findAccounts(userEntitiesList: List): List { + val context = NextcloudTalkApplication.sharedApplication!!.applicationContext + val accMgr = AccountManager.get(context) + val accounts = accMgr.getAccountsByType(context.getString(R.string.nc_import_account_type)) - val accountsAvailable = ArrayList() - var importAccount: ImportAccount - var internalUserEntity: UserEntity - var accountFound: Boolean - for (account in accounts) { - accountFound = false + val accountsAvailable = ArrayList() + var importAccount: ImportAccount + var internalUserEntity: UserEntity + var accountFound: Boolean + for (account in accounts) { + accountFound = false - for (i in userEntitiesList.indices) { - internalUserEntity = userEntitiesList[i] - importAccount = getInformationFromAccount(account) - if (importAccount.token != null) { - if (importAccount.baseUrl.startsWith("http://") || importAccount.baseUrl.startsWith("https://")) { - if (internalUserEntity.username == importAccount.username && internalUserEntity.baseUrl == importAccount.baseUrl) { - accountFound = true - break - } - } else { - if (internalUserEntity.username == importAccount.username && (internalUserEntity - .baseUrl == "http://" + importAccount.baseUrl || - internalUserEntity.baseUrl == "https://" + importAccount - .baseUrl)) { - accountFound = true - break - } - - } - } else { - accountFound = true - break - } + for (i in userEntitiesList.indices) { + internalUserEntity = userEntitiesList[i] + importAccount = getInformationFromAccount(account) + if (importAccount.token != null) { + if (importAccount.baseUrl.startsWith("http://") || importAccount.baseUrl.startsWith( + "https://" + ) + ) { + if (internalUserEntity.username == importAccount.username && internalUserEntity.baseUrl == importAccount.baseUrl) { + accountFound = true + break + } + } else { + if (internalUserEntity.username == importAccount.username && (internalUserEntity + .baseUrl == "http://" + importAccount.baseUrl || + internalUserEntity.baseUrl == "https://" + importAccount + .baseUrl) + ) { + accountFound = true + break } - if (!accountFound) { - accountsAvailable.add(account) + } + } else { + accountFound = true + break + } + } + + if (!accountFound) { + accountsAvailable.add(account) + } + } + + return accountsAvailable + } + + fun getAppNameBasedOnPackage(packageName: String): String { + val context = NextcloudTalkApplication.sharedApplication!!.applicationContext + val packageManager = context.packageManager + var appName = "" + try { + appName = packageManager.getApplicationLabel( + packageManager.getApplicationInfo( + packageName, + PackageManager.GET_META_DATA + ) + ) as String + } catch (e: PackageManager.NameNotFoundException) { + Log.e(TAG, "Failed to get app name based on package") + } + + return appName + } + + fun canWeOpenFilesApp( + context: Context, + accountName: String + ): Boolean { + val pm = context.packageManager + try { + val packageInfo = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), 0) + if (packageInfo.versionCode >= 30060151) { + val ownSignatures = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES) + .signatures + val filesAppSignatures = pm.getPackageInfo( + context.getString(R.string.nc_import_accounts_from), PackageManager.GET_SIGNATURES + ) + .signatures + + if (Arrays.equals(ownSignatures, filesAppSignatures)) { + val accMgr = AccountManager.get(context) + val accounts = + accMgr.getAccountsByType(context.getString(R.string.nc_import_account_type)) + for (account in accounts) { + if (account.name == accountName) { + return true } + } + } else { + return true } + } + } catch (appNotFoundException: PackageManager.NameNotFoundException) { - return accountsAvailable } - fun getAppNameBasedOnPackage(packageName: String): String { - val context = NextcloudTalkApplication.sharedApplication!!.applicationContext - val packageManager = context.packageManager - var appName = "" - try { - appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, - PackageManager.GET_META_DATA)) as String - } catch (e: PackageManager.NameNotFoundException) { - Log.e(TAG, "Failed to get app name based on package") - } + return false + } - return appName + fun getInformationFromAccount(account: Account): ImportAccount { + val lastAtPos = account.name.lastIndexOf("@") + var urlString = account.name.substring(lastAtPos + 1) + val username = account.name.substring(0, lastAtPos) + + val context = NextcloudTalkApplication.sharedApplication!!.applicationContext + val accMgr = AccountManager.get(context) + + var password: String? = null + try { + password = accMgr.getPassword(account) + } catch (exception: Exception) { + Log.e(TAG, "Failed to import account") } - fun canWeOpenFilesApp(context: Context, accountName: String): Boolean { - val pm = context.packageManager - try { - val packageInfo = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), 0) - if (packageInfo.versionCode >= 30060151) { - val ownSignatures = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES).signatures - val filesAppSignatures = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), PackageManager.GET_SIGNATURES).signatures - - if (Arrays.equals(ownSignatures, filesAppSignatures)) { - val accMgr = AccountManager.get(context) - val accounts = accMgr.getAccountsByType(context.getString(R.string.nc_import_account_type)) - for (account in accounts) { - if (account.name == accountName) { - return true - } - } - } else { - return true - } - } - } catch (appNotFoundException: PackageManager.NameNotFoundException) { - - } - - return false + if (urlString.endsWith("/")) { + urlString = urlString.substring(0, urlString.length - 1) } - fun getInformationFromAccount(account: Account): ImportAccount { - val lastAtPos = account.name.lastIndexOf("@") - var urlString = account.name.substring(lastAtPos + 1) - val username = account.name.substring(0, lastAtPos) - - val context = NextcloudTalkApplication.sharedApplication!!.applicationContext - val accMgr = AccountManager.get(context) - - var password: String? = null - try { - password = accMgr.getPassword(account) - } catch (exception: Exception) { - Log.e(TAG, "Failed to import account") - } - - if (urlString.endsWith("/")) { - urlString = urlString.substring(0, urlString.length - 1) - } - - return ImportAccount(username, password, urlString) - } + return ImportAccount(username, password, urlString) + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 6ed0b0f8d..4f53dd09c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -22,258 +22,263 @@ package com.nextcloud.talk.utils; import android.net.Uri; import android.text.TextUtils; import androidx.annotation.DimenRes; -import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.BuildConfig; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.models.RetrofitBucket; import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; import okhttp3.Credentials; public class ApiUtils { - private static String ocsApiVersion = "/ocs/v2.php"; - private static String spreedApiVersion = "/apps/spreed/api/v1"; + private static String ocsApiVersion = "/ocs/v2.php"; + private static String spreedApiVersion = "/apps/spreed/api/v1"; - private static String userAgent = "Mozilla/5.0 (Android) Nextcloud-Talk v"; + private static String userAgent = "Mozilla/5.0 (Android) Nextcloud-Talk v"; - public static String getUserAgent() { - return userAgent + BuildConfig.VERSION_NAME; + public static String getUserAgent() { + return userAgent + BuildConfig.VERSION_NAME; + } + + public static String getUrlForLobbyForConversation(String baseUrl, String token) { + return getRoom(baseUrl, token) + "/webinary/lobby"; + } + + public static String getUrlForRemovingParticipantFromConversation(String baseUrl, + String roomToken, boolean isGuest) { + String url = getUrlForParticipants(baseUrl, roomToken); + + if (isGuest) { + url += "/guests"; } - public static String getUrlForLobbyForConversation(String baseUrl, String token) { - return getRoom(baseUrl, token) + "/webinary/lobby"; + return url; + } + + public static RetrofitBucket getRetrofitBucketForContactsSearch(String baseUrl, + @Nullable String searchQuery) { + RetrofitBucket retrofitBucket = new RetrofitBucket(); + retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/sharees"); + + Map queryMap = new HashMap<>(); + + if (searchQuery == null) { + searchQuery = ""; + } + queryMap.put("format", "json"); + queryMap.put("search", searchQuery); + queryMap.put("itemType", "call"); + + retrofitBucket.setQueryMap(queryMap); + + return retrofitBucket; + } + + public static String getUrlForFilePreviewWithRemotePath(String baseUrl, String remotePath, + int px) { + return baseUrl + "/index.php/core/preview.png?file=" + + Uri.encode(remotePath, "UTF-8") + + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; + } + + public static String getUrlForFilePreviewWithFileId(String baseUrl, String fileId, int px) { + return baseUrl + "/index.php/core/preview?fileId=" + + fileId + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; + } + + public static String getSharingUrl(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/shares"; + } + + public static RetrofitBucket getRetrofitBucketForContactsSearchFor14(String baseUrl, + @Nullable String searchQuery) { + RetrofitBucket retrofitBucket = getRetrofitBucketForContactsSearch(baseUrl, searchQuery); + retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/core/autocomplete/get"); + + retrofitBucket.getQueryMap().put("itemId", "new"); + retrofitBucket.getQueryMap().put("limit", "10000"); + + return retrofitBucket; + } + + public static String getUrlForSettingNotificationlevel(String baseUrl, String token) { + return getRoom(baseUrl, token) + "/notify"; + } + + public static String getUrlForSettingMyselfAsActiveParticipant(String baseUrl, String token) { + return getRoom(baseUrl, token) + "/participants/active"; + } + + public static String getUrlForParticipants(String baseUrl, String token) { + return getRoom(baseUrl, token) + "/participants"; + } + + public static String getUrlForCapabilities(String baseUrl) { + return baseUrl + ocsApiVersion + "/cloud/capabilities"; + } + + public static String getUrlForGetRooms(String baseUrl) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/room"; + } + + public static String getRoom(String baseUrl, String token) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token; + } + + public static RetrofitBucket getRetrofitBucketForCreateRoom(String baseUrl, String roomType, + @Nullable String invite, + @Nullable String conversationName) { + RetrofitBucket retrofitBucket = new RetrofitBucket(); + retrofitBucket.setUrl(baseUrl + ocsApiVersion + spreedApiVersion + "/room"); + Map queryMap = new HashMap<>(); + + queryMap.put("roomType", roomType); + if (invite != null) { + queryMap.put("invite", invite); } - public static String getUrlForRemovingParticipantFromConversation(String baseUrl, String roomToken, boolean isGuest) { - String url = getUrlForParticipants(baseUrl, roomToken); - - if (isGuest) { - url += "/guests"; - } - - return url; + if (conversationName != null) { + queryMap.put("roomName", conversationName); } - public static RetrofitBucket getRetrofitBucketForContactsSearch(String baseUrl, @Nullable String searchQuery) { - RetrofitBucket retrofitBucket = new RetrofitBucket(); - retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/sharees"); + retrofitBucket.setQueryMap(queryMap); - Map queryMap = new HashMap<>(); + return retrofitBucket; + } - if (searchQuery == null) { - searchQuery = ""; - } - queryMap.put("format", "json"); - queryMap.put("search", searchQuery); - queryMap.put("itemType", "call"); + public static RetrofitBucket getRetrofitBucketForAddParticipant(String baseUrl, String token, + String user) { + RetrofitBucket retrofitBucket = new RetrofitBucket(); + retrofitBucket.setUrl( + baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/participants"); - retrofitBucket.setQueryMap(queryMap); + Map queryMap = new HashMap<>(); - return retrofitBucket; + queryMap.put("newParticipant", user); + + retrofitBucket.setQueryMap(queryMap); + + return retrofitBucket; + } + + public static RetrofitBucket getRetrofitBucketForAddGroupParticipant(String baseUrl, String token, + String group) { + RetrofitBucket retrofitBucket = getRetrofitBucketForAddParticipant(baseUrl, token, group); + retrofitBucket.getQueryMap().put("source", "groups"); + return retrofitBucket; + } + + public static RetrofitBucket getRetrofitBucketForAddMailParticipant(String baseUrl, String token, + String mail) { + RetrofitBucket retrofitBucket = getRetrofitBucketForAddParticipant(baseUrl, token, mail); + retrofitBucket.getQueryMap().put("source", "emails"); + return retrofitBucket; + } + + public static String getUrlForRemoveSelfFromRoom(String baseUrl, String token) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/participants/self"; + } + + public static String getUrlForRoomVisibility(String baseUrl, String token) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/public"; + } + + public static String getUrlForCall(String baseUrl, String token) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/call/" + token; + } + + public static String getUrlForCallPing(String baseUrl, String token) { + return getUrlForCall(baseUrl, token) + "/ping"; + } + + public static String getUrlForChat(String baseUrl, String token) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/chat/" + token; + } + + public static String getUrlForExternalServerAuthBackend(String baseUrl) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/signaling/backend"; + } + + public static String getUrlForMentionSuggestions(String baseUrl, String token) { + return getUrlForChat(baseUrl, token) + "/mentions"; + } + + public static String getUrlForSignaling(String baseUrl, @Nullable String token) { + String signalingUrl = baseUrl + ocsApiVersion + spreedApiVersion + "/signaling"; + if (token == null) { + return signalingUrl; + } else { + return signalingUrl + "/" + token; } + } - public static String getUrlForFilePreviewWithRemotePath(String baseUrl, String remotePath, int px) { - return baseUrl + "/index.php/core/preview.png?file=" - + Uri.encode(remotePath, "UTF-8") - + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; + public static String getUrlForModerators(String baseUrl, String roomToken) { + return getRoom(baseUrl, roomToken) + "/moderators"; + } + + public static String getUrlForSignalingSettings(String baseUrl) { + return getUrlForSignaling(baseUrl, null) + "/settings"; + } + + public static String getUrlForUserProfile(String baseUrl) { + return baseUrl + ocsApiVersion + "/cloud/user"; + } + + public static String getUrlPostfixForStatus() { + return "/status.php"; + } + + public static String getUrlForAvatarWithNameAndPixels(String baseUrl, String name, + int avatarSize) { + return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize; + } + + public static String getUrlForAvatarWithName(String baseUrl, String name, + @DimenRes int avatarSize) { + avatarSize = Math.round(NextcloudTalkApplication + .Companion.getSharedApplication().getResources().getDimension(avatarSize)); + + return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize; + } + + public static String getUrlForAvatarWithNameForGuests(String baseUrl, String name, + @DimenRes int avatarSize) { + avatarSize = Math.round(NextcloudTalkApplication + .Companion.getSharedApplication().getResources().getDimension(avatarSize)); + + return baseUrl + "/index.php/avatar/guest/" + Uri.encode(name) + "/" + avatarSize; + } + + public static String getUrlForPassword(String baseUrl, String token) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/password"; + } + + public static String getCredentials(String username, String token) { + if (TextUtils.isEmpty(username) && TextUtils.isEmpty(token)) { + return null; } + return Credentials.basic(username, token); + } - public static String getUrlForFilePreviewWithFileId(String baseUrl, String fileId, int px) { - return baseUrl + "/index.php/core/preview?fileId=" - + fileId + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; - } + public static String getUrlNextcloudPush(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/notifications/api/v2/push"; + } - public static String getSharingUrl(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/shares"; - } + public static String getUrlPushProxy() { + return NextcloudTalkApplication.Companion.getSharedApplication(). + getApplicationContext().getResources().getString(R.string.nc_push_server_url) + "/devices"; + } - public static RetrofitBucket getRetrofitBucketForContactsSearchFor14(String baseUrl, @Nullable String searchQuery) { - RetrofitBucket retrofitBucket = getRetrofitBucketForContactsSearch(baseUrl, searchQuery); - retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/core/autocomplete/get"); + public static String getUrlForConversationFavorites(String baseUrl, String roomToken) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + roomToken + "/favorite"; + } - retrofitBucket.getQueryMap().put("itemId", "new"); - retrofitBucket.getQueryMap().put("limit", "10000"); + public static String getUrlForNotificationWithId(String baseUrl, String notificationId) { + return baseUrl + ocsApiVersion + "/apps/notifications/api/v2/notifications/" + notificationId; + } - return retrofitBucket; - } - - - public static String getUrlForSettingNotificationlevel(String baseUrl, String token) { - return getRoom(baseUrl, token) + "/notify"; - } - - public static String getUrlForSettingMyselfAsActiveParticipant(String baseUrl, String token) { - return getRoom(baseUrl, token) + "/participants/active"; - } - - - public static String getUrlForParticipants(String baseUrl, String token) { - return getRoom(baseUrl, token) + "/participants"; - } - - public static String getUrlForCapabilities(String baseUrl) { - return baseUrl + ocsApiVersion + "/cloud/capabilities"; - } - - public static String getUrlForGetRooms(String baseUrl) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/room"; - } - - public static String getRoom(String baseUrl, String token) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token; - } - - public static RetrofitBucket getRetrofitBucketForCreateRoom(String baseUrl, String roomType, - @Nullable String invite, - @Nullable String conversationName) { - RetrofitBucket retrofitBucket = new RetrofitBucket(); - retrofitBucket.setUrl(baseUrl + ocsApiVersion + spreedApiVersion + "/room"); - Map queryMap = new HashMap<>(); - - queryMap.put("roomType", roomType); - if (invite != null) { - queryMap.put("invite", invite); - } - - if (conversationName != null) { - queryMap.put("roomName", conversationName); - } - - retrofitBucket.setQueryMap(queryMap); - - return retrofitBucket; - } - - public static RetrofitBucket getRetrofitBucketForAddParticipant(String baseUrl, String token, String user) { - RetrofitBucket retrofitBucket = new RetrofitBucket(); - retrofitBucket.setUrl(baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/participants"); - - Map queryMap = new HashMap<>(); - - queryMap.put("newParticipant", user); - - retrofitBucket.setQueryMap(queryMap); - - return retrofitBucket; - - } - - public static RetrofitBucket getRetrofitBucketForAddGroupParticipant(String baseUrl, String token, String group) { - RetrofitBucket retrofitBucket = getRetrofitBucketForAddParticipant(baseUrl, token, group); - retrofitBucket.getQueryMap().put("source", "groups"); - return retrofitBucket; - } - - public static RetrofitBucket getRetrofitBucketForAddMailParticipant(String baseUrl, String token, String mail) { - RetrofitBucket retrofitBucket = getRetrofitBucketForAddParticipant(baseUrl, token, mail); - retrofitBucket.getQueryMap().put("source", "emails"); - return retrofitBucket; - } - - public static String getUrlForRemoveSelfFromRoom(String baseUrl, String token) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/participants/self"; - } - - public static String getUrlForRoomVisibility(String baseUrl, String token) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/public"; - } - - public static String getUrlForCall(String baseUrl, String token) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/call/" + token; - - } - - public static String getUrlForCallPing(String baseUrl, String token) { - return getUrlForCall(baseUrl, token) + "/ping"; - } - - public static String getUrlForChat(String baseUrl, String token) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/chat/" + token; - } - - public static String getUrlForExternalServerAuthBackend(String baseUrl) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/signaling/backend"; - } - - public static String getUrlForMentionSuggestions(String baseUrl, String token) { - return getUrlForChat(baseUrl, token) + "/mentions"; - } - - public static String getUrlForSignaling(String baseUrl, @Nullable String token) { - String signalingUrl = baseUrl + ocsApiVersion + spreedApiVersion + "/signaling"; - if (token == null) { - return signalingUrl; - } else { - return signalingUrl + "/" + token; - } - } - - public static String getUrlForModerators(String baseUrl, String roomToken) { - return getRoom(baseUrl, roomToken) + "/moderators"; - } - - public static String getUrlForSignalingSettings(String baseUrl) { - return getUrlForSignaling(baseUrl, null) + "/settings"; - } - - - public static String getUrlForUserProfile(String baseUrl) { - return baseUrl + ocsApiVersion + "/cloud/user"; - } - - public static String getUrlPostfixForStatus() { - return "/status.php"; - } - - public static String getUrlForAvatarWithNameAndPixels(String baseUrl, String name, int avatarSize) { - return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize; - } - - public static String getUrlForAvatarWithName(String baseUrl, String name, @DimenRes int avatarSize) { - avatarSize = Math.round(NextcloudTalkApplication - .Companion.getSharedApplication().getResources().getDimension(avatarSize)); - - return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize; - } - - public static String getUrlForAvatarWithNameForGuests(String baseUrl, String name, - @DimenRes int avatarSize) { - avatarSize = Math.round(NextcloudTalkApplication - .Companion.getSharedApplication().getResources().getDimension(avatarSize)); - - return baseUrl + "/index.php/avatar/guest/" + Uri.encode(name) + "/" + avatarSize; - } - - public static String getUrlForPassword(String baseUrl, String token) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token + "/password"; - } - - public static String getCredentials(String username, String token) { - if (TextUtils.isEmpty(username) && TextUtils.isEmpty(token)) { - return null; - } - return Credentials.basic(username, token); - } - - public static String getUrlNextcloudPush(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/notifications/api/v2/push"; - } - - public static String getUrlPushProxy() { - return NextcloudTalkApplication.Companion.getSharedApplication(). - getApplicationContext().getResources().getString(R.string.nc_push_server_url) + "/devices"; - } - - public static String getUrlForConversationFavorites(String baseUrl, String roomToken) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + roomToken + "/favorite"; - } - - public static String getUrlForNotificationWithId(String baseUrl, String notificationId) { - return baseUrl + ocsApiVersion + "/apps/notifications/api/v2/notifications/" + notificationId; - } - - public static String getUrlForReadOnlyState(String baseUrl, String roomToken) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + roomToken + "/read-only"; - } + public static String getUrlForReadOnlyState(String baseUrl, String roomToken) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + roomToken + "/read-only"; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt index 79fe7eda9..a0e56f3a1 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt @@ -26,19 +26,21 @@ import java.util.Date import java.util.Locale object DateUtils { - fun getLocalDateTimeStringFromTimestamp(timestamp: Long): String { - val cal = Calendar.getInstance() - val tz = cal.timeZone + fun getLocalDateTimeStringFromTimestamp(timestamp: Long): String { + val cal = Calendar.getInstance() + val tz = cal.timeZone - /* date formatter in local timezone */ - val format = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, Locale - .getDefault()) - format.timeZone = tz + /* date formatter in local timezone */ + val format = DateFormat.getDateTimeInstance( + DateFormat.DEFAULT, DateFormat.SHORT, Locale + .getDefault() + ) + format.timeZone = tz - return format.format(Date(timestamp)) - } + return format.format(Date(timestamp)) + } - fun getLocalDateStringFromTimestampForLobby(timestamp: Long): String { - return getLocalDateTimeStringFromTimestamp(timestamp * 1000) - } + fun getLocalDateStringFromTimestampForLobby(timestamp: Long): String { + return getLocalDateTimeStringFromTimestamp(timestamp * 1000) + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/DeviceUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DeviceUtils.java index 8a67ec13f..6a68bb172 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DeviceUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DeviceUtils.java @@ -31,59 +31,67 @@ import java.util.ArrayList; import java.util.List; public class DeviceUtils { - private static final String TAG = "DeviceUtils"; + private static final String TAG = "DeviceUtils"; - public static void ignoreSpecialBatteryFeatures() { - if (Build.MANUFACTURER.equalsIgnoreCase("xiaomi") || Build.MANUFACTURER.equalsIgnoreCase("meizu")) { - try { - @SuppressLint("PrivateApi") Class appOpsUtilsClass = Class.forName("android.miui.AppOpsUtils"); - if (appOpsUtilsClass != null) { - Method setApplicationAutoStartMethod = appOpsUtilsClass.getMethod("setApplicationAutoStart", Context - .class, String.class, Boolean.TYPE); - if (setApplicationAutoStartMethod != null) { - Context applicationContext = NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); - setApplicationAutoStartMethod.invoke(appOpsUtilsClass, applicationContext, applicationContext - .getPackageName(), Boolean.TRUE); - } - } - } catch (ClassNotFoundException e) { - Log.e(TAG, "Class not found"); - } catch (NoSuchMethodException e) { - Log.e(TAG, "No such method"); - } catch (IllegalAccessException e) { - Log.e(TAG, "IllegalAccessException"); - } catch (InvocationTargetException e) { - Log.e(TAG, "InvocationTargetException"); - } - } else if (Build.MANUFACTURER.equalsIgnoreCase("huawei")) { - try { - @SuppressLint("PrivateApi") Class protectAppControlClass = Class.forName("com.huawei.systemmanager.optimize.process" + - ".ProtectAppControl"); - if (protectAppControlClass != null) { - Context applicationContext = NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); - - Method getInstanceMethod = protectAppControlClass.getMethod("getInstance", Context.class); - // ProtectAppControl instance - if (getInstanceMethod != null) { - Object protectAppControlInstance = getInstanceMethod.invoke(null, applicationContext); - - Method setProtectMethod = protectAppControlClass.getDeclaredMethod("setProtect", List.class); - if (setProtectMethod != null) { - List appsList = new ArrayList<>(); - appsList.add(applicationContext.getPackageName()); - setProtectMethod.invoke(protectAppControlInstance, appsList); - } - } - } - } catch (ClassNotFoundException e) { - Log.e(TAG, "Class not found"); - } catch (NoSuchMethodException e) { - Log.e(TAG, "No such method"); - } catch (IllegalAccessException e) { - Log.e(TAG, "IllegalAccessException"); - } catch (InvocationTargetException e) { - Log.e(TAG, "InvocationTargetException"); - } + public static void ignoreSpecialBatteryFeatures() { + if (Build.MANUFACTURER.equalsIgnoreCase("xiaomi") || Build.MANUFACTURER.equalsIgnoreCase( + "meizu")) { + try { + @SuppressLint("PrivateApi") Class appOpsUtilsClass = + Class.forName("android.miui.AppOpsUtils"); + if (appOpsUtilsClass != null) { + Method setApplicationAutoStartMethod = + appOpsUtilsClass.getMethod("setApplicationAutoStart", Context + .class, String.class, Boolean.TYPE); + if (setApplicationAutoStartMethod != null) { + Context applicationContext = + NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); + setApplicationAutoStartMethod.invoke(appOpsUtilsClass, applicationContext, + applicationContext + .getPackageName(), Boolean.TRUE); + } } + } catch (ClassNotFoundException e) { + Log.e(TAG, "Class not found"); + } catch (NoSuchMethodException e) { + Log.e(TAG, "No such method"); + } catch (IllegalAccessException e) { + Log.e(TAG, "IllegalAccessException"); + } catch (InvocationTargetException e) { + Log.e(TAG, "InvocationTargetException"); + } + } else if (Build.MANUFACTURER.equalsIgnoreCase("huawei")) { + try { + @SuppressLint("PrivateApi") Class protectAppControlClass = + Class.forName("com.huawei.systemmanager.optimize.process" + + ".ProtectAppControl"); + if (protectAppControlClass != null) { + Context applicationContext = + NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); + + Method getInstanceMethod = protectAppControlClass.getMethod("getInstance", Context.class); + // ProtectAppControl instance + if (getInstanceMethod != null) { + Object protectAppControlInstance = getInstanceMethod.invoke(null, applicationContext); + + Method setProtectMethod = + protectAppControlClass.getDeclaredMethod("setProtect", List.class); + if (setProtectMethod != null) { + List appsList = new ArrayList<>(); + appsList.add(applicationContext.getPackageName()); + setProtectMethod.invoke(protectAppControlInstance, appsList); + } + } + } + } catch (ClassNotFoundException e) { + Log.e(TAG, "Class not found"); + } catch (NoSuchMethodException e) { + Log.e(TAG, "No such method"); + } catch (IllegalAccessException e) { + Log.e(TAG, "IllegalAccessException"); + } catch (InvocationTargetException e) { + Log.e(TAG, "InvocationTargetException"); + } } + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java index 35e4e89c1..3cb107627 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java @@ -96,316 +96,339 @@ import org.greenrobot.eventbus.EventBus; public class DisplayUtils { - private static final String TAG = "DisplayUtils"; + private static final String TAG = "DisplayUtils"; - public static void setClickableString(String string, String url, TextView textView) { - SpannableString spannableString = new SpannableString(string); - spannableString.setSpan(new ClickableSpan() { + public static void setClickableString(String string, String url, TextView textView) { + SpannableString spannableString = new SpannableString(string); + spannableString.setSpan(new ClickableSpan() { + @Override + public void onClick(@Nonnull View widget) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + NextcloudTalkApplication.Companion.getSharedApplication() + .getApplicationContext() + .startActivity(browserIntent); + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + } + }, 0, string.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + textView.setText(spannableString); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + } + + private static void updateViewSize(@Nullable ImageInfo imageInfo, SimpleDraweeView draweeView) { + if (imageInfo != null) { + int maxSize = draweeView.getContext() + .getResources() + .getDimensionPixelSize(R.dimen.maximum_file_preview_size); + draweeView.getLayoutParams().width = + imageInfo.getWidth() > maxSize ? maxSize : imageInfo.getWidth(); + draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; + draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); + draweeView.requestLayout(); + } + } + + public static Drawable getRoundedDrawable(Drawable drawable) { + Bitmap bitmap = getBitmap(drawable); + new RoundAsCirclePostprocessor(true).process(bitmap); + return new BitmapDrawable(bitmap); + } + + public static Bitmap getRoundedBitmapFromVectorDrawableResource(Resources resources, + int resource) { + VectorDrawable vectorDrawable = (VectorDrawable) resources.getDrawable(resource); + Bitmap bitmap = getBitmap(vectorDrawable); + new RoundPostprocessor(true).process(bitmap); + return bitmap; + } + + public static Drawable getRoundedBitmapDrawableFromVectorDrawableResource(Resources resources, + int resource) { + return new BitmapDrawable(getRoundedBitmapFromVectorDrawableResource(resources, resource)); + } + + private static Bitmap getBitmap(Drawable drawable) { + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + + public static ImageRequest getImageRequestForUrl(String url, @Nullable UserEntity userEntity) { + Map headers = new HashMap<>(); + if (userEntity != null && url.startsWith(userEntity.getBaseUrl()) && url.contains( + "index.php/core/preview?fileId=")) { + headers.put("Authorization", + ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken())); + } + + return ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) + .setProgressiveRenderingEnabled(true) + .setRotationOptions(RotationOptions.autoRotate()) + .disableDiskCache() + .setHeaders(headers) + .build(); + } + + public static ControllerListener getImageControllerListener(SimpleDraweeView draweeView) { + return new ControllerListener() { + @Override + public void onSubmit(String id, Object callerContext) { + + } + + @Override + public void onFinalImageSet(String id, @javax.annotation.Nullable Object imageInfo, + @javax.annotation.Nullable Animatable animatable) { + updateViewSize((ImageInfo) imageInfo, draweeView); + } + + @Override + public void onIntermediateImageSet(String id, @javax.annotation.Nullable Object imageInfo) { + updateViewSize((ImageInfo) imageInfo, draweeView); + } + + @Override + public void onIntermediateImageFailed(String id, Throwable throwable) { + + } + + @Override + public void onFailure(String id, Throwable throwable) { + + } + + @Override + public void onRelease(String id) { + + } + }; + } + + public static float convertDpToPixel(float dp, Context context) { + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, + context.getResources().getDisplayMetrics()) + 0.5f); + } + + // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected + public static void useCompatVectorIfNeeded() { + if (Build.VERSION.SDK_INT < 23) { + try { + @SuppressLint("RestrictedApi") AppCompatDrawableManager drawableManager = + AppCompatDrawableManager.get(); + Class inflateDelegateClass = + Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate"); + Class vdcInflateDelegateClass = + Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate"); + + Constructor constructor = vdcInflateDelegateClass.getDeclaredConstructor(); + constructor.setAccessible(true); + Object vdcInflateDelegate = constructor.newInstance(); + + Class[] args = { String.class, inflateDelegateClass }; + Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args); + addDelegate.setAccessible(true); + addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate); + } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | + InvocationTargetException | IllegalAccessException e) { + Log.e(TAG, "Failed to use reflection to enable proper vector scaling"); + } + } + } + + public static Drawable getTintedDrawable(Resources res, @DrawableRes int drawableResId, + @ColorRes int colorResId) { + Drawable drawable = res.getDrawable(drawableResId); + int color = res.getColor(colorResId); + drawable.setTint(color); + return drawable; + } + + public static Drawable getDrawableForMentionChipSpan(Context context, String id, + CharSequence label, + UserEntity conversationUser, String type, + @XmlRes int chipResource, + @Nullable EditText emojiEditText) { + ChipDrawable chip = ChipDrawable.createFromResource(context, chipResource); + chip.setText(EmojiCompat.get().process(label)); + chip.setEllipsize(TextUtils.TruncateAt.MIDDLE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Configuration config = context.getResources().getConfiguration(); + chip.setLayoutDirection(config.getLayoutDirection()); + } + + int drawable; + + boolean isCall = "call".equals(type) || "calls".equals(type); + + if (!isCall) { + if (chipResource == R.xml.chip_you) { + drawable = R.drawable.mention_chip; + } else { + drawable = R.drawable.accent_circle; + } + + chip.setChipIcon(context.getDrawable(drawable)); + } else { + chip.setChipIcon( + getRoundedDrawable(context.getDrawable(R.drawable.ic_people_group_white_24px))); + } + + chip.setBounds(0, 0, chip.getIntrinsicWidth(), chip.getIntrinsicHeight()); + + if (!isCall) { + String url = ApiUtils.getUrlForAvatarWithName(conversationUser.getBaseUrl(), id, + R.dimen.avatar_size_big); + if ("guests".equals(type) || "guest".equals(type)) { + url = ApiUtils.getUrlForAvatarWithNameForGuests(conversationUser.getBaseUrl(), + String.valueOf(label), R.dimen.avatar_size_big); + } + ImageRequest imageRequest = getImageRequestForUrl(url, null); + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + DataSource> dataSource = + imagePipeline.fetchDecodedImage(imageRequest, context); + + dataSource.subscribe( + new BaseBitmapDataSubscriber() { @Override - public void onClick(@Nonnull View widget) { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext().startActivity(browserIntent); + protected void onNewResultImpl(Bitmap bitmap) { + if (bitmap != null) { + chip.setChipIcon(getRoundedDrawable(new BitmapDrawable(bitmap))); + + // A hack to refresh the chip icon + if (emojiEditText != null) { + emojiEditText.post(() -> emojiEditText.setTextKeepState(emojiEditText.getText(), + TextView.BufferType.SPANNABLE)); + } + } } @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); + protected void onFailureImpl( + DataSource> dataSource) { } - }, 0, string.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - textView.setText(spannableString); - textView.setMovementMethod(LinkMovementMethod.getInstance()); + }, + UiThreadImmediateExecutorService.getInstance()); } - private static void updateViewSize(@Nullable ImageInfo imageInfo, SimpleDraweeView draweeView) { - if (imageInfo != null) { - int maxSize = draweeView.getContext().getResources().getDimensionPixelSize(R.dimen.maximum_file_preview_size); - draweeView.getLayoutParams().width = imageInfo.getWidth() > maxSize ? maxSize : imageInfo.getWidth(); - draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; - draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); - draweeView.requestLayout(); - } + return chip; + } + + public static Spannable searchAndReplaceWithMentionSpan(Context context, Spannable text, + String id, String label, String type, + UserEntity conversationUser, + @XmlRes int chipXmlRes) { + + Spannable spannableString = new SpannableString(text); + String stringText = text.toString(); + + Matcher m = Pattern.compile("@" + label, + Pattern.CASE_INSENSITIVE | Pattern.LITERAL | Pattern.MULTILINE) + .matcher(spannableString); + + ClickableSpan clickableSpan = new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + EventBus.getDefault().post(new UserMentionClickEvent(id)); + } + }; + + int lastStartIndex = -1; + Spans.MentionChipSpan mentionChipSpan; + while (m.find()) { + int start = stringText.indexOf(m.group(), lastStartIndex); + int end = start + m.group().length(); + lastStartIndex = end; + mentionChipSpan = + new Spans.MentionChipSpan(DisplayUtils.getDrawableForMentionChipSpan(context, + id, label, conversationUser, type, chipXmlRes, null), + BetterImageSpan.ALIGN_CENTER, id, + label); + spannableString.setSpan(mentionChipSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + if ("user".equals(type) && !conversationUser.getUserId().equals(id)) { + spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + } } - public static Drawable getRoundedDrawable(Drawable drawable) { - Bitmap bitmap = getBitmap(drawable); - new RoundAsCirclePostprocessor(true).process(bitmap); - return new BitmapDrawable(bitmap); + return spannableString; + } + + public static Spannable searchAndColor(Spannable text, String searchText, @ColorInt int color) { + + Spannable spannableString = new SpannableString(text); + String stringText = text.toString(); + if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchText)) { + return spannableString; } - public static Bitmap getRoundedBitmapFromVectorDrawableResource(Resources resources, int resource) { - VectorDrawable vectorDrawable = (VectorDrawable) resources.getDrawable(resource); - Bitmap bitmap = getBitmap(vectorDrawable); - new RoundPostprocessor(true).process(bitmap); - return bitmap; + Matcher m = Pattern.compile(searchText, + Pattern.CASE_INSENSITIVE | Pattern.LITERAL | Pattern.MULTILINE) + .matcher(spannableString); + + int textSize = NextcloudTalkApplication.Companion.getSharedApplication() + .getResources() + .getDimensionPixelSize(R.dimen + .chat_text_size); + + int lastStartIndex = -1; + while (m.find()) { + int start = stringText.indexOf(m.group(), lastStartIndex); + int end = start + m.group().length(); + lastStartIndex = end; + spannableString.setSpan(new ForegroundColorSpan(color), start, end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + spannableString.setSpan(new AbsoluteSizeSpan(textSize), start, end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } - public static Drawable getRoundedBitmapDrawableFromVectorDrawableResource(Resources resources, int resource) { - return new BitmapDrawable(getRoundedBitmapFromVectorDrawableResource(resources, resource)); + return spannableString; + } + + public static Drawable getMessageSelector(@ColorInt int normalColor, @ColorInt int selectedColor, + @ColorInt int pressedColor, @DrawableRes int shape) { + + Drawable vectorDrawable = + ContextCompat.getDrawable(NextcloudTalkApplication.Companion.getSharedApplication() + .getApplicationContext(), + shape); + Drawable drawable = DrawableCompat.wrap(vectorDrawable).mutate(); + DrawableCompat.setTintList( + drawable, + new ColorStateList( + new int[][] { + new int[] { android.R.attr.state_selected }, + new int[] { android.R.attr.state_pressed }, + new int[] { -android.R.attr.state_pressed, -android.R.attr.state_selected } + }, + new int[] { selectedColor, pressedColor, normalColor } + )); + return drawable; + } + + public static boolean isDarkModeActive(Context context, AppPreferences prefs) { + if (prefs.getTheme().equals("night_yes")) { + return true; } - private static Bitmap getBitmap(Drawable drawable) { - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } - - public static ImageRequest getImageRequestForUrl(String url, @Nullable UserEntity userEntity) { - Map headers = new HashMap<>(); - if (userEntity != null && url.startsWith(userEntity.getBaseUrl()) && url.contains("index.php/core/preview?fileId=")) { - headers.put("Authorization", ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken())); - } - - return ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) - .setProgressiveRenderingEnabled(true) - .setRotationOptions(RotationOptions.autoRotate()) - .disableDiskCache() - .setHeaders(headers) - .build(); - } - - public static ControllerListener getImageControllerListener(SimpleDraweeView draweeView) { - return new ControllerListener() { - @Override - public void onSubmit(String id, Object callerContext) { - - } - - @Override - public void onFinalImageSet(String id, @javax.annotation.Nullable Object imageInfo, @javax.annotation.Nullable Animatable animatable) { - updateViewSize((ImageInfo) imageInfo, draweeView); - } - - @Override - public void onIntermediateImageSet(String id, @javax.annotation.Nullable Object imageInfo) { - updateViewSize((ImageInfo) imageInfo, draweeView); - } - - @Override - public void onIntermediateImageFailed(String id, Throwable throwable) { - - } - - @Override - public void onFailure(String id, Throwable throwable) { - - } - - @Override - public void onRelease(String id) { - - } - }; - } - - public static float convertDpToPixel(float dp, Context context) { - return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, - context.getResources().getDisplayMetrics()) + 0.5f); - } - - // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected - public static void useCompatVectorIfNeeded() { - if (Build.VERSION.SDK_INT < 23) { - try { - @SuppressLint("RestrictedApi") AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get(); - Class inflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate"); - Class vdcInflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate"); - - Constructor constructor = vdcInflateDelegateClass.getDeclaredConstructor(); - constructor.setAccessible(true); - Object vdcInflateDelegate = constructor.newInstance(); - - Class[] args = { String.class, inflateDelegateClass }; - Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args); - addDelegate.setAccessible(true); - addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate); - } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | - InvocationTargetException | IllegalAccessException e) { - Log.e(TAG, "Failed to use reflection to enable proper vector scaling"); - } - } - } - - public static Drawable getTintedDrawable(Resources res, @DrawableRes int drawableResId, @ColorRes int colorResId) { - Drawable drawable = res.getDrawable(drawableResId); - int color = res.getColor(colorResId); - drawable.setTint(color); - return drawable; - } - - - public static Drawable getDrawableForMentionChipSpan(Context context, String id, CharSequence label, - UserEntity conversationUser, String type, - @XmlRes int chipResource, - @Nullable EditText emojiEditText) { - ChipDrawable chip = ChipDrawable.createFromResource(context, chipResource); - chip.setText(EmojiCompat.get().process(label)); - chip.setEllipsize(TextUtils.TruncateAt.MIDDLE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - Configuration config = context.getResources().getConfiguration(); - chip.setLayoutDirection(config.getLayoutDirection()); - } - - int drawable; - - boolean isCall = "call".equals(type) || "calls".equals(type); - - if (!isCall) { - if (chipResource == R.xml.chip_you) { - drawable = R.drawable.mention_chip; - } else { - drawable = R.drawable.accent_circle; - } - - chip.setChipIcon(context.getDrawable(drawable)); - } else { - chip.setChipIcon(getRoundedDrawable(context.getDrawable(R.drawable.ic_people_group_white_24px))); - } - - chip.setBounds(0, 0, chip.getIntrinsicWidth(), chip.getIntrinsicHeight()); - - if (!isCall) { - String url = ApiUtils.getUrlForAvatarWithName(conversationUser.getBaseUrl(), id, R.dimen.avatar_size_big); - if ("guests".equals(type) || "guest".equals(type)) { - url = ApiUtils.getUrlForAvatarWithNameForGuests(conversationUser.getBaseUrl(), String.valueOf(label), R.dimen.avatar_size_big); - } - ImageRequest imageRequest = getImageRequestForUrl(url, null); - ImagePipeline imagePipeline = Fresco.getImagePipeline(); - DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context); - - dataSource.subscribe( - new BaseBitmapDataSubscriber() { - @Override - protected void onNewResultImpl(Bitmap bitmap) { - if (bitmap != null) { - chip.setChipIcon(getRoundedDrawable(new BitmapDrawable(bitmap))); - - // A hack to refresh the chip icon - if (emojiEditText != null) { - emojiEditText.post(() -> emojiEditText.setTextKeepState(emojiEditText.getText(), TextView.BufferType.SPANNABLE)); - } - } - } - - @Override - protected void onFailureImpl(DataSource> dataSource) { - } - }, - UiThreadImmediateExecutorService.getInstance()); - } - - return chip; - } - - - public static Spannable searchAndReplaceWithMentionSpan(Context context, Spannable text, - String id, String label, String type, - UserEntity conversationUser, - @XmlRes int chipXmlRes) { - - Spannable spannableString = new SpannableString(text); - String stringText = text.toString(); - - Matcher m = Pattern.compile("@" + label, - Pattern.CASE_INSENSITIVE | Pattern.LITERAL | Pattern.MULTILINE) - .matcher(spannableString); - - ClickableSpan clickableSpan = new ClickableSpan() { - @Override - public void onClick(@NonNull View widget) { - EventBus.getDefault().post(new UserMentionClickEvent(id)); - } - }; - - int lastStartIndex = -1; - Spans.MentionChipSpan mentionChipSpan; - while (m.find()) { - int start = stringText.indexOf(m.group(), lastStartIndex); - int end = start + m.group().length(); - lastStartIndex = end; - mentionChipSpan = new Spans.MentionChipSpan(DisplayUtils.getDrawableForMentionChipSpan(context, - id, label, conversationUser, type, chipXmlRes, null), - BetterImageSpan.ALIGN_CENTER, id, - label); - spannableString.setSpan(mentionChipSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - if ("user".equals(type) && !conversationUser.getUserId().equals(id)) { - spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - - return spannableString; - - } - - public static Spannable searchAndColor(Spannable text, String searchText, @ColorInt int color) { - - Spannable spannableString = new SpannableString(text); - String stringText = text.toString(); - if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchText)) { - return spannableString; - } - - Matcher m = Pattern.compile(searchText, - Pattern.CASE_INSENSITIVE | Pattern.LITERAL | Pattern.MULTILINE) - .matcher(spannableString); - - - int textSize = NextcloudTalkApplication.Companion.getSharedApplication().getResources().getDimensionPixelSize(R.dimen - .chat_text_size); - - int lastStartIndex = -1; - while (m.find()) { - int start = stringText.indexOf(m.group(), lastStartIndex); - int end = start + m.group().length(); - lastStartIndex = end; - spannableString.setSpan(new ForegroundColorSpan(color), start, end, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - spannableString.setSpan(new AbsoluteSizeSpan(textSize), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - return spannableString; - } - - public static Drawable getMessageSelector(@ColorInt int normalColor, @ColorInt int selectedColor, - @ColorInt int pressedColor, @DrawableRes int shape) { - - Drawable vectorDrawable = ContextCompat.getDrawable(NextcloudTalkApplication.Companion.getSharedApplication() - .getApplicationContext(), - shape); - Drawable drawable = DrawableCompat.wrap(vectorDrawable).mutate(); - DrawableCompat.setTintList( - drawable, - new ColorStateList( - new int[][]{ - new int[]{android.R.attr.state_selected}, - new int[]{android.R.attr.state_pressed}, - new int[]{-android.R.attr.state_pressed, -android.R.attr.state_selected} - }, - new int[]{selectedColor, pressedColor, normalColor} - )); - return drawable; - } - - public static boolean isDarkModeActive(Context context, AppPreferences prefs) { - if (prefs.getTheme().equals("night_yes")) { - return true; - } - - int currentNightMode = - context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - switch (currentNightMode) { - case Configuration.UI_MODE_NIGHT_NO: - return false; - case Configuration.UI_MODE_NIGHT_YES: - return true; - default: - return false; - } + int currentNightMode = + context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + switch (currentNightMode) { + case Configuration.UI_MODE_NIGHT_NO: + return false; + case Configuration.UI_MODE_NIGHT_YES: + return true; + default: + return false; } + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/DoNotDisturbUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DoNotDisturbUtils.kt index ca624cb36..b697a7475 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DoNotDisturbUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/DoNotDisturbUtils.kt @@ -28,78 +28,82 @@ import android.os.Vibrator import com.nextcloud.talk.application.NextcloudTalkApplication object DoNotDisturbUtils { - fun isDnDActive() : Boolean { - val context = NextcloudTalkApplication.sharedApplication?.applicationContext - - val notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (notificationManager.currentInterruptionFilter == NotificationManager - .INTERRUPTION_FILTER_NONE || notificationManager - .currentInterruptionFilter == NotificationManager - .INTERRUPTION_FILTER_ALARMS || notificationManager - .currentInterruptionFilter == NotificationManager.INTERRUPTION_FILTER_PRIORITY) { - return true - } - } - - return false + fun isDnDActive(): Boolean { + val context = NextcloudTalkApplication.sharedApplication?.applicationContext + val notificationManager = + context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (notificationManager.currentInterruptionFilter == NotificationManager + .INTERRUPTION_FILTER_NONE || notificationManager + .currentInterruptionFilter == NotificationManager + .INTERRUPTION_FILTER_ALARMS || notificationManager + .currentInterruptionFilter == NotificationManager.INTERRUPTION_FILTER_PRIORITY + ) { + return true + } } - fun isInDoNotDisturbWithPriority(): Boolean { - val context = NextcloudTalkApplication.sharedApplication?.applicationContext + return false - val notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (notificationManager.currentInterruptionFilter == NotificationManager.INTERRUPTION_FILTER_PRIORITY) { - return true - } - } + } - return false + fun isInDoNotDisturbWithPriority(): Boolean { + val context = NextcloudTalkApplication.sharedApplication?.applicationContext + + val notificationManager = + context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (notificationManager.currentInterruptionFilter == NotificationManager.INTERRUPTION_FILTER_PRIORITY) { + return true + } } - fun shouldPlaySound(): Boolean { - val context = NextcloudTalkApplication.sharedApplication?.applicationContext + return false + } - val notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + fun shouldPlaySound(): Boolean { + val context = NextcloudTalkApplication.sharedApplication?.applicationContext - var shouldPlaySound = true - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (notificationManager.currentInterruptionFilter != NotificationManager.INTERRUPTION_FILTER_ALL) { - shouldPlaySound = false - } - } + val notificationManager = + context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager - if (shouldPlaySound) { - if (audioManager.ringerMode != AudioManager.RINGER_MODE_NORMAL) { - shouldPlaySound = false - } - } - - return shouldPlaySound + var shouldPlaySound = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (notificationManager.currentInterruptionFilter != NotificationManager.INTERRUPTION_FILTER_ALL) { + shouldPlaySound = false + } } - fun hasVibrator(): Boolean { - val context = NextcloudTalkApplication.sharedApplication?.applicationContext - val vibrator = context?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - return vibrator.hasVibrator() + if (shouldPlaySound) { + if (audioManager.ringerMode != AudioManager.RINGER_MODE_NORMAL) { + shouldPlaySound = false + } } - fun shouldVibrate(vibrate: Boolean): Boolean { + return shouldPlaySound + } - if (hasVibrator()) { - val context = NextcloudTalkApplication.sharedApplication?.applicationContext - val audioManager = context?.getSystemService(Context.AUDIO_SERVICE) as AudioManager + fun hasVibrator(): Boolean { + val context = NextcloudTalkApplication.sharedApplication?.applicationContext + val vibrator = context?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + return vibrator.hasVibrator() + } - return if (vibrate) { - audioManager.ringerMode != AudioManager.RINGER_MODE_SILENT - } else { - audioManager.ringerMode == AudioManager.RINGER_MODE_VIBRATE - } - } + fun shouldVibrate(vibrate: Boolean): Boolean { - return false + if (hasVibrator()) { + val context = NextcloudTalkApplication.sharedApplication?.applicationContext + val audioManager = context?.getSystemService(Context.AUDIO_SERVICE) as AudioManager + + return if (vibrate) { + audioManager.ringerMode != AudioManager.RINGER_MODE_SILENT + } else { + audioManager.ringerMode == AudioManager.RINGER_MODE_VIBRATE + } } + + return false + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt index 14afd4298..c132f3a7d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt @@ -26,133 +26,161 @@ import java.util.HashMap object DrawableUtils { + fun getDrawableResourceIdForMimeType(mimetype: String): Int { + var localMimetype = mimetype + val drawableMap = HashMap() - fun getDrawableResourceIdForMimeType(mimetype: String): Int { - var localMimetype = mimetype - val drawableMap = HashMap() + // Initial list of mimetypes was acquired from https://github.com/nextcloud/server/blob/694ba5435b2963e201f6a6d2c775836bde07aaef/core/js/mimetypelist.js + drawableMap["application/coreldraw"] = R.drawable.ic_mimetype_image + drawableMap["application/epub+zip"] = R.drawable.ic_mimetype_text + drawableMap["application/font-sfnt"] = R.drawable.ic_mimetype_image + drawableMap["application/font-woff"] = R.drawable.ic_mimetype_image + drawableMap["application/gpx+xml"] = R.drawable.ic_mimetype_location + drawableMap["application/illustrator"] = R.drawable.ic_mimetype_image + drawableMap["application/javascript"] = R.drawable.ic_mimetype_text_code + drawableMap["application/json"] = R.drawable.ic_mimetype_text_code + drawableMap["application/msaccess"] = R.drawable.ic_mimetype_file + drawableMap["application/msexcel"] = R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/msonenote"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/mspowerpoint"] = R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/msword"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/octet-stream"] = R.drawable.ic_mimetype_file + drawableMap["application/postscript"] = R.drawable.ic_mimetype_image + drawableMap["application/rss+xml"] = R.drawable.ic_mimetype_text_code + drawableMap["application/vnd.android.package-archive"] = + R.drawable.ic_mimetype_package_x_generic + drawableMap["application/vnd.lotus-wordpro"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.garmin.tcx+xml"] = R.drawable.ic_mimetype_location + drawableMap["application/vnd.google-earth.kml+xml"] = R.drawable.ic_mimetype_location + drawableMap["application/vnd.google-earth.kmz"] = R.drawable.ic_mimetype_location + drawableMap["application/vnd.ms-excel"] = R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.ms-excel.addin.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.ms-excel.sheet.binary.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.ms-excel.sheet.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.ms-excel.template.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.ms-fontobject"] = R.drawable.ic_mimetype_image + drawableMap["application/vnd.ms-powerpoint"] = R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.ms-powerpoint.addin.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.ms-powerpoint.template.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.ms-visio.drawing.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.ms-visio.drawing"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.ms-visio.stencil.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.ms-visio.stencil"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.ms-visio.template.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.ms-visio.template"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.ms-word.template.macroEnabled.12"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.oasis.opendocument.presentation"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.oasis.opendocument.presentation-template"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.oasis.opendocument.spreadsheet"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.oasis.opendocument.spreadsheet-template"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.oasis.opendocument.text"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.oasis.opendocument.text-master"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.oasis.opendocument.text-template"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.oasis.opendocument.text-web"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.openxmlformats-officedocument.presentationml.presentation"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.openxmlformats-officedocument.presentationml.slideshow"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.openxmlformats-officedocument.presentationml.template"] = + R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.template"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.template"] = + R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.visio"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/vnd.wordperfect"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/x-7z-compressed"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["application/x-bzip2"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["application/x-cbr"] = R.drawable.ic_mimetype_text + drawableMap["application/x-compressed"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["application/x-dcraw"] = R.drawable.ic_mimetype_image + drawableMap["application/x-deb"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["application/x-fictionbook+xml"] = R.drawable.ic_mimetype_text + drawableMap["application/x-font"] = R.drawable.ic_mimetype_image + drawableMap["application/x-gimp"] = R.drawable.ic_mimetype_image + drawableMap["application/x-gzip"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["application/x-iwork-keynote-sffkey"] = R.drawable.ic_mimetype_x_office_presentation + drawableMap["application/x-iwork-numbers-sffnumbers"] = + R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["application/x-iwork-pages-sffpages"] = R.drawable.ic_mimetype_x_office_document + drawableMap["application/x-mobipocket-ebook"] = R.drawable.ic_mimetype_text + drawableMap["application/x-perl"] = R.drawable.ic_mimetype_text_code + drawableMap["application/x-photoshop"] = R.drawable.ic_mimetype_image + drawableMap["application/x-php"] = R.drawable.ic_mimetype_text_code + drawableMap["application/x-rar-compressed"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["application/x-tar"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["application/x-tex"] = R.drawable.ic_mimetype_text + drawableMap["application/xml"] = R.drawable.ic_mimetype_text_code + drawableMap["application/yaml"] = R.drawable.ic_mimetype_text_code + drawableMap["application/zip"] = R.drawable.ic_mimetype_package_x_generic + drawableMap["database"] = R.drawable.ic_mimetype_file + drawableMap["httpd/unix-directory"] = R.drawable.ic_mimetype_folder + drawableMap["text/css"] = R.drawable.ic_mimetype_text_code + drawableMap["text/csv"] = R.drawable.ic_mimetype_x_office_spreadsheet + drawableMap["text/html"] = R.drawable.ic_mimetype_text_code + drawableMap["text/x-c"] = R.drawable.ic_mimetype_text_code + drawableMap["text/x-c++src"] = R.drawable.ic_mimetype_text_code + drawableMap["text/x-h"] = R.drawable.ic_mimetype_text_code + drawableMap["text/x-java-source"] = R.drawable.ic_mimetype_text_code + drawableMap["text/x-ldif"] = R.drawable.ic_mimetype_text_code + drawableMap["text/x-python"] = R.drawable.ic_mimetype_text_code + drawableMap["text/x-shellscript"] = R.drawable.ic_mimetype_text_code + drawableMap["web"] = R.drawable.ic_mimetype_text_code + drawableMap["application/internet-shortcut"] = R.drawable.ic_mimetype_link - // Initial list of mimetypes was acquired from https://github.com/nextcloud/server/blob/694ba5435b2963e201f6a6d2c775836bde07aaef/core/js/mimetypelist.js - drawableMap["application/coreldraw"] = R.drawable.ic_mimetype_image - drawableMap["application/epub+zip"] = R.drawable.ic_mimetype_text - drawableMap["application/font-sfnt"] = R.drawable.ic_mimetype_image - drawableMap["application/font-woff"] = R.drawable.ic_mimetype_image - drawableMap["application/gpx+xml"] = R.drawable.ic_mimetype_location - drawableMap["application/illustrator"] = R.drawable.ic_mimetype_image - drawableMap["application/javascript"] = R.drawable.ic_mimetype_text_code - drawableMap["application/json"] = R.drawable.ic_mimetype_text_code - drawableMap["application/msaccess"] = R.drawable.ic_mimetype_file - drawableMap["application/msexcel"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/msonenote"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/mspowerpoint"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/msword"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/octet-stream"] = R.drawable.ic_mimetype_file - drawableMap["application/postscript"] = R.drawable.ic_mimetype_image - drawableMap["application/rss+xml"] = R.drawable.ic_mimetype_text_code - drawableMap["application/vnd.android.package-archive"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/vnd.lotus-wordpro"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.garmin.tcx+xml"] = R.drawable.ic_mimetype_location - drawableMap["application/vnd.google-earth.kml+xml"] = R.drawable.ic_mimetype_location - drawableMap["application/vnd.google-earth.kmz"] = R.drawable.ic_mimetype_location - drawableMap["application/vnd.ms-excel"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.ms-excel.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.ms-excel.sheet.binary.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.ms-excel.sheet.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.ms-excel.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.ms-fontobject"] = R.drawable.ic_mimetype_image - drawableMap["application/vnd.ms-powerpoint"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.ms-powerpoint.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.ms-powerpoint.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.ms-visio.drawing.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.ms-visio.drawing"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.ms-visio.stencil.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.ms-visio.stencil"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.ms-visio.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.ms-visio.template"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.ms-word.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.oasis.opendocument.presentation"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.oasis.opendocument.presentation-template"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.oasis.opendocument.spreadsheet"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.oasis.opendocument.spreadsheet-template"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.oasis.opendocument.text"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.oasis.opendocument.text-master"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.oasis.opendocument.text-template"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.oasis.opendocument.text-web"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.openxmlformats-officedocument.presentationml.presentation"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.openxmlformats-officedocument.presentationml.slideshow"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.openxmlformats-officedocument.presentationml.template"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.template"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.template"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.visio"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/vnd.wordperfect"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/x-7z-compressed"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/x-bzip2"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/x-cbr"] = R.drawable.ic_mimetype_text - drawableMap["application/x-compressed"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/x-dcraw"] = R.drawable.ic_mimetype_image - drawableMap["application/x-deb"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/x-fictionbook+xml"] = R.drawable.ic_mimetype_text - drawableMap["application/x-font"] = R.drawable.ic_mimetype_image - drawableMap["application/x-gimp"] = R.drawable.ic_mimetype_image - drawableMap["application/x-gzip"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/x-iwork-keynote-sffkey"] = R.drawable.ic_mimetype_x_office_presentation - drawableMap["application/x-iwork-numbers-sffnumbers"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["application/x-iwork-pages-sffpages"] = R.drawable.ic_mimetype_x_office_document - drawableMap["application/x-mobipocket-ebook"] = R.drawable.ic_mimetype_text - drawableMap["application/x-perl"] = R.drawable.ic_mimetype_text_code - drawableMap["application/x-photoshop"] = R.drawable.ic_mimetype_image - drawableMap["application/x-php"] = R.drawable.ic_mimetype_text_code - drawableMap["application/x-rar-compressed"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/x-tar"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["application/x-tex"] = R.drawable.ic_mimetype_text - drawableMap["application/xml"] = R.drawable.ic_mimetype_text_code - drawableMap["application/yaml"] = R.drawable.ic_mimetype_text_code - drawableMap["application/zip"] = R.drawable.ic_mimetype_package_x_generic - drawableMap["database"] = R.drawable.ic_mimetype_file - drawableMap["httpd/unix-directory"] = R.drawable.ic_mimetype_folder - drawableMap["text/css"] = R.drawable.ic_mimetype_text_code - drawableMap["text/csv"] = R.drawable.ic_mimetype_x_office_spreadsheet - drawableMap["text/html"] = R.drawable.ic_mimetype_text_code - drawableMap["text/x-c"] = R.drawable.ic_mimetype_text_code - drawableMap["text/x-c++src"] = R.drawable.ic_mimetype_text_code - drawableMap["text/x-h"] = R.drawable.ic_mimetype_text_code - drawableMap["text/x-java-source"] = R.drawable.ic_mimetype_text_code - drawableMap["text/x-ldif"] = R.drawable.ic_mimetype_text_code - drawableMap["text/x-python"] = R.drawable.ic_mimetype_text_code - drawableMap["text/x-shellscript"] = R.drawable.ic_mimetype_text_code - drawableMap["web"] = R.drawable.ic_mimetype_text_code - drawableMap["application/internet-shortcut"] = R.drawable.ic_mimetype_link + drawableMap["inode/directory"] = R.drawable.ic_mimetype_folder + drawableMap["unknown"] = R.drawable.ic_mimetype_file + drawableMap["application/pdf"] = R.drawable.ic_mimetype_application_pdf - drawableMap["inode/directory"] = R.drawable.ic_mimetype_folder - drawableMap["unknown"] = R.drawable.ic_mimetype_file - drawableMap["application/pdf"] = R.drawable.ic_mimetype_application_pdf - - if ("DIR" == localMimetype) { - localMimetype = "inode/directory" - return drawableMap[localMimetype]!! - } - - if (drawableMap.containsKey(localMimetype)) { - return drawableMap[localMimetype]!! - } - - if (localMimetype.startsWith("image/")) { - return R.drawable.ic_mimetype_image - } - - if (localMimetype.startsWith("video/")) { - return R.drawable.ic_mimetype_video - } - - if (localMimetype.startsWith("text/")) { - return R.drawable.ic_mimetype_text - } - - return if (localMimetype.startsWith("audio")) { - R.drawable.ic_mimetype_audio - } else drawableMap["unknown"]!! + if ("DIR" == localMimetype) { + localMimetype = "inode/directory" + return drawableMap[localMimetype]!! } + + if (drawableMap.containsKey(localMimetype)) { + return drawableMap[localMimetype]!! + } + + if (localMimetype.startsWith("image/")) { + return R.drawable.ic_mimetype_image + } + + if (localMimetype.startsWith("video/")) { + return R.drawable.ic_mimetype_video + } + + if (localMimetype.startsWith("text/")) { + return R.drawable.ic_mimetype_text + } + + return if (localMimetype.startsWith("audio")) { + R.drawable.ic_mimetype_audio + } else drawableMap["unknown"]!! + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/EmojiTextInputEditText.java b/app/src/main/java/com/nextcloud/talk/utils/EmojiTextInputEditText.java index a1bfd0998..a3e4a3d7f 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/EmojiTextInputEditText.java +++ b/app/src/main/java/com/nextcloud/talk/utils/EmojiTextInputEditText.java @@ -28,43 +28,43 @@ import androidx.emoji.widget.EmojiEditTextHelper; import com.google.android.material.textfield.TextInputEditText; public class EmojiTextInputEditText extends TextInputEditText { - private EmojiEditTextHelper emojiEditTextHelper; + private EmojiEditTextHelper emojiEditTextHelper; - public EmojiTextInputEditText(Context context) { - super(context); - init(); + public EmojiTextInputEditText(Context context) { + super(context); + init(); + } + + public EmojiTextInputEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public EmojiTextInputEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + super.setKeyListener(getEmojiEditTextHelper().getKeyListener(getKeyListener())); + } + + @Override + public void setKeyListener(android.text.method.KeyListener keyListener) { + super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener)); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + InputConnection inputConnection = super.onCreateInputConnection(outAttrs); + return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs); + } + + private EmojiEditTextHelper getEmojiEditTextHelper() { + if (emojiEditTextHelper == null) { + emojiEditTextHelper = new EmojiEditTextHelper(this); } - public EmojiTextInputEditText(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public EmojiTextInputEditText(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - private void init() { - super.setKeyListener(getEmojiEditTextHelper().getKeyListener(getKeyListener())); - } - - @Override - public void setKeyListener(android.text.method.KeyListener keyListener) { - super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener)); - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - InputConnection inputConnection = super.onCreateInputConnection(outAttrs); - return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs); - } - - private EmojiEditTextHelper getEmojiEditTextHelper() { - if (emojiEditTextHelper == null) { - emojiEditTextHelper = new EmojiEditTextHelper(this); - } - - return emojiEditTextHelper; - } + return emojiEditTextHelper; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FABAwareScrollingViewBehavior.java b/app/src/main/java/com/nextcloud/talk/utils/FABAwareScrollingViewBehavior.java index d756778f7..e05265f86 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FABAwareScrollingViewBehavior.java +++ b/app/src/main/java/com/nextcloud/talk/utils/FABAwareScrollingViewBehavior.java @@ -26,45 +26,47 @@ import java.util.List; public class FABAwareScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior { - public FABAwareScrollingViewBehavior(Context context, AttributeSet attrs) { - super(context, attrs); - } + public FABAwareScrollingViewBehavior(Context context, AttributeSet attrs) { + super(context, attrs); + } - @Override - public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { - return super.layoutDependsOn(parent, child, dependency) || - dependency instanceof FloatingActionButton; - } + @Override + public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { + return super.layoutDependsOn(parent, child, dependency) || + dependency instanceof FloatingActionButton; + } - @Override - public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final View child, - final View directTargetChild, final View target, final int nestedScrollAxes) { - // Ensure we react to vertical scrolling - return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL - || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); - } + @Override + public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final View child, + final View directTargetChild, final View target, final int nestedScrollAxes) { + // Ensure we react to vertical scrolling + return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL + || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, + nestedScrollAxes); + } - @Override - public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final View child, - final View target, final int dxConsumed, final int dyConsumed, - final int dxUnconsumed, final int dyUnconsumed) { - super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); - if (dyConsumed > 0) { - // User scrolled down -> hide the FAB - List dependencies = coordinatorLayout.getDependencies(child); - for (View view : dependencies) { - if (view instanceof FloatingActionButton) { - ((FloatingActionButton) view).hide(); - } - } - } else if (dyConsumed < 0) { - // User scrolled up -> show the FAB - List dependencies = coordinatorLayout.getDependencies(child); - for (View view : dependencies) { - if (view instanceof FloatingActionButton) { - ((FloatingActionButton) view).show(); - } - } + @Override + public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final View child, + final View target, final int dxConsumed, final int dyConsumed, + final int dxUnconsumed, final int dyUnconsumed) { + super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, + dyUnconsumed); + if (dyConsumed > 0) { + // User scrolled down -> hide the FAB + List dependencies = coordinatorLayout.getDependencies(child); + for (View view : dependencies) { + if (view instanceof FloatingActionButton) { + ((FloatingActionButton) view).hide(); } + } + } else if (dyConsumed < 0) { + // User scrolled up -> show the FAB + List dependencies = coordinatorLayout.getDependencies(child); + for (View view : dependencies) { + if (view instanceof FloatingActionButton) { + ((FloatingActionButton) view).show(); + } + } } + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/utils/KeyboardUtils.java b/app/src/main/java/com/nextcloud/talk/utils/KeyboardUtils.java index 5ab0b0138..e8d7b0451 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/KeyboardUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/KeyboardUtils.java @@ -29,64 +29,64 @@ import android.view.inputmethod.InputMethodManager; * Basic idea for this solution found here: http://stackoverflow.com/a/9108219/325479 */ public class KeyboardUtils { - private View decorView; - private View contentView; - private boolean isUsedInBottomSheet; - //a small helper to allow showing the editText focus - ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { + private View decorView; + private View contentView; + private boolean isUsedInBottomSheet; + //a small helper to allow showing the editText focus + ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = + new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { - Rect r = new Rect(); - //r will be populated with the coordinates of your view that area still visible. - decorView.getWindowVisibleDisplayFrame(r); + Rect r = new Rect(); + //r will be populated with the coordinates of your view that area still visible. + decorView.getWindowVisibleDisplayFrame(r); - //get screen height and calculate the difference with the useable area from the r - int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels; + //get screen height and calculate the difference with the useable area from the r + int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels; - int diff = height - r.bottom; + int diff = height - r.bottom; - boolean shouldSetBottomPadding = (isUsedInBottomSheet && diff != 0) || (diff > 0); + boolean shouldSetBottomPadding = (isUsedInBottomSheet && diff != 0) || (diff > 0); - if (shouldSetBottomPadding) { - if (contentView.getPaddingBottom() != diff) { - //set the padding of the contentView for the keyboard - contentView.setPadding(0, 0, 0, diff); - } - } else { - //check if the padding is != initialBottomPadding (if yes reset the padding) - if (contentView.getPaddingBottom() != 0) { - //reset the padding of the contentView - contentView.setPadding(0, 0, 0, 0); - } + if (shouldSetBottomPadding) { + if (contentView.getPaddingBottom() != diff) { + //set the padding of the contentView for the keyboard + contentView.setPadding(0, 0, 0, diff); } + } else { + //check if the padding is != initialBottomPadding (if yes reset the padding) + if (contentView.getPaddingBottom() != 0) { + //reset the padding of the contentView + contentView.setPadding(0, 0, 0, 0); + } + } } - }; + }; - public KeyboardUtils(Activity act, View contentView, boolean isUsedInBottomSheet) { - this.decorView = act.getWindow().getDecorView(); - this.contentView = contentView; - this.isUsedInBottomSheet = isUsedInBottomSheet; + public KeyboardUtils(Activity act, View contentView, boolean isUsedInBottomSheet) { + this.decorView = act.getWindow().getDecorView(); + this.contentView = contentView; + this.isUsedInBottomSheet = isUsedInBottomSheet; - decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + } + + /** + * Helper to hide the keyboard + */ + public static void hideKeyboard(Activity act) { + if (act != null && act.getCurrentFocus() != null) { + InputMethodManager inputMethodManager = + (InputMethodManager) act.getSystemService(Activity.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(), 0); } + } - /** - * Helper to hide the keyboard - * - * @param act - */ - public static void hideKeyboard(Activity act) { - if (act != null && act.getCurrentFocus() != null) { - InputMethodManager inputMethodManager = (InputMethodManager) act.getSystemService(Activity.INPUT_METHOD_SERVICE); - inputMethodManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(), 0); - } - } + public void enable() { + decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + } - public void enable() { - decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); - } - - public void disable() { - decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); - } + public void disable() { + decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt index 130885cc9..59093af34 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt @@ -32,43 +32,48 @@ import java.text.SimpleDateFormat import java.util.Date object LoggingUtils { - fun writeLogEntryToFile(context: Context, logEntry: String) { - val dateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss") - val date = Date() - val logEntryWithDateTime = dateFormat.format(date) + ": " + logEntry + "\n" - - try { - val outputStream = context.openFileOutput("nc_log.txt", - Context.MODE_PRIVATE or Context.MODE_APPEND) - outputStream.write(logEntryWithDateTime.toByteArray()) - outputStream.flush() - outputStream.close() - } catch (e: FileNotFoundException) { - e.printStackTrace() - } catch (e: IOException) { - e.printStackTrace() - } + fun writeLogEntryToFile( + context: Context, + logEntry: String + ) { + val dateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss") + val date = Date() + val logEntryWithDateTime = dateFormat.format(date) + ": " + logEntry + "\n" + try { + val outputStream = context.openFileOutput( + "nc_log.txt", + Context.MODE_PRIVATE or Context.MODE_APPEND + ) + outputStream.write(logEntryWithDateTime.toByteArray()) + outputStream.flush() + outputStream.close() + } catch (e: FileNotFoundException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() } - fun sendMailWithAttachment(context: Context) { - val logFile = context.getFileStreamPath("nc_log.txt") - val emailIntent = Intent(Intent.ACTION_SEND) - val mailto = "mario@nextcloud.com" - emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(mailto)) - emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Talk logs") - emailIntent.type = "text/plain" - emailIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION - val uri: Uri + } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - uri = Uri.fromFile(logFile) - } else { - uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, logFile) - } + fun sendMailWithAttachment(context: Context) { + val logFile = context.getFileStreamPath("nc_log.txt") + val emailIntent = Intent(Intent.ACTION_SEND) + val mailto = "mario@nextcloud.com" + emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(mailto)) + emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Talk logs") + emailIntent.type = "text/plain" + emailIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + val uri: Uri - emailIntent.putExtra(Intent.EXTRA_STREAM, uri) - emailIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - context.startActivity(emailIntent) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + uri = Uri.fromFile(logFile) + } else { + uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, logFile) } + + emailIntent.putExtra(Intent.EXTRA_STREAM, uri) + emailIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(emailIntent) + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/MagicCharPolicy.java b/app/src/main/java/com/nextcloud/talk/utils/MagicCharPolicy.java index f1b8c6eab..33203ef24 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/MagicCharPolicy.java +++ b/app/src/main/java/com/nextcloud/talk/utils/MagicCharPolicy.java @@ -29,80 +29,80 @@ import java.util.regex.Pattern; public class MagicCharPolicy implements AutocompletePolicy { - private final char character; + private final char character; - public MagicCharPolicy(char character) { - this.character = character; + public MagicCharPolicy(char character) { + this.character = character; + } + + @Nullable + public static int[] getQueryRange(Spannable text) { + QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class); + if (span == null || span.length == 0) return null; + if (span.length > 1) { + // Do absolutely nothing + } + QuerySpan sp = span[0]; + return new int[] { text.getSpanStart(sp), text.getSpanEnd(sp) }; + } + + private int[] checkText(Spannable text, int cursorPos) { + if (text.length() == 0) { + return null; } - @Nullable - public static int[] getQueryRange(Spannable text) { - QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class); - if (span == null || span.length == 0) return null; - if (span.length > 1) { - // Do absolutely nothing + int[] span = new int[2]; + Pattern pattern = Pattern.compile("@+\\S*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + Matcher matcher = pattern.matcher(text); + + while (matcher.find()) { + if (cursorPos >= matcher.start() && cursorPos <= matcher.end()) { + span[0] = matcher.start(); + span[1] = matcher.end(); + if (text.subSequence(matcher.start(), matcher.end()).charAt(0) == character) { + return span; } - QuerySpan sp = span[0]; - return new int[]{text.getSpanStart(sp), text.getSpanEnd(sp)}; + } } - private int[] checkText(Spannable text, int cursorPos) { - if (text.length() == 0) { - return null; - } + return null; + } - int[] span = new int[2]; - Pattern pattern = Pattern.compile("@+\\S*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - Matcher matcher = pattern.matcher(text); - - while (matcher.find()) { - if (cursorPos >= matcher.start() && cursorPos <= matcher.end()) { - span[0] = matcher.start(); - span[1] = matcher.end(); - if (text.subSequence(matcher.start(), matcher.end()).charAt(0) == character) { - return span; - } - } - } - - return null; + @Override + public boolean shouldShowPopup(Spannable text, int cursorPos) { + int[] show = checkText(text, cursorPos); + if (show != null) { + text.setSpan(new QuerySpan(), show[0], show[1], Spanned.SPAN_INCLUSIVE_INCLUSIVE); + return true; } + return false; + } - @Override - public boolean shouldShowPopup(Spannable text, int cursorPos) { - int[] show = checkText(text, cursorPos); - if (show != null) { - text.setSpan(new QuerySpan(), show[0], show[1], Spanned.SPAN_INCLUSIVE_INCLUSIVE); - return true; - } - return false; - } + @Override + public boolean shouldDismissPopup(Spannable text, int cursorPos) { + return checkText(text, cursorPos) == null; + } - @Override - public boolean shouldDismissPopup(Spannable text, int cursorPos) { - return checkText(text, cursorPos) == null; + @Override + public CharSequence getQuery(Spannable text) { + QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class); + if (span == null || span.length == 0) { + // Should never happen. + return ""; } + QuerySpan sp = span[0]; + return text.subSequence(text.getSpanStart(sp), text.getSpanEnd(sp)); + } - @Override - public CharSequence getQuery(Spannable text) { - QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class); - if (span == null || span.length == 0) { - // Should never happen. - return ""; - } - QuerySpan sp = span[0]; - return text.subSequence(text.getSpanStart(sp), text.getSpanEnd(sp)); + @Override + public void onDismiss(Spannable text) { + // Remove any span added by shouldShow. Should be useless, but anyway. + QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class); + for (QuerySpan s : span) { + text.removeSpan(s); } + } - @Override - public void onDismiss(Spannable text) { - // Remove any span added by shouldShow. Should be useless, but anyway. - QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class); - for (QuerySpan s : span) { - text.removeSpan(s); - } - } - - private static class QuerySpan { - } + private static class QuerySpan { + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/utils/MagicMap.kt b/app/src/main/java/com/nextcloud/talk/utils/MagicMap.kt index b0ae58039..826bfed45 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/MagicMap.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/MagicMap.kt @@ -23,9 +23,9 @@ package com.nextcloud.talk.utils import java.util.concurrent.ConcurrentHashMap class MagicMap : ConcurrentHashMap() { - fun add(element: Any): Int { - val key = System.identityHashCode(element) - super.put(key, element) - return key - } + fun add(element: Any): Int { + val key = System.identityHashCode(element) + super.put(key, element) + return key + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt index 876919ee8..77309b3d1 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt @@ -33,130 +33,176 @@ import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.utils.bundle.BundleKeys object NotificationUtils { - val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS" - val NOTIFICATION_CHANNEL_MESSAGES = "NOTIFICATION_CHANNEL_MESSAGES" - val NOTIFICATION_CHANNEL_CALLS_V2 = "NOTIFICATION_CHANNEL_CALLS_V2" - val NOTIFICATION_CHANNEL_MESSAGES_V2 = "NOTIFICATION_CHANNEL_MESSAGES_V2" - val NOTIFICATION_CHANNEL_MESSAGES_V3 = "NOTIFICATION_CHANNEL_MESSAGES_V3" - val NOTIFICATION_CHANNEL_CALLS_V3 = "NOTIFICATION_CHANNEL_CALLS_V3" + val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS" + val NOTIFICATION_CHANNEL_MESSAGES = "NOTIFICATION_CHANNEL_MESSAGES" + val NOTIFICATION_CHANNEL_CALLS_V2 = "NOTIFICATION_CHANNEL_CALLS_V2" + val NOTIFICATION_CHANNEL_MESSAGES_V2 = "NOTIFICATION_CHANNEL_MESSAGES_V2" + val NOTIFICATION_CHANNEL_MESSAGES_V3 = "NOTIFICATION_CHANNEL_MESSAGES_V3" + val NOTIFICATION_CHANNEL_CALLS_V3 = "NOTIFICATION_CHANNEL_CALLS_V3" - @TargetApi(Build.VERSION_CODES.O) - fun createNotificationChannel(context: Context, - channelId: String, channelName: String, - channelDescription: String, enableLights: Boolean, - importance: Int) { + @TargetApi(Build.VERSION_CODES.O) + fun createNotificationChannel( + context: Context, + channelId: String, + channelName: String, + channelDescription: String, + enableLights: Boolean, + importance: Int + ) { - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O && notificationManager.getNotificationChannel(channelId) == null) { + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O && notificationManager.getNotificationChannel( + channelId + ) == null + ) { - val channel = NotificationChannel(channelId, channelName, - importance) + val channel = NotificationChannel( + channelId, channelName, + importance + ) - channel.description = channelDescription - channel.enableLights(enableLights) - channel.lightColor = R.color.colorPrimary - channel.setSound(null, null) + channel.description = channelDescription + channel.enableLights(enableLights) + channel.lightColor = R.color.colorPrimary + channel.setSound(null, null) - notificationManager.createNotificationChannel(channel) + notificationManager.createNotificationChannel(channel) + } + } + + @TargetApi(Build.VERSION_CODES.O) + fun createNotificationChannelGroup( + context: Context, + groupId: String, + groupName: CharSequence + ) { + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + val notificationChannelGroup = NotificationChannelGroup(groupId, groupName) + if (!notificationManager.notificationChannelGroups.contains(notificationChannelGroup)) { + notificationManager.createNotificationChannelGroup(notificationChannelGroup) + } + } + } + + fun cancelAllNotificationsForAccount( + context: Context?, + conversationUser: UserEntity + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && context != null) { + + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + val statusBarNotifications = notificationManager.activeNotifications + var notification: Notification? + for (statusBarNotification in statusBarNotifications) { + notification = statusBarNotification.notification + + if (notification != null && !notification.extras.isEmpty) { + if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID)) { + notificationManager.cancel(statusBarNotification.id) + } } + } } - @TargetApi(Build.VERSION_CODES.O) - fun createNotificationChannelGroup(context: Context, - groupId: String, groupName: CharSequence) { - if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + } - val notificationChannelGroup = NotificationChannelGroup(groupId, groupName) - if (!notificationManager.notificationChannelGroups.contains(notificationChannelGroup)) { - notificationManager.createNotificationChannelGroup(notificationChannelGroup) - } + fun cancelExistingNotificationWithId( + context: Context?, + conversationUser: UserEntity, + notificationId: Long + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && + context != null + ) { + + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + val statusBarNotifications = notificationManager.activeNotifications + var notification: Notification? + for (statusBarNotification in statusBarNotifications) { + notification = statusBarNotification.notification + + if (notification != null && !notification.extras.isEmpty) { + if (conversationUser.id == notification.extras.getLong( + BundleKeys.KEY_INTERNAL_USER_ID + ) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID) + ) { + notificationManager.cancel(statusBarNotification.id) + } } + } + } + } + + fun findNotificationForRoom( + context: Context?, + conversationUser: UserEntity, + roomTokenOrId: String + ): StatusBarNotification? { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && + context != null + ) { + + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + val statusBarNotifications = notificationManager.activeNotifications + var notification: Notification? + for (statusBarNotification in statusBarNotifications) { + notification = statusBarNotification.notification + + if (notification != null && !notification.extras.isEmpty) { + if (conversationUser.id == notification.extras.getLong( + BundleKeys.KEY_INTERNAL_USER_ID + ) && roomTokenOrId == statusBarNotification.notification.extras.getString( + BundleKeys.KEY_ROOM_TOKEN + ) + ) { + return statusBarNotification + } + } + } } - fun cancelAllNotificationsForAccount(context: Context?, conversationUser: UserEntity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && context != null) { + return null + } - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + fun cancelExistingNotificationsForRoom( + context: Context?, + conversationUser: UserEntity, + roomTokenOrId: String + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && + context != null + ) { - val statusBarNotifications = notificationManager.activeNotifications - var notification: Notification? - for (statusBarNotification in statusBarNotifications) { - notification = statusBarNotification.notification + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - if (notification != null && !notification.extras.isEmpty) { - if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID)) { - notificationManager.cancel(statusBarNotification.id) - } - } - } - } - - } - - fun cancelExistingNotificationWithId(context: Context?, conversationUser: UserEntity, notificationId: Long) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && - context != null) { - - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - val statusBarNotifications = notificationManager.activeNotifications - var notification: Notification? - for (statusBarNotification in statusBarNotifications) { - notification = statusBarNotification.notification - - if (notification != null && !notification.extras.isEmpty) { - if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID)) { - notificationManager.cancel(statusBarNotification.id) - } - } - } - } - } - - fun findNotificationForRoom(context: Context?, - conversationUser: UserEntity, - roomTokenOrId: String): StatusBarNotification? { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && - context != null) { - - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - val statusBarNotifications = notificationManager.activeNotifications - var notification: Notification? - for (statusBarNotification in statusBarNotifications) { - notification = statusBarNotification.notification - - if (notification != null && !notification.extras.isEmpty) { - if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) { - return statusBarNotification - } - } - } - } - - return null - } - - fun cancelExistingNotificationsForRoom(context: Context?, conversationUser: UserEntity, - roomTokenOrId: String) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && - context != null) { - - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - val statusBarNotifications = notificationManager.activeNotifications - var notification: Notification? - for (statusBarNotification in statusBarNotifications) { - notification = statusBarNotification.notification - - if (notification != null && !notification.extras.isEmpty) { - if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) { - notificationManager.cancel(statusBarNotification.id) - } - } - } + val statusBarNotifications = notificationManager.activeNotifications + var notification: Notification? + for (statusBarNotification in statusBarNotifications) { + notification = statusBarNotification.notification + + if (notification != null && !notification.extras.isEmpty) { + if (conversationUser.id == notification.extras.getLong( + BundleKeys.KEY_INTERNAL_USER_ID + ) && roomTokenOrId == statusBarNotification.notification.extras.getString( + BundleKeys.KEY_ROOM_TOKEN + ) + ) { + notificationManager.cancel(statusBarNotification.id) + } } + } } + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/OkHttpNetworkFetcherWithCache.java b/app/src/main/java/com/nextcloud/talk/utils/OkHttpNetworkFetcherWithCache.java index 9f4875f17..c525249c8 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/OkHttpNetworkFetcherWithCache.java +++ b/app/src/main/java/com/nextcloud/talk/utils/OkHttpNetworkFetcherWithCache.java @@ -26,15 +26,16 @@ import okhttp3.Call; import okhttp3.OkHttpClient; public class OkHttpNetworkFetcherWithCache extends OkHttpNetworkFetcher { - public OkHttpNetworkFetcherWithCache(OkHttpClient okHttpClient) { - super(okHttpClient); - } + public OkHttpNetworkFetcherWithCache(OkHttpClient okHttpClient) { + super(okHttpClient); + } - public OkHttpNetworkFetcherWithCache(Call.Factory callFactory, Executor cancellationExecutor) { - super(callFactory, cancellationExecutor); - } + public OkHttpNetworkFetcherWithCache(Call.Factory callFactory, Executor cancellationExecutor) { + super(callFactory, cancellationExecutor); + } - public OkHttpNetworkFetcherWithCache(Call.Factory callFactory, Executor cancellationExecutor, boolean disableOkHttpCache) { - super(callFactory, cancellationExecutor, true); - } + public OkHttpNetworkFetcherWithCache(Call.Factory callFactory, Executor cancellationExecutor, + boolean disableOkHttpCache) { + super(callFactory, cancellationExecutor, true); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java index 652668664..c83d9d687 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java @@ -26,14 +26,14 @@ import android.util.Base64; import android.util.Log; import autodagger.AutoInjector; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.SignatureVerification; -import com.nextcloud.talk.models.json.push.PushConfigurationState; -import com.nextcloud.talk.models.json.push.PushRegistrationOverall; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.EventStatus; +import com.nextcloud.talk.models.SignatureVerification; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.push.PushConfigurationState; +import com.nextcloud.talk.models.json.push.PushRegistrationOverall; import com.nextcloud.talk.utils.database.user.UserUtils; import com.nextcloud.talk.utils.preferences.AppPreferences; import io.reactivex.Observer; @@ -65,377 +65,381 @@ import org.greenrobot.eventbus.EventBus; @AutoInjector(NextcloudTalkApplication.class) public class PushUtils { - private static final String TAG = "PushUtils"; + private static final String TAG = "PushUtils"; - @Inject - UserUtils userUtils; + @Inject + UserUtils userUtils; - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - private File keysFile; - private File publicKeyFile; - private File privateKeyFile; + private File keysFile; + private File publicKeyFile; + private File privateKeyFile; - private String proxyServer; + private String proxyServer; - public PushUtils() { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public PushUtils() { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - keysFile = NextcloudTalkApplication.Companion.getSharedApplication().getDir("PushKeyStore", Context.MODE_PRIVATE); + keysFile = NextcloudTalkApplication.Companion.getSharedApplication() + .getDir("PushKeyStore", Context.MODE_PRIVATE); - publicKeyFile = new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("PushKeystore", - Context.MODE_PRIVATE), "push_key.pub"); - privateKeyFile = new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("PushKeystore", - Context.MODE_PRIVATE), "push_key.priv"); - proxyServer = NextcloudTalkApplication.Companion.getSharedApplication().getResources(). - getString(R.string.nc_push_server_url); - } + publicKeyFile = + new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("PushKeystore", + Context.MODE_PRIVATE), "push_key.pub"); + privateKeyFile = + new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("PushKeystore", + Context.MODE_PRIVATE), "push_key.priv"); + proxyServer = NextcloudTalkApplication.Companion.getSharedApplication().getResources(). + getString(R.string.nc_push_server_url); + } - public SignatureVerification verifySignature(byte[] signatureBytes, byte[] subjectBytes) { - Signature signature = null; - PushConfigurationState pushConfigurationState; - PublicKey publicKey; - SignatureVerification signatureVerification = new SignatureVerification(); - signatureVerification.setSignatureValid(false); + public SignatureVerification verifySignature(byte[] signatureBytes, byte[] subjectBytes) { + Signature signature = null; + PushConfigurationState pushConfigurationState; + PublicKey publicKey; + SignatureVerification signatureVerification = new SignatureVerification(); + signatureVerification.setSignatureValid(false); - List userEntities = userUtils.getUsers(); - try { - signature = Signature.getInstance("SHA512withRSA"); - if (userEntities != null && userEntities.size() > 0) { - for (UserEntity userEntity : userEntities) { - if (!TextUtils.isEmpty(userEntity.getPushConfigurationState())) { - pushConfigurationState = LoganSquare.parse(userEntity.getPushConfigurationState(), - PushConfigurationState.class); - publicKey = (PublicKey) readKeyFromString(true, - pushConfigurationState.getUserPublicKey()); - signature.initVerify(publicKey); - signature.update(subjectBytes); - if (signature.verify(signatureBytes)) { - signatureVerification.setSignatureValid(true); - signatureVerification.setUserEntity(userEntity); - return signatureVerification; - } - } - } + List userEntities = userUtils.getUsers(); + try { + signature = Signature.getInstance("SHA512withRSA"); + if (userEntities != null && userEntities.size() > 0) { + for (UserEntity userEntity : userEntities) { + if (!TextUtils.isEmpty(userEntity.getPushConfigurationState())) { + pushConfigurationState = LoganSquare.parse(userEntity.getPushConfigurationState(), + PushConfigurationState.class); + publicKey = (PublicKey) readKeyFromString(true, + pushConfigurationState.getUserPublicKey()); + signature.initVerify(publicKey); + signature.update(subjectBytes); + if (signature.verify(signatureBytes)) { + signatureVerification.setSignatureValid(true); + signatureVerification.setUserEntity(userEntity); + return signatureVerification; } - } catch (NoSuchAlgorithmException e) { - Log.d(TAG, "No such algorithm"); - } catch (IOException e) { - Log.d(TAG, "Error while trying to parse push configuration viewState"); - } catch (InvalidKeyException e) { - Log.d(TAG, "Invalid key while trying to verify"); - } catch (SignatureException e) { - Log.d(TAG, "Signature exception while trying to verify"); + } } - - return signatureVerification; + } + } catch (NoSuchAlgorithmException e) { + Log.d(TAG, "No such algorithm"); + } catch (IOException e) { + Log.d(TAG, "Error while trying to parse push configuration viewState"); + } catch (InvalidKeyException e) { + Log.d(TAG, "Invalid key while trying to verify"); + } catch (SignatureException e) { + Log.d(TAG, "Signature exception while trying to verify"); } - private int saveKeyToFile(Key key, String path) { - byte[] encoded = key.getEncoded(); + return signatureVerification; + } - try { - if (!new File(path).exists()) { - if (!new File(path).createNewFile()) { - return -1; - } - } + private int saveKeyToFile(Key key, String path) { + byte[] encoded = key.getEncoded(); - try (FileOutputStream keyFileOutputStream = new FileOutputStream(path)) { - keyFileOutputStream.write(encoded); - return 0; - } - } catch (FileNotFoundException e) { - Log.d(TAG, "Failed to save key to file"); - } catch (IOException e) { - Log.d(TAG, "Failed to save key to file via IOException"); + try { + if (!new File(path).exists()) { + if (!new File(path).createNewFile()) { + return -1; } + } - return -1; + try (FileOutputStream keyFileOutputStream = new FileOutputStream(path)) { + keyFileOutputStream.write(encoded); + return 0; + } + } catch (FileNotFoundException e) { + Log.d(TAG, "Failed to save key to file"); + } catch (IOException e) { + Log.d(TAG, "Failed to save key to file via IOException"); } - private String generateSHA512Hash(String pushToken) { - MessageDigest messageDigest = null; - try { - messageDigest = MessageDigest.getInstance("SHA-512"); - messageDigest.update(pushToken.getBytes()); - return bytesToHex(messageDigest.digest()); - } catch (NoSuchAlgorithmException e) { - Log.d(TAG, "SHA-512 algorithm not supported"); - } - return ""; + return -1; + } + + private String generateSHA512Hash(String pushToken) { + MessageDigest messageDigest = null; + try { + messageDigest = MessageDigest.getInstance("SHA-512"); + messageDigest.update(pushToken.getBytes()); + return bytesToHex(messageDigest.digest()); + } catch (NoSuchAlgorithmException e) { + Log.d(TAG, "SHA-512 algorithm not supported"); } + return ""; + } - private String bytesToHex(byte[] bytes) { - StringBuilder result = new StringBuilder(); - for (byte individualByte : bytes) { - result.append(Integer.toString((individualByte & 0xff) + 0x100, 16) - .substring(1)); - } - return result.toString(); + private String bytesToHex(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte individualByte : bytes) { + result.append(Integer.toString((individualByte & 0xff) + 0x100, 16) + .substring(1)); } + return result.toString(); + } - public int generateRsa2048KeyPair() { - if (!publicKeyFile.exists() && !privateKeyFile.exists()) { - if (!keysFile.exists()) { - keysFile.mkdirs(); - } + public int generateRsa2048KeyPair() { + if (!publicKeyFile.exists() && !privateKeyFile.exists()) { + if (!keysFile.exists()) { + keysFile.mkdirs(); + } - KeyPairGenerator keyGen = null; - try { - keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); + KeyPairGenerator keyGen = null; + try { + keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); - KeyPair pair = keyGen.generateKeyPair(); - int statusPrivate = saveKeyToFile(pair.getPrivate(), privateKeyFile.getAbsolutePath()); - int statusPublic = saveKeyToFile(pair.getPublic(), publicKeyFile.getAbsolutePath()); + KeyPair pair = keyGen.generateKeyPair(); + int statusPrivate = saveKeyToFile(pair.getPrivate(), privateKeyFile.getAbsolutePath()); + int statusPublic = saveKeyToFile(pair.getPublic(), publicKeyFile.getAbsolutePath()); - if (statusPrivate == 0 && statusPublic == 0) { - // all went well - return 0; - } else { - return -2; - } - - } catch (NoSuchAlgorithmException e) { - Log.d(TAG, "RSA algorithm not supported"); - } + if (statusPrivate == 0 && statusPublic == 0) { + // all went well + return 0; } else { - // We already have the key - return -1; + return -2; } - - // we failed to generate the key - return -2; + } catch (NoSuchAlgorithmException e) { + Log.d(TAG, "RSA algorithm not supported"); + } + } else { + // We already have the key + return -1; } - public void pushRegistrationToServer() { - String token = appPreferences.getPushToken(); + // we failed to generate the key + return -2; + } - if (!TextUtils.isEmpty(token)) { - String credentials; - String pushTokenHash = generateSHA512Hash(token).toLowerCase(); - PublicKey devicePublicKey = (PublicKey) readKeyFromFile(true); - if (devicePublicKey != null) { - byte[] publicKeyBytes = Base64.encode(devicePublicKey.getEncoded(), Base64.NO_WRAP); - String publicKey = new String(publicKeyBytes); - publicKey = publicKey.replaceAll("(.{64})", "$1\n"); + public void pushRegistrationToServer() { + String token = appPreferences.getPushToken(); - publicKey = "-----BEGIN PUBLIC KEY-----\n" + publicKey + "\n-----END PUBLIC KEY-----\n"; + if (!TextUtils.isEmpty(token)) { + String credentials; + String pushTokenHash = generateSHA512Hash(token).toLowerCase(); + PublicKey devicePublicKey = (PublicKey) readKeyFromFile(true); + if (devicePublicKey != null) { + byte[] publicKeyBytes = Base64.encode(devicePublicKey.getEncoded(), Base64.NO_WRAP); + String publicKey = new String(publicKeyBytes); + publicKey = publicKey.replaceAll("(.{64})", "$1\n"); + + publicKey = "-----BEGIN PUBLIC KEY-----\n" + publicKey + "\n-----END PUBLIC KEY-----\n"; + + if (userUtils.anyUserExists()) { + String providerValue; + PushConfigurationState accountPushData = null; + for (Object userEntityObject : userUtils.getUsers()) { + UserEntity userEntity = (UserEntity) userEntityObject; + providerValue = userEntity.getPushConfigurationState(); + if (!TextUtils.isEmpty(providerValue)) { + try { + accountPushData = LoganSquare.parse(providerValue, PushConfigurationState.class); + } catch (IOException e) { + Log.d(TAG, "Failed to parse account push data"); + accountPushData = null; + } + } else { + accountPushData = null; + } + + if (((TextUtils.isEmpty(providerValue) || accountPushData == null) + && !userEntity.getScheduledForDeletion()) || + (accountPushData != null + && !accountPushData.getPushToken().equals(token) + && !userEntity.getScheduledForDeletion())) { + + Map queryMap = new HashMap<>(); + queryMap.put("format", "json"); + queryMap.put("pushTokenHash", pushTokenHash); + queryMap.put("devicePublicKey", publicKey); + queryMap.put("proxyServer", proxyServer); + + credentials = + ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()); + + String finalCredentials = credentials; + ncApi.registerDeviceForNotificationsWithNextcloud( + credentials, + ApiUtils.getUrlNextcloudPush(userEntity.getBaseUrl()), queryMap) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(PushRegistrationOverall pushRegistrationOverall) { + Map proxyMap = new HashMap<>(); + proxyMap.put("pushToken", token); + proxyMap.put("deviceIdentifier", pushRegistrationOverall.getOcs().getData(). + getDeviceIdentifier()); + proxyMap.put("deviceIdentifierSignature", pushRegistrationOverall.getOcs() + .getData().getSignature()); + proxyMap.put("userPublicKey", pushRegistrationOverall.getOcs() + .getData().getPublicKey()); + + ncApi.registerDeviceForNotificationsWithProxy( + ApiUtils.getUrlPushProxy(), proxyMap) + .subscribeOn(Schedulers.io()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - if (userUtils.anyUserExists()) { - String providerValue; - PushConfigurationState accountPushData = null; - for (Object userEntityObject : userUtils.getUsers()) { - UserEntity userEntity = (UserEntity) userEntityObject; - providerValue = userEntity.getPushConfigurationState(); - if (!TextUtils.isEmpty(providerValue)) { - try { - accountPushData = LoganSquare.parse(providerValue, PushConfigurationState.class); - } catch (IOException e) { - Log.d(TAG, "Failed to parse account push data"); - accountPushData = null; } - } else { - accountPushData = null; - } - if (((TextUtils.isEmpty(providerValue) || accountPushData == null) && !userEntity.getScheduledForDeletion()) || - (accountPushData != null && !accountPushData.getPushToken().equals(token) && !userEntity.getScheduledForDeletion())) { + @Override + public void onNext(Void aVoid) { + PushConfigurationState pushConfigurationState = + new PushConfigurationState(); + pushConfigurationState.setPushToken(token); + pushConfigurationState.setDeviceIdentifier( + pushRegistrationOverall.getOcs() + .getData().getDeviceIdentifier()); + pushConfigurationState.setDeviceIdentifierSignature( + pushRegistrationOverall + .getOcs().getData().getSignature()); + pushConfigurationState.setUserPublicKey( + pushRegistrationOverall.getOcs() + .getData().getPublicKey()); + pushConfigurationState.setUsesRegularPass(false); + try { + userUtils.createOrUpdateUser(null, + null, null, + userEntity.getDisplayName(), + LoganSquare.serialize(pushConfigurationState), null, + null, userEntity.getId(), null, null, null) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - Map queryMap = new HashMap<>(); - queryMap.put("format", "json"); - queryMap.put("pushTokenHash", pushTokenHash); - queryMap.put("devicePublicKey", publicKey); - queryMap.put("proxyServer", proxyServer); + } - credentials = ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()); + @Override + public void onNext(UserEntity userEntity) { + eventBus.post(new EventStatus(userEntity.getId(), + EventStatus.EventType.PUSH_REGISTRATION, true)); + } - String finalCredentials = credentials; - ncApi.registerDeviceForNotificationsWithNextcloud( - credentials, - ApiUtils.getUrlNextcloudPush(userEntity.getBaseUrl()), queryMap) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { + @Override + public void onError(Throwable e) { + eventBus.post(new EventStatus + (userEntity.getId(), + EventStatus.EventType + .PUSH_REGISTRATION, false)); + } - } + @Override + public void onComplete() { - @Override - public void onNext(PushRegistrationOverall pushRegistrationOverall) { - Map proxyMap = new HashMap<>(); - proxyMap.put("pushToken", token); - proxyMap.put("deviceIdentifier", pushRegistrationOverall.getOcs().getData(). - getDeviceIdentifier()); - proxyMap.put("deviceIdentifierSignature", pushRegistrationOverall.getOcs() - .getData().getSignature()); - proxyMap.put("userPublicKey", pushRegistrationOverall.getOcs() - .getData().getPublicKey()); - - - ncApi.registerDeviceForNotificationsWithProxy( - ApiUtils.getUrlPushProxy(), proxyMap) - .subscribeOn(Schedulers.io()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(Void aVoid) { - PushConfigurationState pushConfigurationState = - new PushConfigurationState(); - pushConfigurationState.setPushToken(token); - pushConfigurationState.setDeviceIdentifier( - pushRegistrationOverall.getOcs() - .getData().getDeviceIdentifier()); - pushConfigurationState.setDeviceIdentifierSignature( - pushRegistrationOverall - .getOcs().getData().getSignature()); - pushConfigurationState.setUserPublicKey( - pushRegistrationOverall.getOcs() - .getData().getPublicKey()); - pushConfigurationState.setUsesRegularPass(false); - - try { - userUtils.createOrUpdateUser(null, - null, null, - userEntity.getDisplayName(), - LoganSquare.serialize(pushConfigurationState), null, - null, userEntity.getId(), null, null, null) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(UserEntity userEntity) { - eventBus.post(new EventStatus(userEntity.getId(), EventStatus.EventType.PUSH_REGISTRATION, true)); - } - - @Override - public void onError(Throwable e) { - eventBus.post(new EventStatus - (userEntity.getId(), - EventStatus.EventType - .PUSH_REGISTRATION, false)); - } - - @Override - public void onComplete() { - - } - }); - } catch (IOException e) { - Log.e(TAG, "IOException while updating user"); - } - - - } - - @Override - public void onError(Throwable e) { - eventBus.post(new EventStatus(userEntity.getId(), - EventStatus.EventType.PUSH_REGISTRATION, false)); - } - - @Override - public void onComplete() { - - } - }); - } - - @Override - public void onError(Throwable e) { - eventBus.post(new EventStatus(userEntity.getId(), - EventStatus.EventType.PUSH_REGISTRATION, false)); - - } - - @Override - public void onComplete() { - } + } }); - } + } catch (IOException e) { + Log.e(TAG, "IOException while updating user"); + } + } + + @Override + public void onError(Throwable e) { + eventBus.post(new EventStatus(userEntity.getId(), + EventStatus.EventType.PUSH_REGISTRATION, false)); + } + + @Override + public void onComplete() { + + } + }); } - } + + @Override + public void onError(Throwable e) { + eventBus.post(new EventStatus(userEntity.getId(), + EventStatus.EventType.PUSH_REGISTRATION, false)); + } + + @Override + public void onComplete() { + } + }); } + } } + } + } + } + + private Key readKeyFromString(boolean readPublicKey, String keyString) { + if (readPublicKey) { + keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----", + "").replace("-----END PUBLIC KEY-----", ""); + } else { + keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", + "").replace("-----END PRIVATE KEY-----", ""); } - private Key readKeyFromString(boolean readPublicKey, String keyString) { - if (readPublicKey) { - keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----", - "").replace("-----END PUBLIC KEY-----", ""); - } else { - keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", - "").replace("-----END PRIVATE KEY-----", ""); - } - - KeyFactory keyFactory = null; - try { - keyFactory = KeyFactory.getInstance("RSA"); - if (readPublicKey) { - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(keyString, Base64.DEFAULT)); - return keyFactory.generatePublic(keySpec); - } else { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(keyString, Base64.DEFAULT)); - return keyFactory.generatePrivate(keySpec); - } - - } catch (NoSuchAlgorithmException e) { - Log.d("TAG", "No such algorithm while reading key from string"); - } catch (InvalidKeySpecException e) { - Log.d("TAG", "Invalid key spec while reading key from string"); - } - - return null; + KeyFactory keyFactory = null; + try { + keyFactory = KeyFactory.getInstance("RSA"); + if (readPublicKey) { + X509EncodedKeySpec keySpec = + new X509EncodedKeySpec(Base64.decode(keyString, Base64.DEFAULT)); + return keyFactory.generatePublic(keySpec); + } else { + PKCS8EncodedKeySpec keySpec = + new PKCS8EncodedKeySpec(Base64.decode(keyString, Base64.DEFAULT)); + return keyFactory.generatePrivate(keySpec); + } + } catch (NoSuchAlgorithmException e) { + Log.d("TAG", "No such algorithm while reading key from string"); + } catch (InvalidKeySpecException e) { + Log.d("TAG", "Invalid key spec while reading key from string"); } - public Key readKeyFromFile(boolean readPublicKey) { - String path; + return null; + } - if (readPublicKey) { - path = publicKeyFile.getAbsolutePath(); - } else { - path = privateKeyFile.getAbsolutePath(); - } + public Key readKeyFromFile(boolean readPublicKey) { + String path; - try (FileInputStream fileInputStream = new FileInputStream(path)) { - byte[] bytes = new byte[fileInputStream.available()]; - fileInputStream.read(bytes); - - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - - if (readPublicKey) { - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); - return keyFactory.generatePublic(keySpec); - } else { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); - return keyFactory.generatePrivate(keySpec); - } - - } catch (FileNotFoundException e) { - Log.d(TAG, "Failed to find path while reading the Key"); - } catch (IOException e) { - Log.d(TAG, "IOException while reading the key"); - } catch (InvalidKeySpecException e) { - Log.d(TAG, "InvalidKeySpecException while reading the key"); - } catch (NoSuchAlgorithmException e) { - Log.d(TAG, "RSA algorithm not supported"); - } - - return null; + if (readPublicKey) { + path = publicKeyFile.getAbsolutePath(); + } else { + path = privateKeyFile.getAbsolutePath(); } + + try (FileInputStream fileInputStream = new FileInputStream(path)) { + byte[] bytes = new byte[fileInputStream.available()]; + fileInputStream.read(bytes); + + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + + if (readPublicKey) { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); + return keyFactory.generatePublic(keySpec); + } else { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); + return keyFactory.generatePrivate(keySpec); + } + } catch (FileNotFoundException e) { + Log.d(TAG, "Failed to find path while reading the Key"); + } catch (IOException e) { + Log.d(TAG, "IOException while reading the key"); + } catch (InvalidKeySpecException e) { + Log.d(TAG, "InvalidKeySpecException while reading the key"); + } catch (NoSuchAlgorithmException e) { + Log.d(TAG, "RSA algorithm not supported"); + } + + return null; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/SecurityUtils.java b/app/src/main/java/com/nextcloud/talk/utils/SecurityUtils.java index da00c15c3..3472618bb 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/SecurityUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/SecurityUtils.java @@ -50,82 +50,87 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; public class SecurityUtils { - private static final String TAG = "SecurityUtils"; - private static final String CREDENTIALS_KEY = "KEY_CREDENTIALS"; - private static final byte[] SECRET_BYTE_ARRAY = new byte[]{1, 2, 3, 4, 5, 6}; + private static final String TAG = "SecurityUtils"; + private static final String CREDENTIALS_KEY = "KEY_CREDENTIALS"; + private static final byte[] SECRET_BYTE_ARRAY = new byte[] { 1, 2, 3, 4, 5, 6 }; - private static BiometricPrompt.CryptoObject cryptoObject; + private static BiometricPrompt.CryptoObject cryptoObject; - @RequiresApi(api = Build.VERSION_CODES.M) - public static boolean checkIfWeAreAuthenticated(String screenLockTimeout) { - try { - KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - SecretKey secretKey = (SecretKey) keyStore.getKey(CREDENTIALS_KEY, null); - Cipher cipher = - Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); + @RequiresApi(api = Build.VERSION_CODES.M) + public static boolean checkIfWeAreAuthenticated(String screenLockTimeout) { + try { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + SecretKey secretKey = (SecretKey) keyStore.getKey(CREDENTIALS_KEY, null); + Cipher cipher = + Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + + "/" + + KeyProperties.BLOCK_MODE_GCM + + "/" + + KeyProperties.ENCRYPTION_PADDING_NONE); - // Try encrypting something, it will only work if the user authenticated within - // the last AUTHENTICATION_DURATION_SECONDS seconds. - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - cipher.doFinal(SECRET_BYTE_ARRAY); + // Try encrypting something, it will only work if the user authenticated within + // the last AUTHENTICATION_DURATION_SECONDS seconds. + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + cipher.doFinal(SECRET_BYTE_ARRAY); - cryptoObject = new BiometricPrompt.CryptoObject(cipher); - // If the user has recently authenticated, we will reach here - return true; - } catch (UserNotAuthenticatedException e) { - // User is not authenticated, let's authenticate with device credentials. - return false; - } catch (KeyPermanentlyInvalidatedException e) { - // This happens if the lock screen has been disabled or reset after the key was - // generated after the key was generated. - // Shouldnt really happen because we regenerate the key every time an activity - // is created, but oh well - // Create key, and attempt again - createKey(screenLockTimeout); - return false; - } catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException | - CertificateException | UnrecoverableKeyException | IOException - | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { - return false; - } + cryptoObject = new BiometricPrompt.CryptoObject(cipher); + // If the user has recently authenticated, we will reach here + return true; + } catch (UserNotAuthenticatedException e) { + // User is not authenticated, let's authenticate with device credentials. + return false; + } catch (KeyPermanentlyInvalidatedException e) { + // This happens if the lock screen has been disabled or reset after the key was + // generated after the key was generated. + // Shouldnt really happen because we regenerate the key every time an activity + // is created, but oh well + // Create key, and attempt again + createKey(screenLockTimeout); + return false; + } catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException | + CertificateException | UnrecoverableKeyException | IOException + | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { + return false; } + } - @RequiresApi(api = Build.VERSION_CODES.M) - public static BiometricPrompt.CryptoObject getCryptoObject() { - return cryptoObject; + @RequiresApi(api = Build.VERSION_CODES.M) + public static BiometricPrompt.CryptoObject getCryptoObject() { + return cryptoObject; + } + + @RequiresApi(api = Build.VERSION_CODES.M) + public static void createKey(String validity) { + try { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + KeyGenerator keyGenerator = KeyGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); + + keyGenerator.init(new KeyGenParameterSpec.Builder(CREDENTIALS_KEY, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setRandomizedEncryptionRequired(true) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setUserAuthenticationRequired(true) + .setUserAuthenticationValidityDurationSeconds(getIntegerFromStringTimeout(validity)) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + + keyGenerator.generateKey(); + } catch (NoSuchAlgorithmException | NoSuchProviderException + | InvalidAlgorithmParameterException | KeyStoreException + | CertificateException | IOException e) { + Log.e(TAG, "Failed to create a symmetric key"); } + } - @RequiresApi(api = Build.VERSION_CODES.M) - public static void createKey(String validity) { - try { - KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - KeyGenerator keyGenerator = KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); - - keyGenerator.init(new KeyGenParameterSpec.Builder(CREDENTIALS_KEY, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setRandomizedEncryptionRequired(true) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setUserAuthenticationRequired(true) - .setUserAuthenticationValidityDurationSeconds(getIntegerFromStringTimeout(validity)) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .build()); - - keyGenerator.generateKey(); - } catch (NoSuchAlgorithmException | NoSuchProviderException - | InvalidAlgorithmParameterException | KeyStoreException - | CertificateException | IOException e) { - Log.e(TAG, "Failed to create a symmetric key"); - } - } - - private static int getIntegerFromStringTimeout(String validity) { - Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); - List entryValues = Arrays.asList(resources.getStringArray(R.array.screen_lock_timeout_entry_values)); - int[] entryIntValues = resources.getIntArray(R.array.screen_lock_timeout_entry_int_values); - int indexOfValidity = entryValues.indexOf(validity); - return entryIntValues[indexOfValidity]; - } + private static int getIntegerFromStringTimeout(String validity) { + Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); + List entryValues = + Arrays.asList(resources.getStringArray(R.array.screen_lock_timeout_entry_values)); + int[] entryIntValues = resources.getIntArray(R.array.screen_lock_timeout_entry_int_values); + int indexOfValidity = entryValues.indexOf(validity); + return entryIntValues[indexOfValidity]; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java index fd684a50c..68975581d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java @@ -41,65 +41,65 @@ import java.util.Set; public class ShareUtils { - public static String getStringForIntent(Context context, @Nullable String password, UserUtils userUtils, Conversation - conversation) { - UserEntity userEntity = userUtils.getCurrentUser(); + public static String getStringForIntent(Context context, @Nullable String password, + UserUtils userUtils, Conversation + conversation) { + UserEntity userEntity = userUtils.getCurrentUser(); - String shareString = ""; - if (userEntity != null && context != null) { - shareString = String.format(context.getResources().getString(R.string.nc_share_text), - userEntity.getBaseUrl(), conversation.getToken()); + String shareString = ""; + if (userEntity != null && context != null) { + shareString = String.format(context.getResources().getString(R.string.nc_share_text), + userEntity.getBaseUrl(), conversation.getToken()); - if (!TextUtils.isEmpty(password)) { - shareString += String.format(context.getResources().getString(R.string.nc_share_text_pass), password); - } - } - - return shareString; + if (!TextUtils.isEmpty(password)) { + shareString += + String.format(context.getResources().getString(R.string.nc_share_text_pass), password); + } } - public static List getShareApps(Context context, Intent intent, - @Nullable Set appsFilter, @Nullable Set toExclude) { + return shareString; + } - if (context == null || intent == null) return null; + public static List getShareApps(Context context, Intent intent, + @Nullable Set appsFilter, @Nullable Set toExclude) { - PackageManager manager = context.getPackageManager(); - List apps = manager.queryIntentActivities(intent, 0); + if (context == null || intent == null) return null; - if (apps != null && !apps.isEmpty()) { - List appResources = new ArrayList<>(apps.size()); - boolean shouldCheckPackages = appsFilter != null && !appsFilter.isEmpty(); + PackageManager manager = context.getPackageManager(); + List apps = manager.queryIntentActivities(intent, 0); - for (ResolveInfo resolveInfo : apps) { - String packageName = resolveInfo.activityInfo.packageName; + if (apps != null && !apps.isEmpty()) { + List appResources = new ArrayList<>(apps.size()); + boolean shouldCheckPackages = appsFilter != null && !appsFilter.isEmpty(); - if (shouldCheckPackages && !appsFilter.contains(packageName)) { - continue; - } - - String title = resolveInfo.loadLabel(manager).toString(); - String name = resolveInfo.activityInfo.name; - Drawable drawable = resolveInfo.loadIcon(manager); - appResources.add(new AppAdapter.AppInfo(title, packageName, name, drawable)); - } - - if (toExclude != null && !toExclude.isEmpty()) { - List toRemove = new ArrayList<>(); - - for (AppAdapter.AppInfo appInfo : appResources) { - if (toExclude.contains(appInfo.packageName)) { - toRemove.add(appInfo); - } - } - - if (!toRemove.isEmpty()) appResources.removeAll(toRemove); - } - - return appResources; + for (ResolveInfo resolveInfo : apps) { + String packageName = resolveInfo.activityInfo.packageName; + if (shouldCheckPackages && !appsFilter.contains(packageName)) { + continue; } - return null; + String title = resolveInfo.loadLabel(manager).toString(); + String name = resolveInfo.activityInfo.name; + Drawable drawable = resolveInfo.loadIcon(manager); + appResources.add(new AppAdapter.AppInfo(title, packageName, name, drawable)); + } + + if (toExclude != null && !toExclude.isEmpty()) { + List toRemove = new ArrayList<>(); + + for (AppAdapter.AppInfo appInfo : appResources) { + if (toExclude.contains(appInfo.packageName)) { + toRemove.add(appInfo); + } + } + + if (!toRemove.isEmpty()) appResources.removeAll(toRemove); + } + + return appResources; } + return null; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java b/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java index edc1991fa..aaaa60235 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java +++ b/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java @@ -39,55 +39,56 @@ import java.util.regex.Pattern; public final class TextMatchers { - private static final String TAG = "TextMatchers"; + private static final String TAG = "TextMatchers"; - public static ChatMessage.MessageType getMessageTypeFromString(@NonNull final String text) { - List links = new ArrayList<>(); - Matcher m = PatternsCompat.WEB_URL.matcher(text); - while (m.find()) { - String url = m.group(); - links.add(url); - } - - if (links.size() == 1 && text.trim().length() == links.get(0).length()) { - String specialLink = links.get(0); - if (specialLink.startsWith("https://media.giphy.com/") && specialLink.endsWith(".gif")) { - return ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE; - } else if (specialLink.contains("tenor.com/") && - Pattern.compile("https://media.*\\.tenor\\.com.*\\.gif.*", - Pattern.CASE_INSENSITIVE).matcher(specialLink).matches()) { - return ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE; - } else { - if (specialLink.contains("?")) { - specialLink = specialLink.substring(0, specialLink.indexOf("?")); - } - MimeUtil.registerMimeDetector(MagicMimeMimeDetector.class.getName()); - MimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName()); - MimeUtil.registerMimeDetector(OpendesktopMimeDetector.class.getName()); - - String mimeType = MimeUtil.getMostSpecificMimeType(MimeUtil.getMimeTypes(specialLink)).toString(); - if (mimeType.startsWith("image/")) { - if (mimeType.equalsIgnoreCase("image/gif")) { - return ChatMessage.MessageType.SINGLE_LINK_GIF_MESSAGE; - } else { - return ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE; - } - } else if (mimeType.startsWith("video/")) { - return ChatMessage.MessageType.SINGLE_LINK_VIDEO_MESSAGE; - } else if (mimeType.startsWith("audio/")) { - return ChatMessage.MessageType.SINGLE_LINK_AUDIO_MESSAGE; - } - - return ChatMessage.MessageType.SINGLE_LINK_MESSAGE; - } - } - - // if we have 0 or more than 1 link, we're a regular message - return ChatMessage.MessageType.REGULAR_TEXT_MESSAGE; + public static ChatMessage.MessageType getMessageTypeFromString(@NonNull final String text) { + List links = new ArrayList<>(); + Matcher m = PatternsCompat.WEB_URL.matcher(text); + while (m.find()) { + String url = m.group(); + links.add(url); } - public static boolean isMessageWithSingleEmoticonOnly(@Nullable final String text) { - final EmojiInformation emojiInformation = EmojiUtils.emojiInformation(text); - return (emojiInformation.isOnlyEmojis && emojiInformation.emojis.size() == 1); + if (links.size() == 1 && text.trim().length() == links.get(0).length()) { + String specialLink = links.get(0); + if (specialLink.startsWith("https://media.giphy.com/") && specialLink.endsWith(".gif")) { + return ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE; + } else if (specialLink.contains("tenor.com/") && + Pattern.compile("https://media.*\\.tenor\\.com.*\\.gif.*", + Pattern.CASE_INSENSITIVE).matcher(specialLink).matches()) { + return ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE; + } else { + if (specialLink.contains("?")) { + specialLink = specialLink.substring(0, specialLink.indexOf("?")); + } + MimeUtil.registerMimeDetector(MagicMimeMimeDetector.class.getName()); + MimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName()); + MimeUtil.registerMimeDetector(OpendesktopMimeDetector.class.getName()); + + String mimeType = + MimeUtil.getMostSpecificMimeType(MimeUtil.getMimeTypes(specialLink)).toString(); + if (mimeType.startsWith("image/")) { + if (mimeType.equalsIgnoreCase("image/gif")) { + return ChatMessage.MessageType.SINGLE_LINK_GIF_MESSAGE; + } else { + return ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE; + } + } else if (mimeType.startsWith("video/")) { + return ChatMessage.MessageType.SINGLE_LINK_VIDEO_MESSAGE; + } else if (mimeType.startsWith("audio/")) { + return ChatMessage.MessageType.SINGLE_LINK_AUDIO_MESSAGE; + } + + return ChatMessage.MessageType.SINGLE_LINK_MESSAGE; + } } + + // if we have 0 or more than 1 link, we're a regular message + return ChatMessage.MessageType.REGULAR_TEXT_MESSAGE; + } + + public static boolean isMessageWithSingleEmoticonOnly(@Nullable final String text) { + final EmojiInformation emojiInformation = EmojiUtils.emojiInformation(text); + return (emojiInformation.isOnlyEmojis && emojiInformation.emojis.size() == 1); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/animations/PulseAnimation.java b/app/src/main/java/com/nextcloud/talk/utils/animations/PulseAnimation.java index d0538d77e..18078f2bf 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/animations/PulseAnimation.java +++ b/app/src/main/java/com/nextcloud/talk/utils/animations/PulseAnimation.java @@ -31,57 +31,59 @@ import androidx.annotation.NonNull; public class PulseAnimation { - public static final int RESTART = 1; - public static final int REVERSE = 2; - public static final int INFINITE = -1; - private ObjectAnimator scaleDown; - private int duration = 310; - private View view; - private int repeatMode = ValueAnimator.RESTART; - private int repeatCount = INFINITE; + public static final int RESTART = 1; + public static final int REVERSE = 2; + public static final int INFINITE = -1; + private ObjectAnimator scaleDown; + private int duration = 310; + private View view; + private int repeatMode = ValueAnimator.RESTART; + private int repeatCount = INFINITE; - public static PulseAnimation create() { - return new PulseAnimation(); + public static PulseAnimation create() { + return new PulseAnimation(); + } + + public PulseAnimation with(@NonNull View view) { + this.view = view; + return this; + } + + public void start() { + + if (view == null) throw new NullPointerException("View cant be null!"); + + scaleDown = + ObjectAnimator.ofPropertyValuesHolder(view, PropertyValuesHolder.ofFloat("scaleX", 1.2f), + PropertyValuesHolder.ofFloat("scaleY", 1.2f)); + scaleDown.setDuration(duration); + scaleDown.setRepeatMode(repeatMode); + scaleDown.setRepeatCount(repeatCount); + scaleDown.setAutoCancel(true); + scaleDown.start(); + } + + public void stop() { + if (scaleDown != null && view != null) { + scaleDown.end(); + scaleDown.cancel(); + view.setScaleX(1.0f); + view.setScaleY(1.0f); } + } - public PulseAnimation with(@NonNull View view) { - this.view = view; - return this; - } + public PulseAnimation setDuration(int duration) { + this.duration = duration; + return this; + } - public void start() { + public PulseAnimation setRepeatMode(int repeatMode) { + this.repeatMode = repeatMode; + return this; + } - if (view == null) throw new NullPointerException("View cant be null!"); - - scaleDown = ObjectAnimator.ofPropertyValuesHolder(view, PropertyValuesHolder.ofFloat("scaleX", 1.2f), PropertyValuesHolder.ofFloat("scaleY", 1.2f)); - scaleDown.setDuration(duration); - scaleDown.setRepeatMode(repeatMode); - scaleDown.setRepeatCount(repeatCount); - scaleDown.setAutoCancel(true); - scaleDown.start(); - } - - public void stop() { - if (scaleDown != null && view != null) { - scaleDown.end(); - scaleDown.cancel(); - view.setScaleX(1.0f); - view.setScaleY(1.0f); - } - } - - public PulseAnimation setDuration(int duration) { - this.duration = duration; - return this; - } - - public PulseAnimation setRepeatMode(int repeatMode) { - this.repeatMode = repeatMode; - return this; - } - - public PulseAnimation setRepeatCount(int repeatCount) { - this.repeatCount = repeatCount; - return this; - } + public PulseAnimation setRepeatCount(int repeatCount) { + this.repeatCount = repeatCount; + return this; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/animations/SharedElementTransition.java b/app/src/main/java/com/nextcloud/talk/utils/animations/SharedElementTransition.java index 3dfea2eeb..9953cc8db 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/animations/SharedElementTransition.java +++ b/app/src/main/java/com/nextcloud/talk/utils/animations/SharedElementTransition.java @@ -36,53 +36,59 @@ import java.util.ArrayList; import java.util.List; public class SharedElementTransition extends SharedElementTransitionChangeHandler { - private static final String KEY_WAIT_FOR_TRANSITION_NAMES = "SharedElementTransition.names"; + private static final String KEY_WAIT_FOR_TRANSITION_NAMES = "SharedElementTransition.names"; - private final ArrayList names; + private final ArrayList names; - public SharedElementTransition() { - names = new ArrayList<>(); + public SharedElementTransition() { + names = new ArrayList<>(); + } + + public SharedElementTransition(ArrayList names) { + this.names = names; + } + + @Override + public void saveToBundle(@NonNull Bundle bundle) { + bundle.putStringArrayList(KEY_WAIT_FOR_TRANSITION_NAMES, names); + } + + @Override + public void restoreFromBundle(@NonNull Bundle bundle) { + List savedNames = bundle.getStringArrayList(KEY_WAIT_FOR_TRANSITION_NAMES); + if (savedNames != null) { + names.addAll(savedNames); } + } - public SharedElementTransition(ArrayList names) { - this.names = names; - } + @Nullable + public Transition getExitTransition(@NonNull ViewGroup container, @Nullable View from, + @Nullable View to, boolean isPush) { + return new Fade(Fade.OUT); + } - @Override - public void saveToBundle(@NonNull Bundle bundle) { - bundle.putStringArrayList(KEY_WAIT_FOR_TRANSITION_NAMES, names); - } + @Override + @Nullable + public Transition getSharedElementTransition(@NonNull ViewGroup container, @Nullable View from, + @Nullable View to, boolean isPush) { + return new TransitionSet().addTransition(new ChangeBounds()) + .addTransition(new ChangeClipBounds()) + .addTransition(new ChangeTransform()); + } - @Override - public void restoreFromBundle(@NonNull Bundle bundle) { - List savedNames = bundle.getStringArrayList(KEY_WAIT_FOR_TRANSITION_NAMES); - if (savedNames != null) { - names.addAll(savedNames); - } - } + @Override + @Nullable + public Transition getEnterTransition(@NonNull ViewGroup container, @Nullable View from, + @Nullable View to, boolean isPush) { + return new Fade(Fade.IN); + } - @Nullable - public Transition getExitTransition(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush) { - return new Fade(Fade.OUT); - } - - @Override - @Nullable - public Transition getSharedElementTransition(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush) { - return new TransitionSet().addTransition(new ChangeBounds()).addTransition(new ChangeClipBounds()).addTransition(new ChangeTransform()); - } - - @Override - @Nullable - public Transition getEnterTransition(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush) { - return new Fade(Fade.IN); - } - - @Override - public void configureSharedElements(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush) { - for (String name : names) { - addSharedElement(name); - //waitOnSharedElementNamed(name); - } + @Override + public void configureSharedElements(@NonNull ViewGroup container, @Nullable View from, + @Nullable View to, boolean isPush) { + for (String name : names) { + addSharedElement(name); + //waitOnSharedElementNamed(name); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/animations/ViewHidingBehaviourAnimation.java b/app/src/main/java/com/nextcloud/talk/utils/animations/ViewHidingBehaviourAnimation.java index caa0ad04e..24c8bc011 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/animations/ViewHidingBehaviourAnimation.java +++ b/app/src/main/java/com/nextcloud/talk/utils/animations/ViewHidingBehaviourAnimation.java @@ -31,45 +31,46 @@ import androidx.core.view.ViewCompat; public class ViewHidingBehaviourAnimation extends CoordinatorLayout.Behavior { - private int height; - private boolean slidingDown = false; + private int height; + private boolean slidingDown = false; - @Override - public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) { - height = child.getHeight(); - return super.onLayoutChild(parent, child, layoutDirection); + @Override + public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) { + height = child.getHeight(); + return super.onLayoutChild(parent, child, layoutDirection); + } + + @Override + public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, + @NonNull View child, @NonNull View directTargetChild, @NonNull View + target, int nestedScrollAxes) { + return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; + } + + @Override + public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, + @NonNull View target, int dxConsumed, int + dyConsumed, int dxUnconsumed, int dyUnconsumed) { + if (dyConsumed > 0) { + slideDown(child); + } else if (dyConsumed < 0) { + slideUp(child); } + } - @Override - public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View - target, int nestedScrollAxes) { - return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; + private void slideUp(View child) { + if (slidingDown) { + slidingDown = false; + child.clearAnimation(); + child.animate().translationY(0).setDuration(200); } + } - @Override - public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int - dyConsumed, int dxUnconsumed, int dyUnconsumed) { - if (dyConsumed > 0) { - slideDown(child); - } else if (dyConsumed < 0) { - slideUp(child); - } + private void slideDown(View child) { + if (!slidingDown) { + slidingDown = true; + child.clearAnimation(); + child.animate().translationY(height).setDuration(200); } - - private void slideUp(View child) { - if (slidingDown) { - slidingDown = false; - child.clearAnimation(); - child.animate().translationY(0).setDuration(200); - } - } - - private void slideDown(View child) { - if (!slidingDown) { - slidingDown = true; - child.clearAnimation(); - child.animate().translationY(height).setDuration(200); - } - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index 25dcf55ef..70d07c1b1 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -21,43 +21,43 @@ package com.nextcloud.talk.utils.bundle object BundleKeys { - val KEY_SELECTED_USERS = "KEY_SELECTED_USERS" + val KEY_SELECTED_USERS = "KEY_SELECTED_USERS" val KEY_SELECTED_GROUPS = "KEY_SELECTED_GROUPS" val KEY_USERNAME = "KEY_USERNAME" - val KEY_TOKEN = "KEY_TOKEN" - val KEY_BASE_URL = "KEY_BASE_URL" - val KEY_IS_ACCOUNT_IMPORT = "KEY_IS_ACCOUNT_IMPORT" - val KEY_ORIGINAL_PROTOCOL = "KEY_ORIGINAL_PROTOCOL" - val KEY_ROOM = "KEY_CONVERSATION" - val KEY_OPERATION_CODE = "KEY_OPERATION_CODE" - val KEY_MENU_TYPE = "KEY_MENU_TYPE" - val KEY_SHARE_INTENT = "KEY_SHARE_INTENT" - val KEY_APP_ITEM_PACKAGE_NAME = "KEY_APP_ITEM_PACKAGE_NAME" - val KEY_APP_ITEM_NAME = "KEY_APP_ITEM_NAME" - val KEY_CONVERSATION_PASSWORD = "KEY_CONVERSATION_PASSWORD" - val KEY_ROOM_TOKEN = "KEY_ROOM_TOKEN" - val KEY_USER_ENTITY = "KEY_USER_ENTITY" - val KEY_NEW_CONVERSATION = "KEY_NEW_CONVERSATION" - val KEY_ADD_PARTICIPANTS = "KEY_ADD_PARTICIPANTS" - val KEY_EXISTING_PARTICIPANTS = "KEY_EXISTING_PARTICIPANTS" - val KEY_CALL_URL = "KEY_CALL_URL" - val KEY_MODIFIED_BASE_URL = "KEY_MODIFIED_BASE_URL" - val KEY_NOTIFICATION_SUBJECT = "KEY_NOTIFICATION_SUBJECT" - val KEY_NOTIFICATION_SIGNATURE = "KEY_NOTIFICATION_SIGNATURE" - val KEY_INTERNAL_USER_ID = "KEY_INTERNAL_USER_ID" - val KEY_CONVERSATION_TYPE = "KEY_CONVERSATION_TYPE" - val KEY_INVITED_PARTICIPANTS = "KEY_INVITED_PARTICIPANTS" - val KEY_INVITED_GROUP = "KEY_INVITED_GROUP" - val KEY_CONVERSATION_NAME = "KEY_CONVERSATION_NAME" - val KEY_CALL_VOICE_ONLY = "KEY_CALL_VOICE_ONLY" - val KEY_ACTIVE_CONVERSATION = "KEY_ACTIVE_CONVERSATION" - val KEY_SERVER_CAPABILITIES = "KEY_SERVER_CAPABILITIES" - val KEY_FROM_NOTIFICATION_START_CALL = "KEY_FROM_NOTIFICATION_START_CALL" - val KEY_ROOM_ID = "KEY_ROOM_ID" - val KEY_ARE_CALL_SOUNDS = "KEY_ARE_CALL_SOUNDS" - val KEY_BROWSER_TYPE = "KEY_BROWSER_TYPE" - val KEY_FILE_PATHS = "KEY_FILE_PATHS" - val KEY_ACCOUNT = "KEY_ACCOUNT" - val KEY_FILE_ID = "KEY_FILE_ID" - val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID" + val KEY_TOKEN = "KEY_TOKEN" + val KEY_BASE_URL = "KEY_BASE_URL" + val KEY_IS_ACCOUNT_IMPORT = "KEY_IS_ACCOUNT_IMPORT" + val KEY_ORIGINAL_PROTOCOL = "KEY_ORIGINAL_PROTOCOL" + val KEY_ROOM = "KEY_CONVERSATION" + val KEY_OPERATION_CODE = "KEY_OPERATION_CODE" + val KEY_MENU_TYPE = "KEY_MENU_TYPE" + val KEY_SHARE_INTENT = "KEY_SHARE_INTENT" + val KEY_APP_ITEM_PACKAGE_NAME = "KEY_APP_ITEM_PACKAGE_NAME" + val KEY_APP_ITEM_NAME = "KEY_APP_ITEM_NAME" + val KEY_CONVERSATION_PASSWORD = "KEY_CONVERSATION_PASSWORD" + val KEY_ROOM_TOKEN = "KEY_ROOM_TOKEN" + val KEY_USER_ENTITY = "KEY_USER_ENTITY" + val KEY_NEW_CONVERSATION = "KEY_NEW_CONVERSATION" + val KEY_ADD_PARTICIPANTS = "KEY_ADD_PARTICIPANTS" + val KEY_EXISTING_PARTICIPANTS = "KEY_EXISTING_PARTICIPANTS" + val KEY_CALL_URL = "KEY_CALL_URL" + val KEY_MODIFIED_BASE_URL = "KEY_MODIFIED_BASE_URL" + val KEY_NOTIFICATION_SUBJECT = "KEY_NOTIFICATION_SUBJECT" + val KEY_NOTIFICATION_SIGNATURE = "KEY_NOTIFICATION_SIGNATURE" + val KEY_INTERNAL_USER_ID = "KEY_INTERNAL_USER_ID" + val KEY_CONVERSATION_TYPE = "KEY_CONVERSATION_TYPE" + val KEY_INVITED_PARTICIPANTS = "KEY_INVITED_PARTICIPANTS" + val KEY_INVITED_GROUP = "KEY_INVITED_GROUP" + val KEY_CONVERSATION_NAME = "KEY_CONVERSATION_NAME" + val KEY_CALL_VOICE_ONLY = "KEY_CALL_VOICE_ONLY" + val KEY_ACTIVE_CONVERSATION = "KEY_ACTIVE_CONVERSATION" + val KEY_SERVER_CAPABILITIES = "KEY_SERVER_CAPABILITIES" + val KEY_FROM_NOTIFICATION_START_CALL = "KEY_FROM_NOTIFICATION_START_CALL" + val KEY_ROOM_ID = "KEY_ROOM_ID" + val KEY_ARE_CALL_SOUNDS = "KEY_ARE_CALL_SOUNDS" + val KEY_BROWSER_TYPE = "KEY_BROWSER_TYPE" + val KEY_FILE_PATHS = "KEY_FILE_PATHS" + val KEY_ACCOUNT = "KEY_ACCOUNT" + val KEY_FILE_ID = "KEY_FILE_ID" + val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID" } diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageModule.java b/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageModule.java index 2d5bedcd6..3b0d4e204 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageModule.java +++ b/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageModule.java @@ -32,12 +32,13 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class ArbitraryStorageModule { - @Inject - public ArbitraryStorageModule() { - } + @Inject + public ArbitraryStorageModule() { + } - @Provides - public ArbitraryStorageUtils provideArbitraryStorageUtils(ReactiveEntityStore dataStore) { - return new ArbitraryStorageUtils(dataStore); - } + @Provides + public ArbitraryStorageUtils provideArbitraryStorageUtils( + ReactiveEntityStore dataStore) { + return new ArbitraryStorageUtils(dataStore); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java b/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java index d9db2d559..cbc5e59a2 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java @@ -30,39 +30,41 @@ import io.requery.reactivex.ReactiveEntityStore; import io.requery.reactivex.ReactiveScalar; public class ArbitraryStorageUtils { - private ReactiveEntityStore dataStore; + private ReactiveEntityStore dataStore; - ArbitraryStorageUtils(ReactiveEntityStore dataStore) { - this.dataStore = dataStore; - } + ArbitraryStorageUtils(ReactiveEntityStore dataStore) { + this.dataStore = dataStore; + } + public void storeStorageSetting(long accountIdentifier, String key, String value, String object) { + ArbitraryStorageEntity arbitraryStorageEntity = new ArbitraryStorageEntity(); + arbitraryStorageEntity.setAccountIdentifier(accountIdentifier); + arbitraryStorageEntity.setKey(key); + arbitraryStorageEntity.setValue(value); + arbitraryStorageEntity.setObject(object); - public void storeStorageSetting(long accountIdentifier, String key, String value, String object) { - ArbitraryStorageEntity arbitraryStorageEntity = new ArbitraryStorageEntity(); - arbitraryStorageEntity.setAccountIdentifier(accountIdentifier); - arbitraryStorageEntity.setKey(key); - arbitraryStorageEntity.setValue(value); - arbitraryStorageEntity.setObject(object); + dataStore.upsert(arbitraryStorageEntity) + .toObservable() + .subscribeOn(Schedulers.io()) + .subscribe(); + } - dataStore.upsert(arbitraryStorageEntity) - .toObservable() - .subscribeOn(Schedulers.io()) - .subscribe(); - } + public ArbitraryStorageEntity getStorageSetting(long accountIdentifier, String key, + @Nullable String object) { + Result findStorageQueryResult = dataStore.select(ArbitraryStorage.class) + .where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier) + .and(ArbitraryStorageEntity.KEY.eq(key)).and(ArbitraryStorageEntity.OBJECT.eq(object))) + .limit(1).get(); - public ArbitraryStorageEntity getStorageSetting(long accountIdentifier, String key, @Nullable String object) { - Result findStorageQueryResult = dataStore.select(ArbitraryStorage.class) - .where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier) - .and(ArbitraryStorageEntity.KEY.eq(key)).and(ArbitraryStorageEntity.OBJECT.eq(object))) - .limit(1).get(); + return (ArbitraryStorageEntity) findStorageQueryResult.firstOrNull(); + } - return (ArbitraryStorageEntity) findStorageQueryResult.firstOrNull(); - } + public Observable deleteAllEntriesForAccountIdentifier(long accountIdentifier) { + ReactiveScalar deleteResult = dataStore.delete(ArbitraryStorage.class) + .where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier)) + .get(); - public Observable deleteAllEntriesForAccountIdentifier(long accountIdentifier) { - ReactiveScalar deleteResult = dataStore.delete(ArbitraryStorage.class).where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier)).get(); - - return deleteResult.single().toObservable() - .subscribeOn(Schedulers.io()); - } + return deleteResult.single().toObservable() + .subscribeOn(Schedulers.io()); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/UserModule.java b/app/src/main/java/com/nextcloud/talk/utils/database/user/UserModule.java index d24b61832..a30862268 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/UserModule.java +++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/UserModule.java @@ -32,12 +32,12 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class UserModule { - @Inject - public UserModule() { - } + @Inject + public UserModule() { + } - @Provides - public UserUtils provideUserUtils(ReactiveEntityStore dataStore) { - return new UserUtils(dataStore); - } + @Provides + public UserUtils provideUserUtils(ReactiveEntityStore dataStore) { + return new UserUtils(dataStore); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java b/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java index 1fbffc998..7378fccc2 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java @@ -34,236 +34,238 @@ import io.requery.reactivex.ReactiveEntityStore; import java.util.List; public class UserUtils { - private ReactiveEntityStore dataStore; + private ReactiveEntityStore dataStore; - public UserUtils(ReactiveEntityStore dataStore) { - this.dataStore = dataStore; + public UserUtils(ReactiveEntityStore dataStore) { + this.dataStore = dataStore; + } + + public boolean anyUserExists() { + return (dataStore.count(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)) + .limit(1).get().value() > 0); + } + + public List getUsers() { + Result findUsersQueryResult = dataStore.select(User.class).where + (UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)).get(); + + return findUsersQueryResult.toList(); + } + + public List getUsersScheduledForDeletion() { + Result findUsersQueryResult = + dataStore.select(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.eq(true)) + .get(); + + return findUsersQueryResult.toList(); + } + + public UserEntity getAnyUserAndSetAsActive() { + Result findUserQueryResult = dataStore.select(User.class) + .where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)) + .limit(1).get(); + + UserEntity userEntity; + if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { + userEntity.setCurrent(true); + dataStore.update(userEntity).blockingGet(); + return userEntity; } - public boolean anyUserExists() { - return (dataStore.count(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)) - .limit(1).get().value() > 0); + return null; + } + + public UserEntity getCurrentUser() { + Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.CURRENT.eq(true) + .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true))) + .limit(1).get(); + + return (UserEntity) findUserQueryResult.firstOrNull(); + } + + public Completable deleteUser(long internalId) { + Result findUserQueryResult = + dataStore.select(User.class).where(UserEntity.ID.eq(internalId)).limit(1).get(); + + UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); + + return dataStore.delete(user) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + public Completable deleteUserWithId(long id) { + Result findUserQueryResult = + dataStore.select(User.class).where(UserEntity.ID.eq(id)).limit(1).get(); + + UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); + + return dataStore.delete(user) + .subscribeOn(Schedulers.io()); + } + + public UserEntity getUserById(String id) { + Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USER_ID.eq(id)) + .limit(1).get(); + + return (UserEntity) findUserQueryResult.firstOrNull(); + } + + public UserEntity getUserWithId(long id) { + Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) + .limit(1).get(); + + return (UserEntity) findUserQueryResult.firstOrNull(); + } + + public void disableAllUsersWithoutId(long userId) { + Result findUserQueryResult = + dataStore.select(User.class).where(UserEntity.ID.notEqual(userId)).get(); + + for (Object object : findUserQueryResult) { + UserEntity userEntity = (UserEntity) object; + userEntity.setCurrent(false); + dataStore.update(userEntity).blockingGet(); + } + } + + public boolean checkIfUserIsScheduledForDeletion(String username, String server) { + Result findUserQueryResult = + dataStore.select(User.class).where(UserEntity.USERNAME.eq(username)) + .and(UserEntity.BASE_URL.eq(server)) + .limit(1).get(); + + UserEntity userEntity; + if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { + return userEntity.getScheduledForDeletion(); } - public List getUsers() { - Result findUsersQueryResult = dataStore.select(User.class).where - (UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)).get(); + return false; + } - return findUsersQueryResult.toList(); + public UserEntity getUserWithInternalId(long internalId) { + Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId) + .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true))) + .limit(1).get(); + + return (UserEntity) findUserQueryResult.firstOrNull(); + } + + public boolean getIfUserWithUsernameAndServer(String username, String server) { + Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username) + .and(UserEntity.BASE_URL.eq(server))) + .limit(1).get(); + + return findUserQueryResult.firstOrNull() != null; + } + + public boolean scheduleUserForDeletionWithId(long id) { + Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) + .limit(1).get(); + + UserEntity userEntity; + if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { + userEntity.setScheduledForDeletion(true); + userEntity.setCurrent(false); + dataStore.update(userEntity).blockingGet(); } - public List getUsersScheduledForDeletion() { - Result findUsersQueryResult = dataStore.select(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.eq(true)) - .get(); + return getAnyUserAndSetAsActive() != null; + } - return findUsersQueryResult.toList(); + public Observable createOrUpdateUser(@Nullable String username, + @Nullable String token, + @Nullable String serverUrl, + @Nullable String displayName, + @Nullable String pushConfigurationState, + @Nullable Boolean currentUser, + @Nullable String userId, + @Nullable Long internalId, + @Nullable String capabilities, + @Nullable String certificateAlias, + @Nullable String externalSignalingServer) { + Result findUserQueryResult; + if (internalId == null) { + findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username). + and(UserEntity.BASE_URL.eq(serverUrl))).limit(1).get(); + } else { + findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId)).get(); } + UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); - public UserEntity getAnyUserAndSetAsActive() { - Result findUserQueryResult = dataStore.select(User.class) - .where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)) - .limit(1).get(); + if (user == null) { + user = new UserEntity(); + user.setBaseUrl(serverUrl); + user.setUsername(username); + user.setToken(token); - UserEntity userEntity; - if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { - userEntity.setCurrent(true); - dataStore.update(userEntity).blockingGet(); - return userEntity; - } + if (!TextUtils.isEmpty(displayName)) { + user.setDisplayName(displayName); + } - return null; - } - - public UserEntity getCurrentUser() { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.CURRENT.eq(true) - .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true))) - .limit(1).get(); - - return (UserEntity) findUserQueryResult.firstOrNull(); - } - - public Completable deleteUser(long internalId) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId)).limit(1).get(); - - UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); - - return dataStore.delete(user) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - - } - - public Completable deleteUserWithId(long id) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)).limit(1).get(); - - UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); - - return dataStore.delete(user) - .subscribeOn(Schedulers.io()); - - } - - public UserEntity getUserById(String id) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USER_ID.eq(id)) - .limit(1).get(); - - return (UserEntity) findUserQueryResult.firstOrNull(); - } - - public UserEntity getUserWithId(long id) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) - .limit(1).get(); - - return (UserEntity) findUserQueryResult.firstOrNull(); - } - - - public void disableAllUsersWithoutId(long userId) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.notEqual(userId)).get(); - - for (Object object : findUserQueryResult) { - UserEntity userEntity = (UserEntity) object; - userEntity.setCurrent(false); - dataStore.update(userEntity).blockingGet(); - } - } - - public boolean checkIfUserIsScheduledForDeletion(String username, String server) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username)) - .and(UserEntity.BASE_URL.eq(server)) - .limit(1).get(); - - UserEntity userEntity; - if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { - return userEntity.getScheduledForDeletion(); - } - - return false; - - } - - public UserEntity getUserWithInternalId(long internalId) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId) - .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true))) - .limit(1).get(); - - return (UserEntity) findUserQueryResult.firstOrNull(); - } - - public boolean getIfUserWithUsernameAndServer(String username, String server) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username) - .and(UserEntity.BASE_URL.eq(server))) - .limit(1).get(); - - return findUserQueryResult.firstOrNull() != null; - } - - public boolean scheduleUserForDeletionWithId(long id) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) - .limit(1).get(); - - UserEntity userEntity; - if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { - userEntity.setScheduledForDeletion(true); - userEntity.setCurrent(false); - dataStore.update(userEntity).blockingGet(); - } - - return getAnyUserAndSetAsActive() != null; - - } - - public Observable createOrUpdateUser(@Nullable String username, @Nullable String token, - @Nullable String serverUrl, - @Nullable String displayName, - @Nullable String pushConfigurationState, - @Nullable Boolean currentUser, - @Nullable String userId, - @Nullable Long internalId, - @Nullable String capabilities, - @Nullable String certificateAlias, - @Nullable String externalSignalingServer) { - Result findUserQueryResult; - if (internalId == null) { - findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username). - and(UserEntity.BASE_URL.eq(serverUrl))).limit(1).get(); - } else { - findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId)).get(); - } - - UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); - - if (user == null) { - user = new UserEntity(); - user.setBaseUrl(serverUrl); - user.setUsername(username); - user.setToken(token); - - if (!TextUtils.isEmpty(displayName)) { - user.setDisplayName(displayName); - } - - if (pushConfigurationState != null) { - user.setPushConfigurationState(pushConfigurationState); - } - - if (!TextUtils.isEmpty(userId)) { - user.setUserId(userId); - } - - if (!TextUtils.isEmpty(capabilities)) { - user.setCapabilities(capabilities); - } - - if (!TextUtils.isEmpty(certificateAlias)) { - user.setClientCertificate(certificateAlias); - } - - if (!TextUtils.isEmpty(externalSignalingServer)) { - user.setExternalSignalingServer(externalSignalingServer); - } - - user.setCurrent(true); - - } else { - if (userId != null && (user.getUserId() == null || !user.getUserId().equals(userId))) { - user.setUserId(userId); - } - - if (token != null && !token.equals(user.getToken())) { - user.setToken(token); - } - - if ((displayName != null && user.getDisplayName() == null) || (displayName != null && user.getDisplayName() - != null && !displayName.equals(user.getDisplayName()))) { - user.setDisplayName(displayName); - } - - if (pushConfigurationState != null && !pushConfigurationState.equals(user.getPushConfigurationState())) { - user.setPushConfigurationState(pushConfigurationState); - } - - if (capabilities != null && !capabilities.equals(user.getCapabilities())) { - user.setCapabilities(capabilities); - } - - if (certificateAlias != null && !certificateAlias.equals(user.getClientCertificate())) { - user.setClientCertificate(certificateAlias); - } - - if (externalSignalingServer != null && !externalSignalingServer.equals(user.getExternalSignalingServer())) { - user.setExternalSignalingServer(externalSignalingServer); - } - - if (currentUser != null) { - user.setCurrent(currentUser); - } - } - - return dataStore.upsert(user) - .toObservable() - .subscribeOn(Schedulers.io()); + if (pushConfigurationState != null) { + user.setPushConfigurationState(pushConfigurationState); + } + + if (!TextUtils.isEmpty(userId)) { + user.setUserId(userId); + } + + if (!TextUtils.isEmpty(capabilities)) { + user.setCapabilities(capabilities); + } + + if (!TextUtils.isEmpty(certificateAlias)) { + user.setClientCertificate(certificateAlias); + } + + if (!TextUtils.isEmpty(externalSignalingServer)) { + user.setExternalSignalingServer(externalSignalingServer); + } + + user.setCurrent(true); + } else { + if (userId != null && (user.getUserId() == null || !user.getUserId().equals(userId))) { + user.setUserId(userId); + } + + if (token != null && !token.equals(user.getToken())) { + user.setToken(token); + } + + if ((displayName != null && user.getDisplayName() == null) || (displayName != null + && user.getDisplayName() + != null + && !displayName.equals(user.getDisplayName()))) { + user.setDisplayName(displayName); + } + + if (pushConfigurationState != null && !pushConfigurationState.equals( + user.getPushConfigurationState())) { + user.setPushConfigurationState(pushConfigurationState); + } + + if (capabilities != null && !capabilities.equals(user.getCapabilities())) { + user.setCapabilities(capabilities); + } + + if (certificateAlias != null && !certificateAlias.equals(user.getClientCertificate())) { + user.setClientCertificate(certificateAlias); + } + + if (externalSignalingServer != null && !externalSignalingServer.equals( + user.getExternalSignalingServer())) { + user.setExternalSignalingServer(externalSignalingServer); + } + + if (currentUser != null) { + user.setCurrent(currentUser); + } } + return dataStore.upsert(user) + .toObservable() + .subscribeOn(Schedulers.io()); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java b/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java index 0f13b25cc..495438bae 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java @@ -37,156 +37,164 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class PowerManagerUtils { - private static final String TAG = "PowerManagerUtils"; - private final PowerManager.WakeLock fullLock; - private final PowerManager.WakeLock partialLock; - private final WifiManager.WifiLock wifiLock; - private final boolean wifiLockEnforced; - @Inject - Context context; - private ProximityLock proximityLock; - private boolean proximityDisabled = false; + private static final String TAG = "PowerManagerUtils"; + private final PowerManager.WakeLock fullLock; + private final PowerManager.WakeLock partialLock; + private final WifiManager.WifiLock wifiLock; + private final boolean wifiLockEnforced; + @Inject + Context context; + private ProximityLock proximityLock; + private boolean proximityDisabled = false; - private int orientation; + private int orientation; - public PowerManagerUtils() { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public PowerManagerUtils() { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - fullLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "nctalk:fullwakelock"); - partialLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "nctalk:partialwakelock"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - proximityLock = new ProximityLock(pm); - } - - // we suppress a possible leak because this is indeed application context - @SuppressLint("WifiManagerPotentialLeak") WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "nctalk:wifiwakelock"); - - fullLock.setReferenceCounted(false); - partialLock.setReferenceCounted(false); - wifiLock.setReferenceCounted(false); - - wifiLockEnforced = isWifiPowerActiveModeEnabled(context); - orientation = context.getResources().getConfiguration().orientation; + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + fullLock = + pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, + "nctalk:fullwakelock"); + partialLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "nctalk:partialwakelock"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + proximityLock = new ProximityLock(pm); } - public void setOrientation(int newOrientation) { - orientation = newOrientation; + // we suppress a possible leak because this is indeed application context + @SuppressLint("WifiManagerPotentialLeak") WifiManager wm = + (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "nctalk:wifiwakelock"); + + fullLock.setReferenceCounted(false); + partialLock.setReferenceCounted(false); + wifiLock.setReferenceCounted(false); + + wifiLockEnforced = isWifiPowerActiveModeEnabled(context); + orientation = context.getResources().getConfiguration().orientation; + } + + public void setOrientation(int newOrientation) { + orientation = newOrientation; + updateInCallWakeLockState(); + } + + public void updatePhoneState(PhoneState state) { + switch (state) { + case IDLE: + setWakeLockState(WakeLockState.SLEEP); + break; + case PROCESSING: + setWakeLockState(WakeLockState.PARTIAL); + break; + case INTERACTIVE: + setWakeLockState(WakeLockState.FULL); + break; + case WITH_PROXIMITY_SENSOR_LOCK: + proximityDisabled = false; updateInCallWakeLockState(); + break; + case WITHOUT_PROXIMITY_SENSOR_LOCK: + proximityDisabled = true; + updateInCallWakeLockState(); + break; } + } - public void updatePhoneState(PhoneState state) { - switch (state) { - case IDLE: - setWakeLockState(WakeLockState.SLEEP); - break; - case PROCESSING: - setWakeLockState(WakeLockState.PARTIAL); - break; - case INTERACTIVE: - setWakeLockState(WakeLockState.FULL); - break; - case WITH_PROXIMITY_SENSOR_LOCK: - proximityDisabled = false; - updateInCallWakeLockState(); - break; - case WITHOUT_PROXIMITY_SENSOR_LOCK: - proximityDisabled = true; - updateInCallWakeLockState(); - break; + private void updateInCallWakeLockState() { + if (orientation != Configuration.ORIENTATION_LANDSCAPE + && wifiLockEnforced + && !proximityDisabled) { + setWakeLockState(WakeLockState.PROXIMITY); + } else { + setWakeLockState(WakeLockState.FULL); + } + } + + private boolean isWifiPowerActiveModeEnabled(Context context) { + int wifi_pwr_active_mode = + Settings.Secure.getInt(context.getContentResolver(), "wifi_pwr_active_mode", -1); + return (wifi_pwr_active_mode != 0); + } + + @SuppressLint("WakelockTimeout") + private synchronized void setWakeLockState(WakeLockState newState) { + switch (newState) { + case FULL: + if (!fullLock.isHeld()) { + fullLock.acquire(); } - } - private void updateInCallWakeLockState() { - if (orientation != Configuration.ORIENTATION_LANDSCAPE && wifiLockEnforced && !proximityDisabled) { - setWakeLockState(WakeLockState.PROXIMITY); - } else { - setWakeLockState(WakeLockState.FULL); + if (!partialLock.isHeld()) { + partialLock.acquire(); } - } - private boolean isWifiPowerActiveModeEnabled(Context context) { - int wifi_pwr_active_mode = Settings.Secure.getInt(context.getContentResolver(), "wifi_pwr_active_mode", -1); - return (wifi_pwr_active_mode != 0); - } - - @SuppressLint("WakelockTimeout") - private synchronized void setWakeLockState(WakeLockState newState) { - switch (newState) { - case FULL: - if (!fullLock.isHeld()) { - fullLock.acquire(); - } - - if (!partialLock.isHeld()) { - partialLock.acquire(); - } - - if (!wifiLock.isHeld()) { - wifiLock.acquire(); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - proximityLock.release(); - } - break; - case PARTIAL: - if (!partialLock.isHeld()) { - partialLock.acquire(); - } - - if (!wifiLock.isHeld()) { - wifiLock.acquire(); - } - - fullLock.release(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - proximityLock.release(); - } - break; - case SLEEP: - fullLock.release(); - partialLock.release(); - wifiLock.release(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - proximityLock.release(); - } - break; - case PROXIMITY: - if (!partialLock.isHeld()) { - partialLock.acquire(); - } - - if (!wifiLock.isHeld()) { - wifiLock.acquire(); - } - - fullLock.release( - - ); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - proximityLock.acquire(); - } - break; - default: - // something went very very wrong + if (!wifiLock.isHeld()) { + wifiLock.acquire(); } - } - public enum PhoneState { - IDLE, - PROCESSING, //used when the phone is active but before the user should be alerted. - INTERACTIVE, - WITHOUT_PROXIMITY_SENSOR_LOCK, - WITH_PROXIMITY_SENSOR_LOCK - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + proximityLock.release(); + } + break; + case PARTIAL: + if (!partialLock.isHeld()) { + partialLock.acquire(); + } - public enum WakeLockState { - FULL, - PARTIAL, - SLEEP, - PROXIMITY + if (!wifiLock.isHeld()) { + wifiLock.acquire(); + } + + fullLock.release(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + proximityLock.release(); + } + break; + case SLEEP: + fullLock.release(); + partialLock.release(); + wifiLock.release(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + proximityLock.release(); + } + break; + case PROXIMITY: + if (!partialLock.isHeld()) { + partialLock.acquire(); + } + + if (!wifiLock.isHeld()) { + wifiLock.acquire(); + } + + fullLock.release( + + ); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + proximityLock.acquire(); + } + break; + default: + // something went very very wrong } + } + + public enum PhoneState { + IDLE, + PROCESSING, //used when the phone is active but before the user should be alerted. + INTERACTIVE, + WITHOUT_PROXIMITY_SENSOR_LOCK, + WITH_PROXIMITY_SENSOR_LOCK + } + + public enum WakeLockState { + FULL, + PARTIAL, + SLEEP, + PROXIMITY + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/power/ProximityLock.java b/app/src/main/java/com/nextcloud/talk/utils/power/ProximityLock.java index bf3f7088d..6afa8bae1 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/power/ProximityLock.java +++ b/app/src/main/java/com/nextcloud/talk/utils/power/ProximityLock.java @@ -27,38 +27,39 @@ import androidx.annotation.RequiresApi; import java.util.Optional; class ProximityLock { - private final Optional proximityLock; + private final Optional proximityLock; - @RequiresApi(api = Build.VERSION_CODES.N) - ProximityLock(PowerManager pm) { - proximityLock = getProximityLock(pm); + @RequiresApi(api = Build.VERSION_CODES.N) ProximityLock(PowerManager pm) { + proximityLock = getProximityLock(pm); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + private Optional getProximityLock(PowerManager powerManager) { + if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { + return Optional.ofNullable( + powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, + "nctalk:proximitylock")); + } else { + return Optional.empty(); + } + } + + @SuppressLint("WakelockTimeout") + @RequiresApi(api = Build.VERSION_CODES.N) + void acquire() { + if (!proximityLock.isPresent() || proximityLock.get().isHeld()) { + return; } - @RequiresApi(api = Build.VERSION_CODES.N) - private Optional getProximityLock(PowerManager powerManager) { - if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { - return Optional.ofNullable(powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "nctalk:proximitylock")); - } else { - return Optional.empty(); - } + proximityLock.get().acquire(); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + void release() { + if (!proximityLock.isPresent() || !proximityLock.get().isHeld()) { + return; } - @SuppressLint("WakelockTimeout") - @RequiresApi(api = Build.VERSION_CODES.N) - void acquire() { - if (!proximityLock.isPresent() || proximityLock.get().isHeld()) { - return; - } - - proximityLock.get().acquire(); - } - - @RequiresApi(api = Build.VERSION_CODES.N) - void release() { - if (!proximityLock.isPresent() || !proximityLock.get().isHeld()) { - return; - } - - proximityLock.get().release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); - } + proximityLock.get().release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java index a3f573e2e..adf3432fa 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java @@ -35,258 +35,258 @@ import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener; @SaveOption(SaveMode.APPLY) public interface AppPreferences { - @KeyByString("proxy_type") - @RegisterChangeListenerMethod - void registerProxyTypeListener(OnPreferenceValueChangedListener listener); + @KeyByString("proxy_type") + @RegisterChangeListenerMethod + void registerProxyTypeListener(OnPreferenceValueChangedListener listener); - @KeyByString("proxy_type") - @UnregisterChangeListenerMethod - void unregisterProxyTypeListener(OnPreferenceValueChangedListener listener); + @KeyByString("proxy_type") + @UnregisterChangeListenerMethod + void unregisterProxyTypeListener(OnPreferenceValueChangedListener listener); - @KeyByString("proxy_type") - String getProxyType(); + @KeyByString("proxy_type") + String getProxyType(); - @KeyByString("proxy_type") - void setProxyType(String proxyType); + @KeyByString("proxy_type") + void setProxyType(String proxyType); - @KeyByString("proxy_server") - @RemoveMethod - void removeProxyType(); + @KeyByString("proxy_server") + @RemoveMethod + void removeProxyType(); - @KeyByString("proxy_host") - String getProxyHost(); + @KeyByString("proxy_host") + String getProxyHost(); - @KeyByString("proxy_host") - void setProxyHost(String proxyHost); + @KeyByString("proxy_host") + void setProxyHost(String proxyHost); - @KeyByString("proxy_host") - @RemoveMethod - void removeProxyHost(); + @KeyByString("proxy_host") + @RemoveMethod + void removeProxyHost(); - @KeyByString("proxy_port") - String getProxyPort(); + @KeyByString("proxy_port") + String getProxyPort(); - @KeyByString("proxy_port") - void setProxyPort(String proxyPort); + @KeyByString("proxy_port") + void setProxyPort(String proxyPort); - @KeyByString("proxy_port") - @RemoveMethod - void removeProxyPort(); + @KeyByString("proxy_port") + @RemoveMethod + void removeProxyPort(); - @KeyByString("proxy_credentials") - @RegisterChangeListenerMethod - void registerProxyCredentialsListener(OnPreferenceValueChangedListener listener); + @KeyByString("proxy_credentials") + @RegisterChangeListenerMethod + void registerProxyCredentialsListener(OnPreferenceValueChangedListener listener); - @KeyByString("proxy_credentials") - @UnregisterChangeListenerMethod - void unregisterProxyCredentialsListener(OnPreferenceValueChangedListener listener); + @KeyByString("proxy_credentials") + @UnregisterChangeListenerMethod + void unregisterProxyCredentialsListener(OnPreferenceValueChangedListener listener); - @KeyByString("proxy_credentials") - boolean getProxyCredentials(); + @KeyByString("proxy_credentials") + boolean getProxyCredentials(); - @KeyByString("proxy_credentials") - void setProxyNeedsCredentials(boolean proxyNeedsCredentials); + @KeyByString("proxy_credentials") + void setProxyNeedsCredentials(boolean proxyNeedsCredentials); - @KeyByString("proxy_credentials") - @RemoveMethod - void removeProxyCredentials(); + @KeyByString("proxy_credentials") + @RemoveMethod + void removeProxyCredentials(); - @KeyByString("proxy_username") - String getProxyUsername(); + @KeyByString("proxy_username") + String getProxyUsername(); - @KeyByString("proxy_username") - void setProxyUsername(String proxyUsername); + @KeyByString("proxy_username") + void setProxyUsername(String proxyUsername); - @KeyByString("proxy_username") - @RemoveMethod - void removeProxyUsername(); + @KeyByString("proxy_username") + @RemoveMethod + void removeProxyUsername(); - @KeyByString("proxy_password") - String getProxyPassword(); + @KeyByString("proxy_password") + String getProxyPassword(); - @KeyByString("proxy_password") - void setProxyPassword(String proxyPassword); + @KeyByString("proxy_password") + void setProxyPassword(String proxyPassword); - @KeyByString("proxy_password") - @RemoveMethod - void removeProxyPassword(); + @KeyByString("proxy_password") + @RemoveMethod + void removeProxyPassword(); - @KeyByString("push_token") - String getPushToken(); + @KeyByString("push_token") + String getPushToken(); - @KeyByString("push_token") - void setPushToken(String pushToken); + @KeyByString("push_token") + void setPushToken(String pushToken); - @KeyByString("push_token") - @RemoveMethod - void removePushToken(); + @KeyByString("push_token") + @RemoveMethod + void removePushToken(); - @KeyByString("tempClientCertAlias") - String getTemporaryClientCertAlias(); + @KeyByString("tempClientCertAlias") + String getTemporaryClientCertAlias(); - @KeyByString("tempClientCertAlias") - void setTemporaryClientCertAlias(String alias); + @KeyByString("tempClientCertAlias") + void setTemporaryClientCertAlias(String alias); - @KeyByString("tempClientCertAlias") - @RemoveMethod - void removeTemporaryClientCertAlias(); + @KeyByString("tempClientCertAlias") + @RemoveMethod + void removeTemporaryClientCertAlias(); - @KeyByString("pushToTalk_intro_shown") - boolean getPushToTalkIntroShown(); + @KeyByString("pushToTalk_intro_shown") + boolean getPushToTalkIntroShown(); - @KeyByString("pushToTalk_intro_shown") - void setPushToTalkIntroShown(boolean shown); + @KeyByString("pushToTalk_intro_shown") + void setPushToTalkIntroShown(boolean shown); - @KeyByString("pushToTalk_intro_shown") - @RemoveMethod - void removePushToTalkIntroShown(); + @KeyByString("pushToTalk_intro_shown") + @RemoveMethod + void removePushToTalkIntroShown(); - @KeyByString("call_ringtone") - String getCallRingtoneUri(); + @KeyByString("call_ringtone") + String getCallRingtoneUri(); - @KeyByString("call_ringtone") - void setCallRingtoneUri(String value); + @KeyByString("call_ringtone") + void setCallRingtoneUri(String value); - @KeyByString("call_ringtone") - @RemoveMethod - void removeCallRingtoneUri(); + @KeyByString("call_ringtone") + @RemoveMethod + void removeCallRingtoneUri(); - @KeyByString("message_ringtone") - String getMessageRingtoneUri(); + @KeyByString("message_ringtone") + String getMessageRingtoneUri(); - @KeyByString("message_ringtone") - void setMessageRingtoneUri(String value); + @KeyByString("message_ringtone") + void setMessageRingtoneUri(String value); - @KeyByString("message_ringtone") - @RemoveMethod - void removeMessageRingtoneUri(); + @KeyByString("message_ringtone") + @RemoveMethod + void removeMessageRingtoneUri(); - @KeyByString("notification_channels_upgrade_to_v2") - boolean getIsNotificationChannelUpgradedToV2(); + @KeyByString("notification_channels_upgrade_to_v2") + boolean getIsNotificationChannelUpgradedToV2(); - @KeyByString("notification_channels_upgrade_to_v2") - void setNotificationChannelIsUpgradedToV2(boolean value); + @KeyByString("notification_channels_upgrade_to_v2") + void setNotificationChannelIsUpgradedToV2(boolean value); - @KeyByString("notification_channels_upgrade_to_v2") - @RemoveMethod - void removeNotificationChannelUpgradeToV2(); + @KeyByString("notification_channels_upgrade_to_v2") + @RemoveMethod + void removeNotificationChannelUpgradeToV2(); - @KeyByString("notification_channels_upgrade_to_v3") - boolean getIsNotificationChannelUpgradedToV3(); + @KeyByString("notification_channels_upgrade_to_v3") + boolean getIsNotificationChannelUpgradedToV3(); - @KeyByString("notification_channels_upgrade_to_v3") - void setNotificationChannelIsUpgradedToV3(boolean value); + @KeyByString("notification_channels_upgrade_to_v3") + void setNotificationChannelIsUpgradedToV3(boolean value); - @KeyByString("notification_channels_upgrade_to_v3") - @RemoveMethod - void removeNotificationChannelUpgradeToV3(); + @KeyByString("notification_channels_upgrade_to_v3") + @RemoveMethod + void removeNotificationChannelUpgradeToV3(); - @KeyByString("notifications_vibrate") - @DefaultValue(R.bool.value_true) - boolean getShouldVibrateSetting(); + @KeyByString("notifications_vibrate") + @DefaultValue(R.bool.value_true) + boolean getShouldVibrateSetting(); - @KeyByString("notifications_vibrate") - void setVibrateSetting(boolean value); + @KeyByString("notifications_vibrate") + void setVibrateSetting(boolean value); - @KeyByString("notifications_vibrate") - @RemoveMethod - void removeVibrateSetting(); + @KeyByString("notifications_vibrate") + @RemoveMethod + void removeVibrateSetting(); - @KeyByString("screen_security") - @DefaultValue(R.bool.value_false) - boolean getIsScreenSecured(); + @KeyByString("screen_security") + @DefaultValue(R.bool.value_false) + boolean getIsScreenSecured(); - @KeyByString("screen_security") - void setScreenSecurity(boolean value); + @KeyByString("screen_security") + void setScreenSecurity(boolean value); - @KeyByString("screen_security") - @RemoveMethod - void removeScreenSecurity(); + @KeyByString("screen_security") + @RemoveMethod + void removeScreenSecurity(); - @KeyByString("screen_security") - @RegisterChangeListenerMethod - void registerScreenSecurityListener(OnPreferenceValueChangedListener listener); + @KeyByString("screen_security") + @RegisterChangeListenerMethod + void registerScreenSecurityListener(OnPreferenceValueChangedListener listener); - @KeyByString("screen_security") - @UnregisterChangeListenerMethod - void unregisterScreenSecurityListener(OnPreferenceValueChangedListener listener); + @KeyByString("screen_security") + @UnregisterChangeListenerMethod + void unregisterScreenSecurityListener(OnPreferenceValueChangedListener listener); - @KeyByString("screen_lock") - @DefaultValue(R.bool.value_false) - boolean getIsScreenLocked(); + @KeyByString("screen_lock") + @DefaultValue(R.bool.value_false) + boolean getIsScreenLocked(); - @KeyByString("screen_lock") - void setScreenLock(boolean value); + @KeyByString("screen_lock") + void setScreenLock(boolean value); - @KeyByString("screen_lock") - @RemoveMethod - void removeScreenLock(); + @KeyByString("screen_lock") + @RemoveMethod + void removeScreenLock(); - @KeyByString("screen_lock") - @RegisterChangeListenerMethod - void registerScreenLockListener(OnPreferenceValueChangedListener listener); + @KeyByString("screen_lock") + @RegisterChangeListenerMethod + void registerScreenLockListener(OnPreferenceValueChangedListener listener); - @KeyByString("screen_lock") - @UnregisterChangeListenerMethod - void unregisterScreenLockListener(OnPreferenceValueChangedListener listener); + @KeyByString("screen_lock") + @UnregisterChangeListenerMethod + void unregisterScreenLockListener(OnPreferenceValueChangedListener listener); - @KeyByString("incognito_keyboard") - @DefaultValue(R.bool.value_true) - boolean getIsKeyboardIncognito(); + @KeyByString("incognito_keyboard") + @DefaultValue(R.bool.value_true) + boolean getIsKeyboardIncognito(); - @KeyByString("incognito_keyboard") - void setIncognitoKeyboard(boolean value); + @KeyByString("incognito_keyboard") + void setIncognitoKeyboard(boolean value); - @KeyByString("incognito_keyboard") - @RemoveMethod - void removeIncognitoKeyboard(); + @KeyByString("incognito_keyboard") + @RemoveMethod + void removeIncognitoKeyboard(); - @KeyByString("link_previews") - @DefaultValue(R.bool.value_true) - boolean getAreLinkPreviewsAllowed(); + @KeyByString("link_previews") + @DefaultValue(R.bool.value_true) + boolean getAreLinkPreviewsAllowed(); - @KeyByString("link_previews") - void setLinkPreviewsAllowed(boolean value); + @KeyByString("link_previews") + void setLinkPreviewsAllowed(boolean value); - @KeyByString("link_previews") - @RemoveMethod - void removeLinkPreviews(); + @KeyByString("link_previews") + @RemoveMethod + void removeLinkPreviews(); - @KeyByString("screen_lock_timeout") - @DefaultValue(R.string.nc_screen_lock_timeout_sixty) - String getScreenLockTimeout(); + @KeyByString("screen_lock_timeout") + @DefaultValue(R.string.nc_screen_lock_timeout_sixty) + String getScreenLockTimeout(); - @KeyByString("screen_lock_timeout") - void setScreenLockTimeout(int value); + @KeyByString("screen_lock_timeout") + void setScreenLockTimeout(int value); - @KeyByString("screen_lock_timeout") - @RemoveMethod - void removeScreenLockTimeout(); + @KeyByString("screen_lock_timeout") + @RemoveMethod + void removeScreenLockTimeout(); - @KeyByString("screen_lock_timeout") - @RegisterChangeListenerMethod - void registerScreenLockTimeoutListener(OnPreferenceValueChangedListener listener); + @KeyByString("screen_lock_timeout") + @RegisterChangeListenerMethod + void registerScreenLockTimeoutListener(OnPreferenceValueChangedListener listener); - @KeyByString("screen_lock_timeout") - @UnregisterChangeListenerMethod - void unregisterScreenLockTimeoutListener(OnPreferenceValueChangedListener listener); + @KeyByString("screen_lock_timeout") + @UnregisterChangeListenerMethod + void unregisterScreenLockTimeoutListener(OnPreferenceValueChangedListener listener); - @KeyByResource(R.string.nc_settings_theme_key) - @DefaultValue(R.string.nc_default_theme) - String getTheme(); + @KeyByResource(R.string.nc_settings_theme_key) + @DefaultValue(R.string.nc_default_theme) + String getTheme(); - @KeyByResource(R.string.nc_settings_theme_key) - @RemoveMethod - void removeTheme(); + @KeyByResource(R.string.nc_settings_theme_key) + @RemoveMethod + void removeTheme(); - @KeyByResource(R.string.nc_settings_theme_key) - @RegisterChangeListenerMethod - void registerThemeChangeListener(OnPreferenceValueChangedListener listener); + @KeyByResource(R.string.nc_settings_theme_key) + @RegisterChangeListenerMethod + void registerThemeChangeListener(OnPreferenceValueChangedListener listener); - @KeyByResource(R.string.nc_settings_theme_key) - @UnregisterChangeListenerMethod - void unregisterThemeChangeListener(OnPreferenceValueChangedListener listener); + @KeyByResource(R.string.nc_settings_theme_key) + @UnregisterChangeListenerMethod + void unregisterThemeChangeListener(OnPreferenceValueChangedListener listener); - @ClearMethod - void clear(); + @ClearMethod + void clear(); } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/MagicUserInputModule.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/MagicUserInputModule.java index 101ac26fe..4fd4891c3 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/MagicUserInputModule.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/MagicUserInputModule.java @@ -40,55 +40,59 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class MagicUserInputModule extends StandardUserInputModule { - @Inject - AppPreferences appPreferences; + @Inject + AppPreferences appPreferences; - private List keysWithIntegerInput = new ArrayList<>(); + private List keysWithIntegerInput = new ArrayList<>(); - public MagicUserInputModule(Context context) { - super(context); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public MagicUserInputModule(Context context) { + super(context); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } + + public MagicUserInputModule(Context context, List keysWithIntegerInput) { + super(context); + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + this.keysWithIntegerInput = keysWithIntegerInput; + } + + @Override + public void showEditTextInput( + String key, + CharSequence title, + CharSequence defaultValue, + final Listener listener) { + final View view = LayoutInflater.from(context).inflate(R.layout.dialog_edittext, null); + final EditText inputField = view.findViewById(R.id.mp_text_input); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.getIsKeyboardIncognito()) { + inputField.setImeOptions( + inputField.getImeOptions() | EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING); } - public MagicUserInputModule(Context context, List keysWithIntegerInput) { - super(context); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - this.keysWithIntegerInput = keysWithIntegerInput; + if (defaultValue != null) { + inputField.setText(defaultValue); + inputField.setSelection(defaultValue.length()); } - @Override - public void showEditTextInput( - String key, - CharSequence title, - CharSequence defaultValue, - final Listener listener) { - final View view = LayoutInflater.from(context).inflate(R.layout.dialog_edittext, null); - final EditText inputField = view.findViewById(R.id.mp_text_input); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.getIsKeyboardIncognito()) { - inputField.setImeOptions(inputField.getImeOptions() | EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING); - } - - if (defaultValue != null) { - inputField.setText(defaultValue); - inputField.setSelection(defaultValue.length()); - } - - if (keysWithIntegerInput.contains(key)) { - inputField.setInputType(InputType.TYPE_CLASS_NUMBER); - } - - final Dialog dialog = new AlertDialog.Builder(context) - .setTitle(title) - .setView(view) - .show(); - view.findViewById(R.id.mp_btn_confirm).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - listener.onInput(inputField.getText().toString()); - dialog.dismiss(); - } - }); + if (keysWithIntegerInput.contains(key)) { + inputField.setInputType(InputType.TYPE_CLASS_NUMBER); } + final Dialog dialog = new AlertDialog.Builder(context) + .setTitle(title) + .setView(view) + .show(); + view.findViewById(R.id.mp_btn_confirm).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + listener.onInput(inputField.getText().toString()); + dialog.dismiss(); + } + }); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageFactory.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageFactory.java index cfc03eefb..c2769c655 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageFactory.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageFactory.java @@ -25,17 +25,16 @@ import com.nextcloud.talk.models.database.UserEntity; import com.yarolegovich.mp.io.StorageModule; public class DatabaseStorageFactory implements StorageModule.Factory { - private UserEntity conversationUser; - private String conversationToken; + private UserEntity conversationUser; + private String conversationToken; + public DatabaseStorageFactory(UserEntity conversationUser, String conversationToken) { + this.conversationUser = conversationUser; + this.conversationToken = conversationToken; + } - public DatabaseStorageFactory(UserEntity conversationUser, String conversationToken) { - this.conversationUser = conversationUser; - this.conversationToken = conversationToken; - } - - @Override - public StorageModule create(Context context) { - return new DatabaseStorageModule(conversationUser, conversationToken); - } + @Override + public StorageModule create(Context context) { + return new DatabaseStorageModule(conversationUser, conversationToken); + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java index cd97a13ae..86ea8b28c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java @@ -23,11 +23,11 @@ package com.nextcloud.talk.utils.preferences.preferencestorage; import android.os.Bundle; import android.text.TextUtils; import autodagger.AutoInjector; -import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.ArbitraryStorageEntity; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils; import com.yarolegovich.mp.io.StorageModule; @@ -39,149 +39,160 @@ import javax.inject.Inject; @AutoInjector(NextcloudTalkApplication.class) public class DatabaseStorageModule implements StorageModule { - @Inject - ArbitraryStorageUtils arbitraryStorageUtils; + @Inject + ArbitraryStorageUtils arbitraryStorageUtils; - @Inject - NcApi ncApi; + @Inject + NcApi ncApi; - private UserEntity conversationUser; - private String conversationToken; - private long accountIdentifier; + private UserEntity conversationUser; + private String conversationToken; + private long accountIdentifier; - private boolean lobbyValue; + private boolean lobbyValue; - private String messageNotificationLevel; - public DatabaseStorageModule(UserEntity conversationUser, String conversationToken) { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + private String messageNotificationLevel; - this.conversationUser = conversationUser; - this.accountIdentifier = conversationUser.getId(); - this.conversationToken = conversationToken; + public DatabaseStorageModule(UserEntity conversationUser, String conversationToken) { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + + this.conversationUser = conversationUser; + this.accountIdentifier = conversationUser.getId(); + this.conversationToken = conversationToken; + } + + @Override + public void saveBoolean(String key, boolean value) { + if (!key.equals("conversation_lobby")) { + arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, Boolean.toString(value), + conversationToken); + } else { + lobbyValue = value; } + } - @Override - public void saveBoolean(String key, boolean value) { - if (!key.equals("conversation_lobby")) { - arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, Boolean.toString(value), conversationToken); - } else { - lobbyValue = value; - } - } + @Override + public void saveString(String key, String value) { + if (!key.equals("message_notification_level")) { + arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, value, conversationToken); + } else { + if (conversationUser.hasSpreedFeatureCapability("notification-levels")) { + if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals( + value)) { + int intValue; + switch (value) { + case "never": + intValue = 3; + break; + case "mention": + intValue = 2; + break; + case "always": + intValue = 1; + break; + default: + intValue = 0; + } - @Override - public void saveString(String key, String value) { - if (!key.equals("message_notification_level")) { - arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, value, conversationToken); - } else { - if (conversationUser.hasSpreedFeatureCapability("notification-levels")) { - if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(value)) { - int intValue; - switch (value) { - case "never": - intValue = 3; - break; - case "mention": - intValue = 2; - break; - case "always": - intValue = 1; - break; - default: - intValue = 0; - } + ncApi.setNotificationLevel( + ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken()), + ApiUtils.getUrlForSettingNotificationlevel(conversationUser.getBaseUrl(), + conversationToken), + intValue) + .subscribeOn(Schedulers.io()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { - ncApi.setNotificationLevel(ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken()), - ApiUtils.getUrlForSettingNotificationlevel(conversationUser.getBaseUrl(), conversationToken), - intValue) - .subscribeOn(Schedulers.io()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(GenericOverall genericOverall) { - messageNotificationLevel = value; - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - } - }); - } else { - messageNotificationLevel = value; } - } - } - } - @Override - public void saveInt(String key, int value) { - arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, Integer.toString(value), conversationToken); - } + @Override + public void onNext(GenericOverall genericOverall) { + messageNotificationLevel = value; + } - @Override - public void saveStringSet(String key, Set value) { + @Override + public void onError(Throwable e) { - } + } - @Override - public boolean getBoolean(String key, boolean defaultVal) { - if (key.equals("conversation_lobby")) { - return lobbyValue; + @Override + public void onComplete() { + } + }); } else { - ArbitraryStorageEntity valueFromDb = arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken); - if (valueFromDb == null) { - return defaultVal; - } else { - return Boolean.parseBoolean(valueFromDb.getValue()); - } + messageNotificationLevel = value; } + } } + } - @Override - public String getString(String key, String defaultVal) { - if (!key.equals("message_notification_level")) { - ArbitraryStorageEntity valueFromDb = arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken); - if (valueFromDb == null) { - return defaultVal; - } else { - return valueFromDb.getValue(); - } - } else { - return messageNotificationLevel; - } + @Override + public void saveInt(String key, int value) { + arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, Integer.toString(value), + conversationToken); + } + + @Override + public void saveStringSet(String key, Set value) { + + } + + @Override + public boolean getBoolean(String key, boolean defaultVal) { + if (key.equals("conversation_lobby")) { + return lobbyValue; + } else { + ArbitraryStorageEntity valueFromDb = + arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken); + if (valueFromDb == null) { + return defaultVal; + } else { + return Boolean.parseBoolean(valueFromDb.getValue()); + } } + } - @Override - public int getInt(String key, int defaultVal) { - ArbitraryStorageEntity valueFromDb = arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken); - if (valueFromDb == null) { - return defaultVal; - } else { - return Integer.parseInt(valueFromDb.getValue()); - } + @Override + public String getString(String key, String defaultVal) { + if (!key.equals("message_notification_level")) { + ArbitraryStorageEntity valueFromDb = + arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken); + if (valueFromDb == null) { + return defaultVal; + } else { + return valueFromDb.getValue(); + } + } else { + return messageNotificationLevel; } + } - @Override - public Set getStringSet(String key, Set defaultVal) { - return null; + @Override + public int getInt(String key, int defaultVal) { + ArbitraryStorageEntity valueFromDb = + arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken); + if (valueFromDb == null) { + return defaultVal; + } else { + return Integer.parseInt(valueFromDb.getValue()); } + } - @Override - public void onSaveInstanceState(Bundle outState) { + @Override + public Set getStringSet(String key, Set defaultVal) { + return null; + } - } + @Override + public void onSaveInstanceState(Bundle outState) { - @Override - public void onRestoreInstanceState(Bundle savedState) { + } - } + @Override + public void onRestoreInstanceState(Bundle savedState) { + + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideCurrentRoomHolder.java b/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideCurrentRoomHolder.java index 6ce60d386..e1f7563b3 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideCurrentRoomHolder.java +++ b/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideCurrentRoomHolder.java @@ -23,62 +23,63 @@ package com.nextcloud.talk.utils.singletons; import com.nextcloud.talk.models.database.UserEntity; public class ApplicationWideCurrentRoomHolder { - private static final ApplicationWideCurrentRoomHolder holder = new ApplicationWideCurrentRoomHolder(); - private String currentRoomId = ""; - private String currentRoomToken = ""; - private UserEntity userInRoom = new UserEntity(); - private boolean inCall = false; - private String session = ""; + private static final ApplicationWideCurrentRoomHolder holder = + new ApplicationWideCurrentRoomHolder(); + private String currentRoomId = ""; + private String currentRoomToken = ""; + private UserEntity userInRoom = new UserEntity(); + private boolean inCall = false; + private String session = ""; - public static ApplicationWideCurrentRoomHolder getInstance() { - return holder; - } + public static ApplicationWideCurrentRoomHolder getInstance() { + return holder; + } - public void clear() { - currentRoomId = ""; - userInRoom = new UserEntity(); - inCall = false; - currentRoomToken = ""; - session = ""; - } + public void clear() { + currentRoomId = ""; + userInRoom = new UserEntity(); + inCall = false; + currentRoomToken = ""; + session = ""; + } - public String getCurrentRoomToken() { - return currentRoomToken; - } + public String getCurrentRoomToken() { + return currentRoomToken; + } - public void setCurrentRoomToken(String currentRoomToken) { - this.currentRoomToken = currentRoomToken; - } + public void setCurrentRoomToken(String currentRoomToken) { + this.currentRoomToken = currentRoomToken; + } - public String getCurrentRoomId() { - return currentRoomId; - } + public String getCurrentRoomId() { + return currentRoomId; + } - public void setCurrentRoomId(String currentRoomId) { - this.currentRoomId = currentRoomId; - } + public void setCurrentRoomId(String currentRoomId) { + this.currentRoomId = currentRoomId; + } - public UserEntity getUserInRoom() { - return userInRoom; - } + public UserEntity getUserInRoom() { + return userInRoom; + } - public void setUserInRoom(UserEntity userInRoom) { - this.userInRoom = userInRoom; - } + public void setUserInRoom(UserEntity userInRoom) { + this.userInRoom = userInRoom; + } - public boolean isInCall() { - return inCall; - } + public boolean isInCall() { + return inCall; + } - public void setInCall(boolean inCall) { - this.inCall = inCall; - } + public void setInCall(boolean inCall) { + this.inCall = inCall; + } - public String getSession() { - return session; - } + public String getSession() { + return session; + } - public void setSession(String session) { - this.session = session; - } + public void setSession(String session) { + this.session = session; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java b/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java index 60a5899d5..20d173b9e 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java +++ b/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java @@ -23,25 +23,23 @@ package com.nextcloud.talk.utils.singletons; import androidx.annotation.Nullable; public class ApplicationWideMessageHolder { - private static final ApplicationWideMessageHolder holder = new ApplicationWideMessageHolder(); - private MessageType messageType; + private static final ApplicationWideMessageHolder holder = new ApplicationWideMessageHolder(); + private MessageType messageType; - public static ApplicationWideMessageHolder getInstance() { - return holder; - } + public static ApplicationWideMessageHolder getInstance() { + return holder; + } - public MessageType getMessageType() { - return messageType; - } - - public void setMessageType(@Nullable MessageType messageType) { - this.messageType = messageType; - } - - public enum MessageType { - WRONG_ACCOUNT, ACCOUNT_UPDATED_NOT_ADDED, ACCOUNT_SCHEDULED_FOR_DELETION, SERVER_WITHOUT_TALK, - FAILED_TO_IMPORT_ACCOUNT, ACCOUNT_WAS_IMPORTED, CALL_PASSWORD_WRONG - } + public MessageType getMessageType() { + return messageType; + } + public void setMessageType(@Nullable MessageType messageType) { + this.messageType = messageType; + } + public enum MessageType { + WRONG_ACCOUNT, ACCOUNT_UPDATED_NOT_ADDED, ACCOUNT_SCHEDULED_FOR_DELETION, SERVER_WITHOUT_TALK, + FAILED_TO_IMPORT_ACCOUNT, ACCOUNT_WAS_IMPORTED, CALL_PASSWORD_WRONG + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/singletons/AvatarStatusCodeHolder.java b/app/src/main/java/com/nextcloud/talk/utils/singletons/AvatarStatusCodeHolder.java index 25729f79f..8fb4b9eab 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/singletons/AvatarStatusCodeHolder.java +++ b/app/src/main/java/com/nextcloud/talk/utils/singletons/AvatarStatusCodeHolder.java @@ -21,18 +21,18 @@ package com.nextcloud.talk.utils.singletons; public class AvatarStatusCodeHolder { - private static final AvatarStatusCodeHolder holder = new AvatarStatusCodeHolder(); - private int statusCode; + private static final AvatarStatusCodeHolder holder = new AvatarStatusCodeHolder(); + private int statusCode; - public static AvatarStatusCodeHolder getInstance() { - return holder; - } + public static AvatarStatusCodeHolder getInstance() { + return holder; + } - public int getStatusCode() { - return statusCode; - } + public int getStatusCode() { + return statusCode; + } - public void setStatusCode(int statusCode) { - this.statusCode = statusCode; - } + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java b/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java index 43a6bcc65..4ae805faf 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java @@ -42,158 +42,161 @@ import java.util.Set; import javax.net.ssl.X509KeyManager; public class MagicKeyManager implements X509KeyManager { - private static final String TAG = "MagicKeyManager"; - private final X509KeyManager keyManager; + private static final String TAG = "MagicKeyManager"; + private final X509KeyManager keyManager; - private UserUtils userUtils; - private AppPreferences appPreferences; - private Context context; + private UserUtils userUtils; + private AppPreferences appPreferences; + private Context context; - public MagicKeyManager(X509KeyManager keyManager, UserUtils userUtils, AppPreferences appPreferences) { - this.keyManager = keyManager; - this.userUtils = userUtils; - this.appPreferences = appPreferences; + public MagicKeyManager(X509KeyManager keyManager, UserUtils userUtils, + AppPreferences appPreferences) { + this.keyManager = keyManager; + this.userUtils = userUtils; + this.appPreferences = appPreferences; - context = NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); + context = NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext(); + } + + @Override + public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { + String alias; + if (!TextUtils.isEmpty(alias = userUtils.getCurrentUser().getClientCertificate()) || + !TextUtils.isEmpty(alias = appPreferences.getTemporaryClientCertAlias()) + && new ArrayList<>(Arrays.asList(getClientAliases())).contains(alias)) { + return alias; + } + + return null; + } + + @Override + public String chooseServerAlias(String s, Principal[] principals, Socket socket) { + return null; + } + + private X509Certificate[] getCertificatesForAlias(@Nullable String alias) { + if (alias != null) { + GetCertificatesForAliasRunnable getCertificatesForAliasRunnable = + new GetCertificatesForAliasRunnable(alias); + Thread getCertificatesThread = new Thread(getCertificatesForAliasRunnable); + getCertificatesThread.start(); + try { + getCertificatesThread.join(); + return getCertificatesForAliasRunnable.getCertificates(); + } catch (InterruptedException e) { + Log.e(TAG, + "Failed to join the thread while getting certificates: " + e.getLocalizedMessage()); + } + } + + return null; + } + + private PrivateKey getPrivateKeyForAlias(@Nullable String alias) { + if (alias != null) { + GetPrivateKeyForAliasRunnable getPrivateKeyForAliasRunnable = + new GetPrivateKeyForAliasRunnable(alias); + Thread getPrivateKeyThread = new Thread(getPrivateKeyForAliasRunnable); + getPrivateKeyThread.start(); + try { + getPrivateKeyThread.join(); + return getPrivateKeyForAliasRunnable.getPrivateKey(); + } catch (InterruptedException e) { + Log.e(TAG, + "Failed to join the thread while getting private key: " + e.getLocalizedMessage()); + } + } + + return null; + } + + @Override + public X509Certificate[] getCertificateChain(String s) { + if (new ArrayList<>(Arrays.asList(getClientAliases())).contains(s)) { + return getCertificatesForAlias(s); + } + + return null; + } + + private String[] getClientAliases() { + Set aliases = new HashSet<>(); + String alias; + if (!TextUtils.isEmpty(alias = appPreferences.getTemporaryClientCertAlias())) { + aliases.add(alias); + } + + List userEntities = userUtils.getUsers(); + for (int i = 0; i < userEntities.size(); i++) { + if (!TextUtils.isEmpty(alias = userEntities.get(i).getClientCertificate())) { + aliases.add(alias); + } + } + + return aliases.toArray(new String[aliases.size()]); + } + + @Override + public String[] getClientAliases(String s, Principal[] principals) { + return getClientAliases(); + } + + @Override + public String[] getServerAliases(String s, Principal[] principals) { + return null; + } + + @Override + public PrivateKey getPrivateKey(String s) { + if (new ArrayList<>(Arrays.asList(getClientAliases())).contains(s)) { + return getPrivateKeyForAlias(s); + } + + return null; + } + + private class GetCertificatesForAliasRunnable implements Runnable { + private volatile X509Certificate[] certificates; + private String alias; + + public GetCertificatesForAliasRunnable(String alias) { + this.alias = alias; } @Override - public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { - String alias; - if (!TextUtils.isEmpty(alias = userUtils.getCurrentUser().getClientCertificate()) || - !TextUtils.isEmpty(alias = appPreferences.getTemporaryClientCertAlias()) - && new ArrayList<>(Arrays.asList(getClientAliases())).contains(alias)) { - return alias; - } + public void run() { + try { + certificates = KeyChain.getCertificateChain(context, alias); + } catch (KeyChainException | InterruptedException e) { + Log.e(TAG, e.getLocalizedMessage()); + } + } - return null; + public X509Certificate[] getCertificates() { + return certificates; + } + } + + private class GetPrivateKeyForAliasRunnable implements Runnable { + private volatile PrivateKey privateKey; + private String alias; + + public GetPrivateKeyForAliasRunnable(String alias) { + this.alias = alias; } @Override - public String chooseServerAlias(String s, Principal[] principals, Socket socket) { - return null; + public void run() { + try { + privateKey = KeyChain.getPrivateKey(context, alias); + } catch (KeyChainException | InterruptedException e) { + Log.e(TAG, e.getLocalizedMessage()); + } } - private X509Certificate[] getCertificatesForAlias(@Nullable String alias) { - if (alias != null) { - GetCertificatesForAliasRunnable getCertificatesForAliasRunnable = new GetCertificatesForAliasRunnable(alias); - Thread getCertificatesThread = new Thread(getCertificatesForAliasRunnable); - getCertificatesThread.start(); - try { - getCertificatesThread.join(); - return getCertificatesForAliasRunnable.getCertificates(); - } catch (InterruptedException e) { - Log.e(TAG, "Failed to join the thread while getting certificates: " + e.getLocalizedMessage()); - } - } - - return null; + public PrivateKey getPrivateKey() { + return privateKey; } - - private PrivateKey getPrivateKeyForAlias(@Nullable String alias) { - if (alias != null) { - GetPrivateKeyForAliasRunnable getPrivateKeyForAliasRunnable = new GetPrivateKeyForAliasRunnable(alias); - Thread getPrivateKeyThread = new Thread(getPrivateKeyForAliasRunnable); - getPrivateKeyThread.start(); - try { - getPrivateKeyThread.join(); - return getPrivateKeyForAliasRunnable.getPrivateKey(); - } catch (InterruptedException e) { - Log.e(TAG, "Failed to join the thread while getting private key: " + e.getLocalizedMessage()); - } - } - - return null; - } - - - @Override - public X509Certificate[] getCertificateChain(String s) { - if (new ArrayList<>(Arrays.asList(getClientAliases())).contains(s)) { - return getCertificatesForAlias(s); - } - - return null; - } - - private String[] getClientAliases() { - Set aliases = new HashSet<>(); - String alias; - if (!TextUtils.isEmpty(alias = appPreferences.getTemporaryClientCertAlias())) { - aliases.add(alias); - } - - List userEntities = userUtils.getUsers(); - for (int i = 0; i < userEntities.size(); i++) { - if (!TextUtils.isEmpty(alias = userEntities.get(i).getClientCertificate())) { - aliases.add(alias); - } - } - - return aliases.toArray(new String[aliases.size()]); - } - - @Override - public String[] getClientAliases(String s, Principal[] principals) { - return getClientAliases(); - } - - @Override - public String[] getServerAliases(String s, Principal[] principals) { - return null; - } - - @Override - public PrivateKey getPrivateKey(String s) { - if (new ArrayList<>(Arrays.asList(getClientAliases())).contains(s)) { - return getPrivateKeyForAlias(s); - } - - return null; - } - - private class GetCertificatesForAliasRunnable implements Runnable { - private volatile X509Certificate[] certificates; - private String alias; - - public GetCertificatesForAliasRunnable(String alias) { - this.alias = alias; - } - - @Override - public void run() { - try { - certificates = KeyChain.getCertificateChain(context, alias); - } catch (KeyChainException | InterruptedException e) { - Log.e(TAG, e.getLocalizedMessage()); - } - } - - public X509Certificate[] getCertificates() { - return certificates; - } - } - - private class GetPrivateKeyForAliasRunnable implements Runnable { - private volatile PrivateKey privateKey; - private String alias; - - public GetPrivateKeyForAliasRunnable(String alias) { - this.alias = alias; - } - - @Override - public void run() { - try { - privateKey = KeyChain.getPrivateKey(context, alias); - } catch (KeyChainException | InterruptedException e) { - Log.e(TAG, e.getLocalizedMessage()); - } - } - - public PrivateKey getPrivateKey() { - return privateKey; - } - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicTrustManager.java b/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicTrustManager.java index 8264a8ee7..fb20ad8de 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicTrustManager.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicTrustManager.java @@ -41,148 +41,148 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import org.greenrobot.eventbus.EventBus; - public class MagicTrustManager implements X509TrustManager { - private static final String TAG = "MagicTrustManager"; + private static final String TAG = "MagicTrustManager"; - private File keystoreFile; - private X509TrustManager systemTrustManager = null; - private KeyStore trustedKeyStore = null; + private File keystoreFile; + private X509TrustManager systemTrustManager = null; + private KeyStore trustedKeyStore = null; - public MagicTrustManager() { - keystoreFile = new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("CertsKeystore", - Context.MODE_PRIVATE), "keystore.bks"); - - try { - trustedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - FileInputStream fileInputStream = new FileInputStream(keystoreFile); - trustedKeyStore.load(fileInputStream, null); - } catch (Exception exception) { - try { - trustedKeyStore.load(null, null); - } catch (Exception e) { - Log.d(TAG, "Failed to create in-memory key store " + e.getLocalizedMessage()); - } - } - - TrustManagerFactory trustManagerFactory = null; - try { - trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory. - getDefaultAlgorithm()); - - trustManagerFactory.init((KeyStore) null); - - for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { - if (trustManager instanceof X509TrustManager) { - systemTrustManager = (X509TrustManager) trustManager; - break; - } - } - - } catch (Exception exception) { - Log.d(TAG, "Failed to load default trust manager " + exception.getLocalizedMessage()); - } + public MagicTrustManager() { + keystoreFile = + new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("CertsKeystore", + Context.MODE_PRIVATE), "keystore.bks"); + try { + trustedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + FileInputStream fileInputStream = new FileInputStream(keystoreFile); + trustedKeyStore.load(fileInputStream, null); + } catch (Exception exception) { + try { + trustedKeyStore.load(null, null); + } catch (Exception e) { + Log.d(TAG, "Failed to create in-memory key store " + e.getLocalizedMessage()); + } } - public HostnameVerifier getHostnameVerifier(HostnameVerifier defaultHostNameVerifier) { - return new MagicHostnameVerifier(defaultHostNameVerifier); + TrustManagerFactory trustManagerFactory = null; + try { + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory. + getDefaultAlgorithm()); + + trustManagerFactory.init((KeyStore) null); + + for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { + if (trustManager instanceof X509TrustManager) { + systemTrustManager = (X509TrustManager) trustManager; + break; + } + } + } catch (Exception exception) { + Log.d(TAG, "Failed to load default trust manager " + exception.getLocalizedMessage()); + } + } + + public HostnameVerifier getHostnameVerifier(HostnameVerifier defaultHostNameVerifier) { + return new MagicHostnameVerifier(defaultHostNameVerifier); + } + + private boolean isCertInTrustStore(X509Certificate[] x509Certificates, String s) { + if (systemTrustManager != null) { + X509Certificate x509Certificate = x509Certificates[0]; + try { + systemTrustManager.checkServerTrusted(x509Certificates, s); + return true; + } catch (CertificateException e) { + if (!isCertInMagicTrustStore(x509Certificate)) { + EventBus.getDefault().post(new CertificateEvent(x509Certificate, this, + null)); + long startTime = System.currentTimeMillis(); + while (!isCertInMagicTrustStore(x509Certificate) && System.currentTimeMillis() <= + startTime + 15000) { + //do nothing + } + return isCertInMagicTrustStore(x509Certificate); + } else { + return true; + } + } } - private boolean isCertInTrustStore(X509Certificate[] x509Certificates, String s) { - if (systemTrustManager != null) { - X509Certificate x509Certificate = x509Certificates[0]; - try { - systemTrustManager.checkServerTrusted(x509Certificates, s); - return true; - } catch (CertificateException e) { - if (!isCertInMagicTrustStore(x509Certificate)) { - EventBus.getDefault().post(new CertificateEvent(x509Certificate, this, - null)); - long startTime = System.currentTimeMillis(); - while (!isCertInMagicTrustStore(x509Certificate) && System.currentTimeMillis() <= - startTime + 15000) { - //do nothing - } - return isCertInMagicTrustStore(x509Certificate); - } else { - return true; - } - } - } + return false; + } + private boolean isCertInMagicTrustStore(X509Certificate x509Certificate) { + if (trustedKeyStore != null) { + try { + if (trustedKeyStore.getCertificateAlias(x509Certificate) != null) { + return true; + } + } catch (KeyStoreException exception) { return false; + } } - private boolean isCertInMagicTrustStore(X509Certificate x509Certificate) { - if (trustedKeyStore != null) { - try { - if (trustedKeyStore.getCertificateAlias(x509Certificate) != null) { - return true; - } - } catch (KeyStoreException exception) { - return false; - } - } + return false; + } - return false; + public void addCertInTrustStore(X509Certificate x509Certificate) { + if (trustedKeyStore != null) { + try { + trustedKeyStore.setCertificateEntry(x509Certificate.getSubjectDN().getName(), + x509Certificate); + FileOutputStream fileOutputStream = new FileOutputStream(keystoreFile); + trustedKeyStore.store(fileOutputStream, null); + } catch (Exception exception) { + Log.d(TAG, "Failed to set certificate entry " + exception.getLocalizedMessage()); + } } + } - public void addCertInTrustStore(X509Certificate x509Certificate) { - if (trustedKeyStore != null) { - try { - trustedKeyStore.setCertificateEntry(x509Certificate.getSubjectDN().getName(), x509Certificate); - FileOutputStream fileOutputStream = new FileOutputStream(keystoreFile); - trustedKeyStore.store(fileOutputStream, null); - } catch (Exception exception) { - Log.d(TAG, "Failed to set certificate entry " + exception.getLocalizedMessage()); - } - } + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + systemTrustManager.checkClientTrusted(x509Certificates, s); + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + if (!isCertInTrustStore(x509Certificates, s)) { + throw new CertificateException(); + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return systemTrustManager.getAcceptedIssuers(); + } + + private class MagicHostnameVerifier implements HostnameVerifier { + private static final String TAG = "MagicHostnameVerifier"; + private HostnameVerifier defaultHostNameVerifier; + + private MagicHostnameVerifier(HostnameVerifier defaultHostNameVerifier) { + this.defaultHostNameVerifier = defaultHostNameVerifier; } @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - systemTrustManager.checkClientTrusted(x509Certificates, s); - } + public boolean verify(String s, SSLSession sslSession) { - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - if (!isCertInTrustStore(x509Certificates, s)) { - throw new CertificateException(); + if (defaultHostNameVerifier.verify(s, sslSession)) { + return true; + } + + try { + X509Certificate[] certificates = (X509Certificate[]) sslSession.getPeerCertificates(); + if (certificates.length > 0 && isCertInTrustStore(certificates, s)) { + return true; } + } catch (SSLPeerUnverifiedException e) { + Log.d(TAG, "Couldn't get certificate for host name verification"); + } + + return false; } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return systemTrustManager.getAcceptedIssuers(); - } - - private class MagicHostnameVerifier implements HostnameVerifier { - private static final String TAG = "MagicHostnameVerifier"; - private HostnameVerifier defaultHostNameVerifier; - - private MagicHostnameVerifier(HostnameVerifier defaultHostNameVerifier) { - this.defaultHostNameVerifier = defaultHostNameVerifier; - } - - @Override - public boolean verify(String s, SSLSession sslSession) { - - if (defaultHostNameVerifier.verify(s, sslSession)) { - return true; - } - - try { - X509Certificate[] certificates = (X509Certificate[]) sslSession.getPeerCertificates(); - if (certificates.length > 0 && isCertInTrustStore(certificates, s)) { - return true; - } - } catch (SSLPeerUnverifiedException e) { - Log.d(TAG, "Couldn't get certificate for host name verification"); - } - - return false; - } - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt b/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt index 6d42f0755..f631404c1 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt @@ -20,144 +20,166 @@ import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager -class SSLSocketFactoryCompat(keyManager: KeyManager?, - trustManager: X509TrustManager) : SSLSocketFactory() { +class SSLSocketFactoryCompat( + keyManager: KeyManager?, + trustManager: X509TrustManager +) : SSLSocketFactory() { - private var delegate: SSLSocketFactory - - companion object { - // Android 5.0+ (API level 21) provides reasonable default settings - // but it still allows SSLv3 - // https://developer.android.com/reference/javax/net/ssl/SSLSocket.html - var protocols: Array? = null - var cipherSuites: Array? = null - - init { - if (Build.VERSION.SDK_INT >= 23) { - // Since Android 6.0 (API level 23), - // - TLSv1.1 and TLSv1.2 is enabled by default - // - SSLv3 is disabled by default - // - all modern ciphers are activated by default - protocols = null - cipherSuites = null - } else { - val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket? - try { - socket?.let { - /* set reasonable protocol versions */ - // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0) - // - remove all SSL versions (especially SSLv3) because they're insecure now - val _protocols = LinkedList() - for (protocol in socket.supportedProtocols.filterNot { it.contains("SSL", true) }) - _protocols += protocol - protocols = _protocols.toTypedArray() - - /* set up reasonable cipher suites */ - val knownCiphers = arrayOf( - // TLS 1.2 - "TLS_RSA_WITH_AES_256_GCM_SHA384", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - // maximum interoperability - "TLS_RSA_WITH_3DES_EDE_CBC_SHA", - "SSL_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_RSA_WITH_AES_128_CBC_SHA", - // additionally - "TLS_RSA_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" - ) - val availableCiphers = socket.supportedCipherSuites - - /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus - * disabling ciphers which are enabled by default, but have become unsecure), but for - * the security level of DAVdroid and maximum compatibility, disabling of insecure - * ciphers should be a server-side task */ - - // for the final set of enabled ciphers, take the ciphers enabled by default, ... - val _cipherSuites = LinkedList() - _cipherSuites.addAll(socket.enabledCipherSuites) - // ... add explicitly allowed ciphers ... - _cipherSuites.addAll(knownCiphers) - // ... and keep only those which are actually available - _cipherSuites.retainAll(availableCiphers) - - cipherSuites = _cipherSuites.toTypedArray() - } - } catch (e: IOException) { - // Exception is to be ignored - } finally { - socket?.close() // doesn't implement Closeable on all supported Android versions - } - } - } - } + private var delegate: SSLSocketFactory + companion object { + // Android 5.0+ (API level 21) provides reasonable default settings + // but it still allows SSLv3 + // https://developer.android.com/reference/javax/net/ssl/SSLSocket.html + var protocols: Array? = null + var cipherSuites: Array? = null init { + if (Build.VERSION.SDK_INT >= 23) { + // Since Android 6.0 (API level 23), + // - TLSv1.1 and TLSv1.2 is enabled by default + // - SSLv3 is disabled by default + // - all modern ciphers are activated by default + protocols = null + cipherSuites = null + } else { + val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket? try { - val sslContext = SSLContext.getInstance("TLS") - sslContext.init( - if (keyManager != null) arrayOf(keyManager) else null, - arrayOf(trustManager), - null) - delegate = sslContext.socketFactory - } catch (e: GeneralSecurityException) { - throw IllegalStateException() // system has no TLS + socket?.let { + /* set reasonable protocol versions */ + // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0) + // - remove all SSL versions (especially SSLv3) because they're insecure now + val _protocols = LinkedList() + for (protocol in socket.supportedProtocols.filterNot { it.contains("SSL", true) }) + _protocols += protocol + protocols = _protocols.toTypedArray() + + /* set up reasonable cipher suites */ + val knownCiphers = arrayOf( + // TLS 1.2 + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + // maximum interoperability + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "SSL_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + // additionally + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" + ) + val availableCiphers = socket.supportedCipherSuites + + /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus + * disabling ciphers which are enabled by default, but have become unsecure), but for + * the security level of DAVdroid and maximum compatibility, disabling of insecure + * ciphers should be a server-side task */ + + // for the final set of enabled ciphers, take the ciphers enabled by default, ... + val _cipherSuites = LinkedList() + _cipherSuites.addAll(socket.enabledCipherSuites) + // ... add explicitly allowed ciphers ... + _cipherSuites.addAll(knownCiphers) + // ... and keep only those which are actually available + _cipherSuites.retainAll(availableCiphers) + + cipherSuites = _cipherSuites.toTypedArray() + } + } catch (e: IOException) { + // Exception is to be ignored + } finally { + socket?.close() // doesn't implement Closeable on all supported Android versions } + } } + } - override fun getDefaultCipherSuites(): Array? = cipherSuites - ?: delegate.defaultCipherSuites - - override fun getSupportedCipherSuites(): Array? = cipherSuites - ?: delegate.supportedCipherSuites - - override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket { - val ssl = delegate.createSocket(s, host, port, autoClose) - if (ssl is SSLSocket) - upgradeTLS(ssl) - return ssl + init { + try { + val sslContext = SSLContext.getInstance("TLS") + sslContext.init( + if (keyManager != null) arrayOf(keyManager) else null, + arrayOf(trustManager), + null + ) + delegate = sslContext.socketFactory + } catch (e: GeneralSecurityException) { + throw IllegalStateException() // system has no TLS } + } - override fun createSocket(host: String, port: Int): Socket { - val ssl = delegate.createSocket(host, port) - if (ssl is SSLSocket) - upgradeTLS(ssl) - return ssl - } + override fun getDefaultCipherSuites(): Array? = cipherSuites + ?: delegate.defaultCipherSuites - override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket { - val ssl = delegate.createSocket(host, port, localHost, localPort) - if (ssl is SSLSocket) - upgradeTLS(ssl) - return ssl - } + override fun getSupportedCipherSuites(): Array? = cipherSuites + ?: delegate.supportedCipherSuites - override fun createSocket(host: InetAddress, port: Int): Socket { - val ssl = delegate.createSocket(host, port) - if (ssl is SSLSocket) - upgradeTLS(ssl) - return ssl - } + override fun createSocket( + s: Socket, + host: String, + port: Int, + autoClose: Boolean + ): Socket { + val ssl = delegate.createSocket(s, host, port, autoClose) + if (ssl is SSLSocket) + upgradeTLS(ssl) + return ssl + } - override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket { - val ssl = delegate.createSocket(address, port, localAddress, localPort) - if (ssl is SSLSocket) - upgradeTLS(ssl) - return ssl - } + override fun createSocket( + host: String, + port: Int + ): Socket { + val ssl = delegate.createSocket(host, port) + if (ssl is SSLSocket) + upgradeTLS(ssl) + return ssl + } + override fun createSocket( + host: String, + port: Int, + localHost: InetAddress, + localPort: Int + ): Socket { + val ssl = delegate.createSocket(host, port, localHost, localPort) + if (ssl is SSLSocket) + upgradeTLS(ssl) + return ssl + } - private fun upgradeTLS(ssl: SSLSocket) { - protocols?.let { ssl.enabledProtocols = it } - cipherSuites?.let { ssl.enabledCipherSuites = it } - } + override fun createSocket( + host: InetAddress, + port: Int + ): Socket { + val ssl = delegate.createSocket(host, port) + if (ssl is SSLSocket) + upgradeTLS(ssl) + return ssl + } + + override fun createSocket( + address: InetAddress, + port: Int, + localAddress: InetAddress, + localPort: Int + ): Socket { + val ssl = delegate.createSocket(address, port, localAddress, localPort) + if (ssl is SSLSocket) + upgradeTLS(ssl) + return ssl + } + + private fun upgradeTLS(ssl: SSLSocket) { + protocols?.let { ssl.enabledProtocols = it } + cipherSuites?.let { ssl.enabledCipherSuites = it } + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/text/Spans.java b/app/src/main/java/com/nextcloud/talk/utils/text/Spans.java index 7fea458e6..f41ec01ce 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/text/Spans.java +++ b/app/src/main/java/com/nextcloud/talk/utils/text/Spans.java @@ -27,16 +27,16 @@ import lombok.Data; public class Spans { - @Data - public static class MentionChipSpan extends BetterImageSpan { - public String id; - public CharSequence label; + @Data + public static class MentionChipSpan extends BetterImageSpan { + public String id; + public CharSequence label; - public MentionChipSpan(@NonNull Drawable drawable, int verticalAlignment, String id, CharSequence label) { - super(drawable, verticalAlignment); - this.id = id; - this.label = label; - } + public MentionChipSpan(@NonNull Drawable drawable, int verticalAlignment, String id, + CharSequence label) { + super(drawable, verticalAlignment); + this.id = id; + this.label = label; } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ui/MaterialPreferenceCategoryWithRightLink.java b/app/src/main/java/com/nextcloud/talk/utils/ui/MaterialPreferenceCategoryWithRightLink.java index 7d99a1f22..e0f173d3d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ui/MaterialPreferenceCategoryWithRightLink.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ui/MaterialPreferenceCategoryWithRightLink.java @@ -37,127 +37,128 @@ import com.yarolegovich.mp.util.Utils; public class MaterialPreferenceCategoryWithRightLink extends CardView { - private ViewGroup container; - private TextView title; - private TextView action; + private ViewGroup container; + private TextView title; + private TextView action; - public MaterialPreferenceCategoryWithRightLink(Context context) { - super(context); - init(null); - } + public MaterialPreferenceCategoryWithRightLink(Context context) { + super(context); + init(null); + } - public MaterialPreferenceCategoryWithRightLink(Context context, AttributeSet attrs) { - super(context, attrs); - init(attrs); - } + public MaterialPreferenceCategoryWithRightLink(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } - public MaterialPreferenceCategoryWithRightLink(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(attrs); - } + public MaterialPreferenceCategoryWithRightLink(Context context, AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } - private void init(AttributeSet attrs) { - int titleColor = -1; - String titleText = ""; - String actionText = ""; - if (attrs != null) { - TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.MaterialPreferenceCategory); - try { - if (ta.hasValue(R.styleable.MaterialPreferenceCategory_mpc_title)) { - titleText = ta.getString(R.styleable.MaterialPreferenceCategory_mpc_title); - } - - if (ta.hasValue(R.styleable.MaterialPreferenceCategory_mpc_action)) { - actionText = ta.getString(R.styleable.MaterialPreferenceCategory_mpc_action); - } - - titleColor = ta.getColor(R.styleable.MaterialPreferenceCategory_mpc_title_color, -1); - } finally { - ta.recycle(); - } + private void init(AttributeSet attrs) { + int titleColor = -1; + String titleText = ""; + String actionText = ""; + if (attrs != null) { + TypedArray ta = + getContext().obtainStyledAttributes(attrs, R.styleable.MaterialPreferenceCategory); + try { + if (ta.hasValue(R.styleable.MaterialPreferenceCategory_mpc_title)) { + titleText = ta.getString(R.styleable.MaterialPreferenceCategory_mpc_title); } - inflate(getContext(), R.layout.category_with_right_action, this); - - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - params.setMargins(0, 0, 0, Utils.dpToPixels(getContext(), 4)); - - setUseCompatPadding(true); - - setRadius(0); - - container = findViewById(R.id.mpc_container); - title = findViewById(R.id.mpc_title); - action = findViewById(R.id.mpc_action); - - if (!TextUtils.isEmpty(titleText)) { - title.setVisibility(View.VISIBLE); - title.setText(titleText); + if (ta.hasValue(R.styleable.MaterialPreferenceCategory_mpc_action)) { + actionText = ta.getString(R.styleable.MaterialPreferenceCategory_mpc_action); } - if (!TextUtils.isEmpty(actionText)) { - action.setVisibility(View.VISIBLE); - action.setText(actionText); - } - - if (titleColor != -1) { - title.setTextColor(titleColor); - } + titleColor = ta.getColor(R.styleable.MaterialPreferenceCategory_mpc_title_color, -1); + } finally { + ta.recycle(); + } } - public void setAction(String actionText) { - action.setText(actionText); + inflate(getContext(), R.layout.category_with_right_action, this); + + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + params.setMargins(0, 0, 0, Utils.dpToPixels(getContext(), 4)); + + setUseCompatPadding(true); + + setRadius(0); + + container = findViewById(R.id.mpc_container); + title = findViewById(R.id.mpc_title); + action = findViewById(R.id.mpc_action); + + if (!TextUtils.isEmpty(titleText)) { + title.setVisibility(View.VISIBLE); + title.setText(titleText); } - public void setTitle(String titleText) { - title.setVisibility(View.VISIBLE); - title.setText(titleText); + if (!TextUtils.isEmpty(actionText)) { + action.setVisibility(View.VISIBLE); + action.setText(actionText); } - public void setTitleColor(@ColorInt int color) { - title.setTextColor(color); + if (titleColor != -1) { + title.setTextColor(titleColor); } + } - public void setTitleColorRes(@ColorRes int colorRes) { - title.setTextColor(ContextCompat.getColor(getContext(), colorRes)); + public void setAction(String actionText) { + action.setText(actionText); + } + + public void setTitle(String titleText) { + title.setVisibility(View.VISIBLE); + title.setText(titleText); + } + + public void setTitleColor(@ColorInt int color) { + title.setTextColor(color); + } + + public void setTitleColorRes(@ColorRes int colorRes) { + title.setTextColor(ContextCompat.getColor(getContext(), colorRes)); + } + + @Override + public void addView(View child) { + if (container != null) { + container.addView(child); + } else { + super.addView(child); } + } - @Override - public void addView(View child) { - if (container != null) { - container.addView(child); - } else { - super.addView(child); - } + @Override + public void addView(View child, int index) { + if (container != null) { + container.addView(child, index); + } else { + super.addView(child, index); } + } - @Override - public void addView(View child, int index) { - if (container != null) { - container.addView(child, index); - } else { - super.addView(child, index); - } + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (container != null) { + container.addView(child, params); + } else { + super.addView(child, params); } + } - @Override - public void addView(View child, ViewGroup.LayoutParams params) { - if (container != null) { - container.addView(child, params); - } else { - super.addView(child, params); - } + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (container != null) { + container.addView(child, index, params); + } else { + super.addView(child, index, params); } - - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { - if (container != null) { - container.addView(child, index, params); - } else { - super.addView(child, index, params); - } - } - + } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicAudioManager.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicAudioManager.java index 4aef56ee4..0ff42314c 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicAudioManager.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicAudioManager.java @@ -52,606 +52,605 @@ import org.webrtc.ThreadUtils; * MagicAudioManager manages all audio related parts of the AppRTC demo. */ public class MagicAudioManager { - private static final String TAG = "MagicAudioManager"; - private static final String SPEAKERPHONE_AUTO = "auto"; - private static final String SPEAKERPHONE_FALSE = "false"; - private final Context magicContext; - // Handles all tasks related to Bluetooth headset devices. - private final MagicBluetoothManager bluetoothManager; - // Contains speakerphone setting: auto, true or false - private String useSpeakerphone; - private AudioManager audioManager; - private AudioManagerEvents audioManagerEvents; - private AudioManagerState amState; - private int savedAudioMode = AudioManager.MODE_INVALID; - private boolean savedIsSpeakerPhoneOn = false; - private boolean savedIsMicrophoneMute = false; - private boolean hasWiredHeadset = false; - // Default audio device; speaker phone for video calls or earpiece for audio - // only calls. - private AudioDevice defaultAudioDevice; - // Contains the currently selected audio device. - // This device is changed automatically using a certain scheme where e.g. - // a wired headset "wins" over speaker phone. It is also possible for a - // user to explicitly select a device (and overrid any predefined scheme). - // See |userSelectedAudioDevice| for details. - private AudioDevice selectedAudioDevice; - // Contains the user-selected audio device which overrides the predefined - // selection scheme. - // TODO(henrika): always set to AudioDevice.NONE today. Add support for - // explicit selection based on choice by userSelectedAudioDevice. - private AudioDevice userSelectedAudioDevice; - // Proximity sensor object. It measures the proximity of an object in cm - // relative to the view screen of a device and can therefore be used to - // assist device switching (close to ear <=> use headset earpiece if - // available, far from ear <=> use speaker phone). - private MagicProximitySensor proximitySensor = null; - // Contains a list of available audio devices. A Set collection is used to - // avoid duplicate elements. - private Set audioDevices = new HashSet<>(); - // Broadcast receiver for wired headset intent broadcasts. - private BroadcastReceiver wiredHeadsetReceiver; - // Callback method for changes in audio focus. - private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; + private static final String TAG = "MagicAudioManager"; + private static final String SPEAKERPHONE_AUTO = "auto"; + private static final String SPEAKERPHONE_FALSE = "false"; + private final Context magicContext; + // Handles all tasks related to Bluetooth headset devices. + private final MagicBluetoothManager bluetoothManager; + // Contains speakerphone setting: auto, true or false + private String useSpeakerphone; + private AudioManager audioManager; + private AudioManagerEvents audioManagerEvents; + private AudioManagerState amState; + private int savedAudioMode = AudioManager.MODE_INVALID; + private boolean savedIsSpeakerPhoneOn = false; + private boolean savedIsMicrophoneMute = false; + private boolean hasWiredHeadset = false; + // Default audio device; speaker phone for video calls or earpiece for audio + // only calls. + private AudioDevice defaultAudioDevice; + // Contains the currently selected audio device. + // This device is changed automatically using a certain scheme where e.g. + // a wired headset "wins" over speaker phone. It is also possible for a + // user to explicitly select a device (and overrid any predefined scheme). + // See |userSelectedAudioDevice| for details. + private AudioDevice selectedAudioDevice; + // Contains the user-selected audio device which overrides the predefined + // selection scheme. + // TODO(henrika): always set to AudioDevice.NONE today. Add support for + // explicit selection based on choice by userSelectedAudioDevice. + private AudioDevice userSelectedAudioDevice; + // Proximity sensor object. It measures the proximity of an object in cm + // relative to the view screen of a device and can therefore be used to + // assist device switching (close to ear <=> use headset earpiece if + // available, far from ear <=> use speaker phone). + private MagicProximitySensor proximitySensor = null; + // Contains a list of available audio devices. A Set collection is used to + // avoid duplicate elements. + private Set audioDevices = new HashSet<>(); + // Broadcast receiver for wired headset intent broadcasts. + private BroadcastReceiver wiredHeadsetReceiver; + // Callback method for changes in audio focus. + private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; - private PowerManagerUtils powerManagerUtils; + private PowerManagerUtils powerManagerUtils; - private MagicAudioManager(Context context, boolean useProximitySensor) { - Log.d(TAG, "ctor"); - ThreadUtils.checkIsOnMainThread(); - magicContext = context; - audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)); - bluetoothManager = MagicBluetoothManager.create(context, this); - wiredHeadsetReceiver = new WiredHeadsetReceiver(); - amState = AudioManagerState.UNINITIALIZED; + private MagicAudioManager(Context context, boolean useProximitySensor) { + Log.d(TAG, "ctor"); + ThreadUtils.checkIsOnMainThread(); + magicContext = context; + audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)); + bluetoothManager = MagicBluetoothManager.create(context, this); + wiredHeadsetReceiver = new WiredHeadsetReceiver(); + amState = AudioManagerState.UNINITIALIZED; - powerManagerUtils = new PowerManagerUtils(); - powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITH_PROXIMITY_SENSOR_LOCK); + powerManagerUtils = new PowerManagerUtils(); + powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.WITH_PROXIMITY_SENSOR_LOCK); - if (useProximitySensor) { - useSpeakerphone = SPEAKERPHONE_AUTO; - } else { - useSpeakerphone = SPEAKERPHONE_FALSE; - } - - - if (useSpeakerphone.equals(SPEAKERPHONE_FALSE)) { - defaultAudioDevice = AudioDevice.EARPIECE; - } else { - defaultAudioDevice = AudioDevice.SPEAKER_PHONE; - } - - // Create and initialize the proximity sensor. - // Tablet devices (e.g. Nexus 7) does not support proximity sensors. - // Note that, the sensor will not be active until start() has been called. - proximitySensor = MagicProximitySensor.create(context, new Runnable() { - // This method will be called each time a viewState change is detected. - // Example: user holds his hand over the device (closer than ~5 cm), - // or removes his hand from the device. - public void run() { - onProximitySensorChangedState(); - } - }); - - Log.d(TAG, "defaultAudioDevice: " + defaultAudioDevice); + if (useProximitySensor) { + useSpeakerphone = SPEAKERPHONE_AUTO; + } else { + useSpeakerphone = SPEAKERPHONE_FALSE; } - /** - * Construction. - */ - public static MagicAudioManager create(Context context, boolean useProximitySensor) { - return new MagicAudioManager(context, useProximitySensor); + if (useSpeakerphone.equals(SPEAKERPHONE_FALSE)) { + defaultAudioDevice = AudioDevice.EARPIECE; + } else { + defaultAudioDevice = AudioDevice.SPEAKER_PHONE; } - public void toggleUseSpeakerphone() { - if (useSpeakerphone.equals(SPEAKERPHONE_FALSE)) { - useSpeakerphone = SPEAKERPHONE_AUTO; - setDefaultAudioDevice(AudioDevice.SPEAKER_PHONE); - } else { - useSpeakerphone = SPEAKERPHONE_FALSE; - setDefaultAudioDevice(AudioDevice.EARPIECE); - } + // Create and initialize the proximity sensor. + // Tablet devices (e.g. Nexus 7) does not support proximity sensors. + // Note that, the sensor will not be active until start() has been called. + proximitySensor = MagicProximitySensor.create(context, new Runnable() { + // This method will be called each time a viewState change is detected. + // Example: user holds his hand over the device (closer than ~5 cm), + // or removes his hand from the device. + public void run() { + onProximitySensorChangedState(); + } + }); - updateAudioDeviceState(); - } - - public boolean isSpeakerphoneAutoOn() { - return (useSpeakerphone.equals(SPEAKERPHONE_AUTO)); - } - - /** - * This method is called when the proximity sensor reports a viewState change, - * e.g. from "NEAR to FAR" or from "FAR to NEAR". - */ - private void onProximitySensorChangedState() { - - if (!useSpeakerphone.equals(SPEAKERPHONE_AUTO)) { - return; - } - - // The proximity sensor should only be activated when there are exactly two - // available audio devices. - if (audioDevices.size() == 2 && audioDevices.contains(MagicAudioManager.AudioDevice.EARPIECE) - && audioDevices.contains(MagicAudioManager.AudioDevice.SPEAKER_PHONE)) { - if (proximitySensor.sensorReportsNearState()) { - // Sensor reports that a "handset is being held up to a person's ear", - // or "something is covering the light sensor". - setAudioDeviceInternal(MagicAudioManager.AudioDevice.EARPIECE); - - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .SENSOR_NEAR, null, null, null, null)); - - } else { - // Sensor reports that a "handset is removed from a person's ear", or - // "the light sensor is no longer covered". - setAudioDeviceInternal(MagicAudioManager.AudioDevice.SPEAKER_PHONE); - - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .SENSOR_FAR, null, null, null, null)); - } - } - } - - public void start(AudioManagerEvents audioManagerEvents) { - Log.d(TAG, "start"); - ThreadUtils.checkIsOnMainThread(); - if (amState == AudioManagerState.RUNNING) { - Log.e(TAG, "AudioManager is already active"); - return; - } - // TODO(henrika): perhaps call new method called preInitAudio() here if UNINITIALIZED. - - Log.d(TAG, "AudioManager starts..."); - this.audioManagerEvents = audioManagerEvents; - amState = AudioManagerState.RUNNING; - - // Store current audio viewState so we can restore it when stop() is called. - savedAudioMode = audioManager.getMode(); - savedIsSpeakerPhoneOn = audioManager.isSpeakerphoneOn(); - savedIsMicrophoneMute = audioManager.isMicrophoneMute(); - hasWiredHeadset = hasWiredHeadset(); - - // Create an AudioManager.OnAudioFocusChangeListener instance. - audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { - // Called on the listener to notify if the audio focus for this listener has been changed. - // The |focusChange| value indicates whether the focus was gained, whether the focus was lost, - // and whether that loss is transient, or whether the new focus holder will hold it for an - // unknown amount of time. - // TODO(henrika): possibly extend support of handling audio-focus changes. Only contains - // logging for now. - @Override - public void onAudioFocusChange(int focusChange) { - String typeOfChange = "AUDIOFOCUS_NOT_DEFINED"; - switch (focusChange) { - case AudioManager.AUDIOFOCUS_GAIN: - typeOfChange = "AUDIOFOCUS_GAIN"; - break; - case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: - typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT"; - break; - case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: - typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE"; - break; - case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: - typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"; - break; - case AudioManager.AUDIOFOCUS_LOSS: - typeOfChange = "AUDIOFOCUS_LOSS"; - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT"; - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: - typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"; - break; - default: - typeOfChange = "AUDIOFOCUS_INVALID"; - break; - } - Log.d(TAG, "onAudioFocusChange: " + typeOfChange); - } - }; - - // Request audio playout focus (without ducking) and install listener for changes in focus. - int result = audioManager.requestAudioFocus(audioFocusChangeListener, - AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); - if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - Log.d(TAG, "Audio focus request granted for VOICE_CALL streams"); - } else { - Log.e(TAG, "Audio focus request failed"); - } - - // Start by setting MODE_IN_COMMUNICATION as default audio mode. It is - // required to be in this mode when playout and/or recording starts for - // best possible VoIP performance. - audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); - - // Always disable microphone mute during a WebRTC call. - setMicrophoneMute(false); - - // Set initial device states. - userSelectedAudioDevice = AudioDevice.NONE; - selectedAudioDevice = AudioDevice.NONE; - audioDevices.clear(); - - // Initialize and start Bluetooth if a BT device is available or initiate - // detection of new (enabled) BT devices. - bluetoothManager.start(); - - // Do initial selection of audio device. This setting can later be changed - // either by adding/removing a BT or wired headset or by covering/uncovering - // the proximity sensor. - updateAudioDeviceState(); - - proximitySensor.start(); - // Register receiver for broadcast intents related to adding/removing a - // wired headset. - registerReceiver(wiredHeadsetReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); - Log.d(TAG, "AudioManager started"); - } - - public void stop() { - Log.d(TAG, "stop"); - ThreadUtils.checkIsOnMainThread(); - if (amState != AudioManagerState.RUNNING) { - Log.e(TAG, "Trying to stop AudioManager in incorrect viewState: " + amState); - return; - } - amState = AudioManagerState.UNINITIALIZED; - - unregisterReceiver(wiredHeadsetReceiver); - - bluetoothManager.stop(); - - // Restore previously stored audio states. - setSpeakerphoneOn(savedIsSpeakerPhoneOn); - setMicrophoneMute(savedIsMicrophoneMute); - audioManager.setMode(savedAudioMode); - - // Abandon audio focus. Gives the previous focus owner, if any, focus. - audioManager.abandonAudioFocus(audioFocusChangeListener); - audioFocusChangeListener = null; - Log.d(TAG, "Abandoned audio focus for VOICE_CALL streams"); - - if (proximitySensor != null) { - proximitySensor.stop(); - proximitySensor = null; - } - - powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.IDLE); - - audioManagerEvents = null; - Log.d(TAG, "AudioManager stopped"); - } + Log.d(TAG, "defaultAudioDevice: " + defaultAudioDevice); + } /** - * Changes selection of the currently active audio device. - */ - private void setAudioDeviceInternal(AudioDevice device) { - Log.d(TAG, "setAudioDeviceInternal(device=" + device + ")"); + * Construction. + */ + public static MagicAudioManager create(Context context, boolean useProximitySensor) { + return new MagicAudioManager(context, useProximitySensor); + } - if (audioDevices.contains(device)) { + public void toggleUseSpeakerphone() { + if (useSpeakerphone.equals(SPEAKERPHONE_FALSE)) { + useSpeakerphone = SPEAKERPHONE_AUTO; + setDefaultAudioDevice(AudioDevice.SPEAKER_PHONE); + } else { + useSpeakerphone = SPEAKERPHONE_FALSE; + setDefaultAudioDevice(AudioDevice.EARPIECE); + } - switch (device) { - case SPEAKER_PHONE: - setSpeakerphoneOn(true); - break; - case EARPIECE: - setSpeakerphoneOn(false); - break; - case WIRED_HEADSET: - setSpeakerphoneOn(false); - break; - case BLUETOOTH: - setSpeakerphoneOn(false); - break; - default: - Log.e(TAG, "Invalid audio device selection"); - break; - } - selectedAudioDevice = device; + updateAudioDeviceState(); + } + + public boolean isSpeakerphoneAutoOn() { + return (useSpeakerphone.equals(SPEAKERPHONE_AUTO)); + } + + /** + * This method is called when the proximity sensor reports a viewState change, + * e.g. from "NEAR to FAR" or from "FAR to NEAR". + */ + private void onProximitySensorChangedState() { + + if (!useSpeakerphone.equals(SPEAKERPHONE_AUTO)) { + return; + } + + // The proximity sensor should only be activated when there are exactly two + // available audio devices. + if (audioDevices.size() == 2 && audioDevices.contains(MagicAudioManager.AudioDevice.EARPIECE) + && audioDevices.contains(MagicAudioManager.AudioDevice.SPEAKER_PHONE)) { + if (proximitySensor.sensorReportsNearState()) { + // Sensor reports that a "handset is being held up to a person's ear", + // or "something is covering the light sensor". + setAudioDeviceInternal(MagicAudioManager.AudioDevice.EARPIECE); + + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .SENSOR_NEAR, null, null, null, null)); + } else { + // Sensor reports that a "handset is removed from a person's ear", or + // "the light sensor is no longer covered". + setAudioDeviceInternal(MagicAudioManager.AudioDevice.SPEAKER_PHONE); + + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .SENSOR_FAR, null, null, null, null)); + } + } + } + + public void start(AudioManagerEvents audioManagerEvents) { + Log.d(TAG, "start"); + ThreadUtils.checkIsOnMainThread(); + if (amState == AudioManagerState.RUNNING) { + Log.e(TAG, "AudioManager is already active"); + return; + } + // TODO(henrika): perhaps call new method called preInitAudio() here if UNINITIALIZED. + + Log.d(TAG, "AudioManager starts..."); + this.audioManagerEvents = audioManagerEvents; + amState = AudioManagerState.RUNNING; + + // Store current audio viewState so we can restore it when stop() is called. + savedAudioMode = audioManager.getMode(); + savedIsSpeakerPhoneOn = audioManager.isSpeakerphoneOn(); + savedIsMicrophoneMute = audioManager.isMicrophoneMute(); + hasWiredHeadset = hasWiredHeadset(); + + // Create an AudioManager.OnAudioFocusChangeListener instance. + audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { + // Called on the listener to notify if the audio focus for this listener has been changed. + // The |focusChange| value indicates whether the focus was gained, whether the focus was lost, + // and whether that loss is transient, or whether the new focus holder will hold it for an + // unknown amount of time. + // TODO(henrika): possibly extend support of handling audio-focus changes. Only contains + // logging for now. + @Override + public void onAudioFocusChange(int focusChange) { + String typeOfChange = "AUDIOFOCUS_NOT_DEFINED"; + switch (focusChange) { + case AudioManager.AUDIOFOCUS_GAIN: + typeOfChange = "AUDIOFOCUS_GAIN"; + break; + case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: + typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT"; + break; + case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: + typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE"; + break; + case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: + typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"; + break; + case AudioManager.AUDIOFOCUS_LOSS: + typeOfChange = "AUDIOFOCUS_LOSS"; + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT"; + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"; + break; + default: + typeOfChange = "AUDIOFOCUS_INVALID"; + break; } + Log.d(TAG, "onAudioFocusChange: " + typeOfChange); + } + }; + + // Request audio playout focus (without ducking) and install listener for changes in focus. + int result = audioManager.requestAudioFocus(audioFocusChangeListener, + AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + Log.d(TAG, "Audio focus request granted for VOICE_CALL streams"); + } else { + Log.e(TAG, "Audio focus request failed"); } - /** - * Changes default audio device. - * TODO(henrika): add usage of this method in the AppRTCMobile client. - */ - public void setDefaultAudioDevice(AudioDevice defaultDevice) { - ThreadUtils.checkIsOnMainThread(); - switch (defaultDevice) { - case SPEAKER_PHONE: - defaultAudioDevice = defaultDevice; - break; - case EARPIECE: - if (hasEarpiece()) { - defaultAudioDevice = defaultDevice; - } else { - defaultAudioDevice = AudioDevice.SPEAKER_PHONE; - } - break; - default: - Log.e(TAG, "Invalid default audio device selection"); - break; - } - Log.d(TAG, "setDefaultAudioDevice(device=" + defaultAudioDevice + ")"); - updateAudioDeviceState(); + // Start by setting MODE_IN_COMMUNICATION as default audio mode. It is + // required to be in this mode when playout and/or recording starts for + // best possible VoIP performance. + audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); + + // Always disable microphone mute during a WebRTC call. + setMicrophoneMute(false); + + // Set initial device states. + userSelectedAudioDevice = AudioDevice.NONE; + selectedAudioDevice = AudioDevice.NONE; + audioDevices.clear(); + + // Initialize and start Bluetooth if a BT device is available or initiate + // detection of new (enabled) BT devices. + bluetoothManager.start(); + + // Do initial selection of audio device. This setting can later be changed + // either by adding/removing a BT or wired headset or by covering/uncovering + // the proximity sensor. + updateAudioDeviceState(); + + proximitySensor.start(); + // Register receiver for broadcast intents related to adding/removing a + // wired headset. + registerReceiver(wiredHeadsetReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); + Log.d(TAG, "AudioManager started"); + } + + public void stop() { + Log.d(TAG, "stop"); + ThreadUtils.checkIsOnMainThread(); + if (amState != AudioManagerState.RUNNING) { + Log.e(TAG, "Trying to stop AudioManager in incorrect viewState: " + amState); + return; + } + amState = AudioManagerState.UNINITIALIZED; + + unregisterReceiver(wiredHeadsetReceiver); + + bluetoothManager.stop(); + + // Restore previously stored audio states. + setSpeakerphoneOn(savedIsSpeakerPhoneOn); + setMicrophoneMute(savedIsMicrophoneMute); + audioManager.setMode(savedAudioMode); + + // Abandon audio focus. Gives the previous focus owner, if any, focus. + audioManager.abandonAudioFocus(audioFocusChangeListener); + audioFocusChangeListener = null; + Log.d(TAG, "Abandoned audio focus for VOICE_CALL streams"); + + if (proximitySensor != null) { + proximitySensor.stop(); + proximitySensor = null; } - /** - * Changes selection of the currently active audio device. - */ - public void selectAudioDevice(AudioDevice device) { - ThreadUtils.checkIsOnMainThread(); - if (!audioDevices.contains(device)) { - Log.e(TAG, "Can not select " + device + " from available " + audioDevices); - } - userSelectedAudioDevice = device; - updateAudioDeviceState(); - } + powerManagerUtils.updatePhoneState(PowerManagerUtils.PhoneState.IDLE); - /** - * Returns current set of available/selectable audio devices. - */ - public Set getAudioDevices() { - ThreadUtils.checkIsOnMainThread(); - return Collections.unmodifiableSet(new HashSet(audioDevices)); - } + audioManagerEvents = null; + Log.d(TAG, "AudioManager stopped"); + } - /** - * Returns the currently selected audio device. - */ - public AudioDevice getSelectedAudioDevice() { - ThreadUtils.checkIsOnMainThread(); - return selectedAudioDevice; - } + /** + * Changes selection of the currently active audio device. + */ + private void setAudioDeviceInternal(AudioDevice device) { + Log.d(TAG, "setAudioDeviceInternal(device=" + device + ")"); - /** - * Helper method for receiver registration. - */ - private void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { - magicContext.registerReceiver(receiver, filter); - } + if (audioDevices.contains(device)) { - /** - * Helper method for unregistration of an existing receiver. - */ - private void unregisterReceiver(BroadcastReceiver receiver) { - magicContext.unregisterReceiver(receiver); + switch (device) { + case SPEAKER_PHONE: + setSpeakerphoneOn(true); + break; + case EARPIECE: + setSpeakerphoneOn(false); + break; + case WIRED_HEADSET: + setSpeakerphoneOn(false); + break; + case BLUETOOTH: + setSpeakerphoneOn(false); + break; + default: + Log.e(TAG, "Invalid audio device selection"); + break; + } + selectedAudioDevice = device; } + } - /** - * Sets the speaker phone mode. - */ - private void setSpeakerphoneOn(boolean on) { - boolean wasOn = audioManager.isSpeakerphoneOn(); - if (wasOn == on) { - return; - } - audioManager.setSpeakerphoneOn(on); - } - - /** - * Sets the microphone mute viewState. - */ - private void setMicrophoneMute(boolean on) { - boolean wasMuted = audioManager.isMicrophoneMute(); - if (wasMuted == on) { - return; - } - audioManager.setMicrophoneMute(on); - } - - /** - * Gets the current earpiece viewState. - */ - private boolean hasEarpiece() { - return magicContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); - } - - /** - * Checks whether a wired headset is connected or not. - * This is not a valid indication that audio playback is actually over - * the wired headset as audio routing depends on other conditions. We - * only use it as an early indicator (during initialization) of an attached - * wired headset. - */ - @Deprecated - private boolean hasWiredHeadset() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return audioManager.isWiredHeadsetOn(); + /** + * Changes default audio device. + * TODO(henrika): add usage of this method in the AppRTCMobile client. + */ + public void setDefaultAudioDevice(AudioDevice defaultDevice) { + ThreadUtils.checkIsOnMainThread(); + switch (defaultDevice) { + case SPEAKER_PHONE: + defaultAudioDevice = defaultDevice; + break; + case EARPIECE: + if (hasEarpiece()) { + defaultAudioDevice = defaultDevice; } else { - final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL); - for (AudioDeviceInfo device : devices) { - final int type = device.getType(); - if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) { - Log.d(TAG, "hasWiredHeadset: found wired headset"); - return true; - } else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) { - Log.d(TAG, "hasWiredHeadset: found USB audio device"); - return true; - } - } - return false; + defaultAudioDevice = AudioDevice.SPEAKER_PHONE; } + break; + default: + Log.e(TAG, "Invalid default audio device selection"); + break; + } + Log.d(TAG, "setDefaultAudioDevice(device=" + defaultAudioDevice + ")"); + updateAudioDeviceState(); + } + + /** + * Changes selection of the currently active audio device. + */ + public void selectAudioDevice(AudioDevice device) { + ThreadUtils.checkIsOnMainThread(); + if (!audioDevices.contains(device)) { + Log.e(TAG, "Can not select " + device + " from available " + audioDevices); + } + userSelectedAudioDevice = device; + updateAudioDeviceState(); + } + + /** + * Returns current set of available/selectable audio devices. + */ + public Set getAudioDevices() { + ThreadUtils.checkIsOnMainThread(); + return Collections.unmodifiableSet(new HashSet(audioDevices)); + } + + /** + * Returns the currently selected audio device. + */ + public AudioDevice getSelectedAudioDevice() { + ThreadUtils.checkIsOnMainThread(); + return selectedAudioDevice; + } + + /** + * Helper method for receiver registration. + */ + private void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + magicContext.registerReceiver(receiver, filter); + } + + /** + * Helper method for unregistration of an existing receiver. + */ + private void unregisterReceiver(BroadcastReceiver receiver) { + magicContext.unregisterReceiver(receiver); + } + + /** + * Sets the speaker phone mode. + */ + private void setSpeakerphoneOn(boolean on) { + boolean wasOn = audioManager.isSpeakerphoneOn(); + if (wasOn == on) { + return; + } + audioManager.setSpeakerphoneOn(on); + } + + /** + * Sets the microphone mute viewState. + */ + private void setMicrophoneMute(boolean on) { + boolean wasMuted = audioManager.isMicrophoneMute(); + if (wasMuted == on) { + return; + } + audioManager.setMicrophoneMute(on); + } + + /** + * Gets the current earpiece viewState. + */ + private boolean hasEarpiece() { + return magicContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); + } + + /** + * Checks whether a wired headset is connected or not. + * This is not a valid indication that audio playback is actually over + * the wired headset as audio routing depends on other conditions. We + * only use it as an early indicator (during initialization) of an attached + * wired headset. + */ + @Deprecated + private boolean hasWiredHeadset() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return audioManager.isWiredHeadsetOn(); + } else { + final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL); + for (AudioDeviceInfo device : devices) { + final int type = device.getType(); + if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) { + Log.d(TAG, "hasWiredHeadset: found wired headset"); + return true; + } else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) { + Log.d(TAG, "hasWiredHeadset: found USB audio device"); + return true; + } + } + return false; + } + } + + /** + * Updates list of possible audio devices and make new device selection. + * TODO(henrika): add unit test to verify all viewState transitions. + */ + public void updateAudioDeviceState() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "--- updateAudioDeviceState: " + + "wired headset=" + hasWiredHeadset + ", " + + "BT viewState=" + bluetoothManager.getState()); + Log.d(TAG, "Device status: " + + "available=" + audioDevices + ", " + + "selected=" + selectedAudioDevice + ", " + + "user selected=" + userSelectedAudioDevice); + + // Check if any Bluetooth headset is connected. The internal BT viewState will + // change accordingly. + // TODO(henrika): perhaps wrap required viewState into BT manager. + if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE + || bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE + || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_DISCONNECTING) { + bluetoothManager.updateDevice(); } - /** - * Updates list of possible audio devices and make new device selection. - * TODO(henrika): add unit test to verify all viewState transitions. - */ - public void updateAudioDeviceState() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "--- updateAudioDeviceState: " - + "wired headset=" + hasWiredHeadset + ", " - + "BT viewState=" + bluetoothManager.getState()); - Log.d(TAG, "Device status: " - + "available=" + audioDevices + ", " - + "selected=" + selectedAudioDevice + ", " - + "user selected=" + userSelectedAudioDevice); + // Update the set of available audio devices. + Set newAudioDevices = new HashSet<>(); - // Check if any Bluetooth headset is connected. The internal BT viewState will - // change accordingly. - // TODO(henrika): perhaps wrap required viewState into BT manager. - if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE - || bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE - || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_DISCONNECTING) { - bluetoothManager.updateDevice(); - } - - // Update the set of available audio devices. - Set newAudioDevices = new HashSet<>(); - - if (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED - || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING - || bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE) { - newAudioDevices.add(AudioDevice.BLUETOOTH); - } - - if (hasWiredHeadset) { - // If a wired headset is connected, then it is the only possible option. - newAudioDevices.add(AudioDevice.WIRED_HEADSET); - } else { - // No wired headset, hence the audio-device list can contain speaker - // phone (on a tablet), or speaker phone and earpiece (on mobile phone). - newAudioDevices.add(AudioDevice.SPEAKER_PHONE); - if (hasEarpiece()) { - newAudioDevices.add(AudioDevice.EARPIECE); - } - } - // Store viewState which is set to true if the device list has changed. - boolean audioDeviceSetUpdated = !audioDevices.equals(newAudioDevices); - // Update the existing audio device set. - audioDevices = newAudioDevices; - // Correct user selected audio devices if needed. - if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE - && userSelectedAudioDevice == AudioDevice.BLUETOOTH) { - // If BT is not available, it can't be the user selection. - userSelectedAudioDevice = AudioDevice.NONE; - } - if (hasWiredHeadset && userSelectedAudioDevice == AudioDevice.SPEAKER_PHONE) { - // If user selected speaker phone, but then plugged wired headset then make - // wired headset as user selected device. - userSelectedAudioDevice = AudioDevice.WIRED_HEADSET; - } - if (!hasWiredHeadset && userSelectedAudioDevice == AudioDevice.WIRED_HEADSET) { - // If user selected wired headset, but then unplugged wired headset then make - // speaker phone as user selected device. - userSelectedAudioDevice = AudioDevice.SPEAKER_PHONE; - } - - // Need to start Bluetooth if it is available and user either selected it explicitly or - // user did not select any output device. - boolean needBluetoothAudioStart = - bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE - && (userSelectedAudioDevice == AudioDevice.NONE - || userSelectedAudioDevice == AudioDevice.BLUETOOTH); - - // Need to stop Bluetooth audio if user selected different device and - // Bluetooth SCO connection is established or in the process. - boolean needBluetoothAudioStop = - (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED - || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING) - && (userSelectedAudioDevice != AudioDevice.NONE - && userSelectedAudioDevice != AudioDevice.BLUETOOTH); - - if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE - || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING - || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED) { - Log.d(TAG, "Need BT audio: start=" + needBluetoothAudioStart + ", " - + "stop=" + needBluetoothAudioStop + ", " - + "BT viewState=" + bluetoothManager.getState()); - } - - // Start or stop Bluetooth SCO connection given states set earlier. - if (needBluetoothAudioStop) { - bluetoothManager.stopScoAudio(); - bluetoothManager.updateDevice(); - } - - // Attempt to start Bluetooth SCO audio (takes a few second to start). - if (needBluetoothAudioStart && - !needBluetoothAudioStop && - !bluetoothManager.startScoAudio()) { - // Remove BLUETOOTH from list of available devices since SCO failed. - audioDevices.remove(AudioDevice.BLUETOOTH); - audioDeviceSetUpdated = true; - } - - - // Update selected audio device. - AudioDevice newAudioDevice = selectedAudioDevice; - - if (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED) { - // If a Bluetooth is connected, then it should be used as output audio - // device. Note that it is not sufficient that a headset is available; - // an active SCO channel must also be up and running. - newAudioDevice = AudioDevice.BLUETOOTH; - } else if (hasWiredHeadset) { - // If a wired headset is connected, but Bluetooth is not, then wired headset is used as - // audio device. - newAudioDevice = AudioDevice.WIRED_HEADSET; - } else { - // No wired headset and no Bluetooth, hence the audio-device list can contain speaker - // phone (on a tablet), or speaker phone and earpiece (on mobile phone). - // |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE - // depending on the user's selection. - newAudioDevice = defaultAudioDevice; - } - // Switch to new device but only if there has been any changes. - if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) { - // Do the required device switch. - setAudioDeviceInternal(newAudioDevice); - Log.d(TAG, "New device status: " - + "available=" + audioDevices + ", " - + "selected=" + newAudioDevice); - if (audioManagerEvents != null) { - // Notify a listening client that audio device has been changed. - audioManagerEvents.onAudioDeviceChanged(selectedAudioDevice, audioDevices); - } - } - Log.d(TAG, "--- updateAudioDeviceState done"); + if (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED + || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING + || bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE) { + newAudioDevices.add(AudioDevice.BLUETOOTH); } - /** - * AudioDevice is the names of possible audio devices that we currently - * support. - */ - public enum AudioDevice { - SPEAKER_PHONE, WIRED_HEADSET, EARPIECE, BLUETOOTH, NONE + if (hasWiredHeadset) { + // If a wired headset is connected, then it is the only possible option. + newAudioDevices.add(AudioDevice.WIRED_HEADSET); + } else { + // No wired headset, hence the audio-device list can contain speaker + // phone (on a tablet), or speaker phone and earpiece (on mobile phone). + newAudioDevices.add(AudioDevice.SPEAKER_PHONE); + if (hasEarpiece()) { + newAudioDevices.add(AudioDevice.EARPIECE); + } + } + // Store viewState which is set to true if the device list has changed. + boolean audioDeviceSetUpdated = !audioDevices.equals(newAudioDevices); + // Update the existing audio device set. + audioDevices = newAudioDevices; + // Correct user selected audio devices if needed. + if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_UNAVAILABLE + && userSelectedAudioDevice == AudioDevice.BLUETOOTH) { + // If BT is not available, it can't be the user selection. + userSelectedAudioDevice = AudioDevice.NONE; + } + if (hasWiredHeadset && userSelectedAudioDevice == AudioDevice.SPEAKER_PHONE) { + // If user selected speaker phone, but then plugged wired headset then make + // wired headset as user selected device. + userSelectedAudioDevice = AudioDevice.WIRED_HEADSET; + } + if (!hasWiredHeadset && userSelectedAudioDevice == AudioDevice.WIRED_HEADSET) { + // If user selected wired headset, but then unplugged wired headset then make + // speaker phone as user selected device. + userSelectedAudioDevice = AudioDevice.SPEAKER_PHONE; } - /** - * AudioManager viewState. - */ - public enum AudioManagerState { - UNINITIALIZED, - PREINITIALIZED, - RUNNING, + // Need to start Bluetooth if it is available and user either selected it explicitly or + // user did not select any output device. + boolean needBluetoothAudioStart = + bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE + && (userSelectedAudioDevice == AudioDevice.NONE + || userSelectedAudioDevice == AudioDevice.BLUETOOTH); + + // Need to stop Bluetooth audio if user selected different device and + // Bluetooth SCO connection is established or in the process. + boolean needBluetoothAudioStop = + (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED + || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING) + && (userSelectedAudioDevice != AudioDevice.NONE + && userSelectedAudioDevice != AudioDevice.BLUETOOTH); + + if (bluetoothManager.getState() == MagicBluetoothManager.State.HEADSET_AVAILABLE + || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTING + || bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED) { + Log.d(TAG, "Need BT audio: start=" + needBluetoothAudioStart + ", " + + "stop=" + needBluetoothAudioStop + ", " + + "BT viewState=" + bluetoothManager.getState()); } - /** - * Selected audio device change event. - */ - public interface AudioManagerEvents { - // Callback fired once audio device is changed or list of available audio devices changed. - void onAudioDeviceChanged( - AudioDevice selectedAudioDevice, Set availableAudioDevices); + // Start or stop Bluetooth SCO connection given states set earlier. + if (needBluetoothAudioStop) { + bluetoothManager.stopScoAudio(); + bluetoothManager.updateDevice(); } - /* Receiver which handles changes in wired headset availability. */ - private class WiredHeadsetReceiver extends BroadcastReceiver { - private static final int STATE_UNPLUGGED = 0; - private static final int STATE_PLUGGED = 1; - private static final int HAS_NO_MIC = 0; - - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra("viewState", STATE_UNPLUGGED); - // int microphone = intent.getIntExtra("microphone", HAS_NO_MIC); - // String name = intent.getStringExtra("name"); - hasWiredHeadset = (state == STATE_PLUGGED); - updateAudioDeviceState(); - } + // Attempt to start Bluetooth SCO audio (takes a few second to start). + if (needBluetoothAudioStart && + !needBluetoothAudioStop && + !bluetoothManager.startScoAudio()) { + // Remove BLUETOOTH from list of available devices since SCO failed. + audioDevices.remove(AudioDevice.BLUETOOTH); + audioDeviceSetUpdated = true; } + + // Update selected audio device. + AudioDevice newAudioDevice = selectedAudioDevice; + + if (bluetoothManager.getState() == MagicBluetoothManager.State.SCO_CONNECTED) { + // If a Bluetooth is connected, then it should be used as output audio + // device. Note that it is not sufficient that a headset is available; + // an active SCO channel must also be up and running. + newAudioDevice = AudioDevice.BLUETOOTH; + } else if (hasWiredHeadset) { + // If a wired headset is connected, but Bluetooth is not, then wired headset is used as + // audio device. + newAudioDevice = AudioDevice.WIRED_HEADSET; + } else { + // No wired headset and no Bluetooth, hence the audio-device list can contain speaker + // phone (on a tablet), or speaker phone and earpiece (on mobile phone). + // |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE + // depending on the user's selection. + newAudioDevice = defaultAudioDevice; + } + // Switch to new device but only if there has been any changes. + if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) { + // Do the required device switch. + setAudioDeviceInternal(newAudioDevice); + Log.d(TAG, "New device status: " + + "available=" + audioDevices + ", " + + "selected=" + newAudioDevice); + if (audioManagerEvents != null) { + // Notify a listening client that audio device has been changed. + audioManagerEvents.onAudioDeviceChanged(selectedAudioDevice, audioDevices); + } + } + Log.d(TAG, "--- updateAudioDeviceState done"); + } + + /** + * AudioDevice is the names of possible audio devices that we currently + * support. + */ + public enum AudioDevice { + SPEAKER_PHONE, WIRED_HEADSET, EARPIECE, BLUETOOTH, NONE + } + + /** + * AudioManager viewState. + */ + public enum AudioManagerState { + UNINITIALIZED, + PREINITIALIZED, + RUNNING, + } + + /** + * Selected audio device change event. + */ + public interface AudioManagerEvents { + // Callback fired once audio device is changed or list of available audio devices changed. + void onAudioDeviceChanged( + AudioDevice selectedAudioDevice, Set availableAudioDevices); + } + + /* Receiver which handles changes in wired headset availability. */ + private class WiredHeadsetReceiver extends BroadcastReceiver { + private static final int STATE_UNPLUGGED = 0; + private static final int STATE_PLUGGED = 1; + private static final int HAS_NO_MIC = 0; + + @Override + public void onReceive(Context context, Intent intent) { + int state = intent.getIntExtra("viewState", STATE_UNPLUGGED); + // int microphone = intent.getIntExtra("microphone", HAS_NO_MIC); + // String name = intent.getStringExtra("name"); + hasWiredHeadset = (state == STATE_PLUGGED); + updateAudioDeviceState(); + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicBluetoothManager.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicBluetoothManager.java index 70d776c32..9fda329d2 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicBluetoothManager.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicBluetoothManager.java @@ -51,508 +51,508 @@ import java.util.Set; import org.webrtc.ThreadUtils; public class MagicBluetoothManager { - private static final String TAG = "MagicBluetoothManager"; + private static final String TAG = "MagicBluetoothManager"; - // Timeout interval for starting or stopping audio to a Bluetooth SCO device. - private static final int BLUETOOTH_SCO_TIMEOUT_MS = 4000; - // Maximum number of SCO connection attempts. - private static final int MAX_SCO_CONNECTION_ATTEMPTS = 2; - private final Context apprtcContext; - private final MagicAudioManager apprtcAudioManager; - private final AudioManager audioManager; - private final Handler handler; - private final BluetoothProfile.ServiceListener bluetoothServiceListener; - private final BroadcastReceiver bluetoothHeadsetReceiver; - int scoConnectionAttempts; - private State bluetoothState; - private BluetoothAdapter bluetoothAdapter; - private BluetoothHeadset bluetoothHeadset; - private BluetoothDevice bluetoothDevice; - // Runs when the Bluetooth timeout expires. We use that timeout after calling - // startScoAudio() or stopScoAudio() because we're not guaranteed to get a - // callback after those calls. - private final Runnable bluetoothTimeoutRunnable = new Runnable() { - @Override - public void run() { - bluetoothTimeout(); - } - }; - - protected MagicBluetoothManager(Context context, MagicAudioManager audioManager) { - Log.d(TAG, "ctor"); - ThreadUtils.checkIsOnMainThread(); - apprtcContext = context; - apprtcAudioManager = audioManager; - this.audioManager = getAudioManager(context); - bluetoothState = State.UNINITIALIZED; - bluetoothServiceListener = new BluetoothServiceListener(); - bluetoothHeadsetReceiver = new BluetoothHeadsetBroadcastReceiver(); - handler = new Handler(Looper.getMainLooper()); + // Timeout interval for starting or stopping audio to a Bluetooth SCO device. + private static final int BLUETOOTH_SCO_TIMEOUT_MS = 4000; + // Maximum number of SCO connection attempts. + private static final int MAX_SCO_CONNECTION_ATTEMPTS = 2; + private final Context apprtcContext; + private final MagicAudioManager apprtcAudioManager; + private final AudioManager audioManager; + private final Handler handler; + private final BluetoothProfile.ServiceListener bluetoothServiceListener; + private final BroadcastReceiver bluetoothHeadsetReceiver; + int scoConnectionAttempts; + private State bluetoothState; + private BluetoothAdapter bluetoothAdapter; + private BluetoothHeadset bluetoothHeadset; + private BluetoothDevice bluetoothDevice; + // Runs when the Bluetooth timeout expires. We use that timeout after calling + // startScoAudio() or stopScoAudio() because we're not guaranteed to get a + // callback after those calls. + private final Runnable bluetoothTimeoutRunnable = new Runnable() { + @Override + public void run() { + bluetoothTimeout(); } + }; - /** - * Construction. - */ - static MagicBluetoothManager create(Context context, MagicAudioManager audioManager) { - return new MagicBluetoothManager(context, audioManager); - } - - /** - * Returns the internal viewState. - */ - public State getState() { - ThreadUtils.checkIsOnMainThread(); - return bluetoothState; - } + protected MagicBluetoothManager(Context context, MagicAudioManager audioManager) { + Log.d(TAG, "ctor"); + ThreadUtils.checkIsOnMainThread(); + apprtcContext = context; + apprtcAudioManager = audioManager; + this.audioManager = getAudioManager(context); + bluetoothState = State.UNINITIALIZED; + bluetoothServiceListener = new BluetoothServiceListener(); + bluetoothHeadsetReceiver = new BluetoothHeadsetBroadcastReceiver(); + handler = new Handler(Looper.getMainLooper()); + } /** - * Activates components required to detect Bluetooth devices and to enable - * BT SCO (audio is routed via BT SCO) for the headset profile. The end - * viewState will be HEADSET_UNAVAILABLE but a viewState machine has started which - * will start a viewState change sequence where the final outcome depends on - * if/when the BT headset is enabled. - * Example of viewState change sequence when start() is called while BT device - * is connected and enabled: - * UNINITIALIZED --> HEADSET_UNAVAILABLE --> HEADSET_AVAILABLE --> - * SCO_CONNECTING --> SCO_CONNECTED <==> audio is now routed via BT SCO. - * Note that the MagicAudioManager is also involved in driving this viewState - * change. - */ - public void start() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "start"); - if (!hasPermission(apprtcContext, android.Manifest.permission.BLUETOOTH)) { - Log.w(TAG, "Process (pid=" + Process.myPid() + ") lacks BLUETOOTH permission"); - return; - } - if (bluetoothState != State.UNINITIALIZED) { - Log.w(TAG, "Invalid BT viewState"); - return; - } - bluetoothHeadset = null; - bluetoothDevice = null; - scoConnectionAttempts = 0; - // Get a handle to the default local Bluetooth adapter. - bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - if (bluetoothAdapter == null) { - Log.w(TAG, "Device does not support Bluetooth"); - return; - } - // Ensure that the device supports use of BT SCO audio for off call use cases. - if (!audioManager.isBluetoothScoAvailableOffCall()) { - Log.e(TAG, "Bluetooth SCO audio is not available off call"); - return; - } - logBluetoothAdapterInfo(bluetoothAdapter); - // Establish a connection to the HEADSET profile (includes both Bluetooth Headset and - // Hands-Free) proxy object and install a listener. - if (!getBluetoothProfileProxy( - apprtcContext, bluetoothServiceListener, BluetoothProfile.HEADSET)) { - Log.e(TAG, "BluetoothAdapter.getProfileProxy(HEADSET) failed"); - return; - } - // Register receivers for BluetoothHeadset change notifications. - IntentFilter bluetoothHeadsetFilter = new IntentFilter(); - // Register receiver for change in connection viewState of the Headset profile. - bluetoothHeadsetFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - // Register receiver for change in audio connection viewState of the Headset profile. - bluetoothHeadsetFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); - registerReceiver(bluetoothHeadsetReceiver, bluetoothHeadsetFilter); - Log.d(TAG, "HEADSET profile viewState: " - + stateToString(bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET))); - Log.d(TAG, "Bluetooth proxy for headset profile has started"); - bluetoothState = State.HEADSET_UNAVAILABLE; - Log.d(TAG, "start done: BT viewState=" + bluetoothState); + * Construction. + */ + static MagicBluetoothManager create(Context context, MagicAudioManager audioManager) { + return new MagicBluetoothManager(context, audioManager); + } + + /** + * Returns the internal viewState. + */ + public State getState() { + ThreadUtils.checkIsOnMainThread(); + return bluetoothState; + } + + /** + * Activates components required to detect Bluetooth devices and to enable + * BT SCO (audio is routed via BT SCO) for the headset profile. The end + * viewState will be HEADSET_UNAVAILABLE but a viewState machine has started which + * will start a viewState change sequence where the final outcome depends on + * if/when the BT headset is enabled. + * Example of viewState change sequence when start() is called while BT device + * is connected and enabled: + * UNINITIALIZED --> HEADSET_UNAVAILABLE --> HEADSET_AVAILABLE --> + * SCO_CONNECTING --> SCO_CONNECTED <==> audio is now routed via BT SCO. + * Note that the MagicAudioManager is also involved in driving this viewState + * change. + */ + public void start() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "start"); + if (!hasPermission(apprtcContext, android.Manifest.permission.BLUETOOTH)) { + Log.w(TAG, "Process (pid=" + Process.myPid() + ") lacks BLUETOOTH permission"); + return; + } + if (bluetoothState != State.UNINITIALIZED) { + Log.w(TAG, "Invalid BT viewState"); + return; + } + bluetoothHeadset = null; + bluetoothDevice = null; + scoConnectionAttempts = 0; + // Get a handle to the default local Bluetooth adapter. + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (bluetoothAdapter == null) { + Log.w(TAG, "Device does not support Bluetooth"); + return; + } + // Ensure that the device supports use of BT SCO audio for off call use cases. + if (!audioManager.isBluetoothScoAvailableOffCall()) { + Log.e(TAG, "Bluetooth SCO audio is not available off call"); + return; + } + logBluetoothAdapterInfo(bluetoothAdapter); + // Establish a connection to the HEADSET profile (includes both Bluetooth Headset and + // Hands-Free) proxy object and install a listener. + if (!getBluetoothProfileProxy( + apprtcContext, bluetoothServiceListener, BluetoothProfile.HEADSET)) { + Log.e(TAG, "BluetoothAdapter.getProfileProxy(HEADSET) failed"); + return; + } + // Register receivers for BluetoothHeadset change notifications. + IntentFilter bluetoothHeadsetFilter = new IntentFilter(); + // Register receiver for change in connection viewState of the Headset profile. + bluetoothHeadsetFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); + // Register receiver for change in audio connection viewState of the Headset profile. + bluetoothHeadsetFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); + registerReceiver(bluetoothHeadsetReceiver, bluetoothHeadsetFilter); + Log.d(TAG, "HEADSET profile viewState: " + + stateToString(bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET))); + Log.d(TAG, "Bluetooth proxy for headset profile has started"); + bluetoothState = State.HEADSET_UNAVAILABLE; + Log.d(TAG, "start done: BT viewState=" + bluetoothState); + } + + /** + * Stops and closes all components related to Bluetooth audio. + */ + public void stop() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "stop: BT viewState=" + bluetoothState); + if (bluetoothAdapter == null) { + return; + } + // Stop BT SCO connection with remote device if needed. + stopScoAudio(); + // Close down remaining BT resources. + if (bluetoothState == State.UNINITIALIZED) { + return; + } + unregisterReceiver(bluetoothHeadsetReceiver); + cancelTimer(); + if (bluetoothHeadset != null) { + bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHeadset); + bluetoothHeadset = null; + } + bluetoothAdapter = null; + bluetoothDevice = null; + bluetoothState = State.UNINITIALIZED; + Log.d(TAG, "stop done: BT viewState=" + bluetoothState); + } + + /** + * Starts Bluetooth SCO connection with remote device. + * Note that the phone application always has the priority on the usage of the SCO connection + * for telephony. If this method is called while the phone is in call it will be ignored. + * Similarly, if a call is received or sent while an application is using the SCO connection, + * the connection will be lost for the application and NOT returned automatically when the call + * ends. Also note that: up to and including API version JELLY_BEAN_MR1, this method initiates a + * virtual voice call to the Bluetooth headset. After API version JELLY_BEAN_MR2 only a raw SCO + * audio connection is established. + * TODO(henrika): should we add support for virtual voice call to BT headset also for JBMR2 and + * higher. It might be required to initiates a virtual voice call since many devices do not + * accept SCO audio without a "call". + */ + public boolean startScoAudio() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "startSco: BT viewState=" + bluetoothState + ", " + + "attempts: " + scoConnectionAttempts + ", " + + "SCO is on: " + isScoOn()); + if (scoConnectionAttempts >= MAX_SCO_CONNECTION_ATTEMPTS) { + Log.e(TAG, "BT SCO connection fails - no more attempts"); + return false; + } + if (bluetoothState != State.HEADSET_AVAILABLE) { + Log.e(TAG, "BT SCO connection fails - no headset available"); + return false; + } + // Start BT SCO channel and wait for ACTION_AUDIO_STATE_CHANGED. + Log.d(TAG, "Starting Bluetooth SCO and waits for ACTION_AUDIO_STATE_CHANGED..."); + // The SCO connection establishment can take several seconds, hence we cannot rely on the + // connection to be available when the method returns but instead register to receive the + // intent ACTION_SCO_AUDIO_STATE_UPDATED and wait for the viewState to be SCO_AUDIO_STATE_CONNECTED. + bluetoothState = State.SCO_CONNECTING; + audioManager.startBluetoothSco(); + audioManager.setBluetoothScoOn(true); + scoConnectionAttempts++; + startTimer(); + Log.d(TAG, "startScoAudio done: BT viewState=" + bluetoothState + ", " + + "SCO is on: " + isScoOn()); + return true; + } + + /** + * Stops Bluetooth SCO connection with remote device. + */ + public void stopScoAudio() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "stopScoAudio: BT viewState=" + bluetoothState + ", " + + "SCO is on: " + isScoOn()); + if (bluetoothState != State.SCO_CONNECTING && bluetoothState != State.SCO_CONNECTED) { + return; + } + cancelTimer(); + audioManager.stopBluetoothSco(); + audioManager.setBluetoothScoOn(false); + bluetoothState = State.SCO_DISCONNECTING; + Log.d(TAG, "stopScoAudio done: BT viewState=" + bluetoothState + ", " + + "SCO is on: " + isScoOn()); + } + + /** + * Use the BluetoothHeadset proxy object (controls the Bluetooth Headset + * Service via IPC) to update the list of connected devices for the HEADSET + * profile. The internal viewState will change to HEADSET_UNAVAILABLE or to + * HEADSET_AVAILABLE and |bluetoothDevice| will be mapped to the connected + * device if available. + */ + public void updateDevice() { + if (bluetoothState == State.UNINITIALIZED || bluetoothHeadset == null) { + return; + } + Log.d(TAG, "updateDevice"); + // Get connected devices for the headset profile. Returns the set of + // devices which are in viewState STATE_CONNECTED. The BluetoothDevice class + // is just a thin wrapper for a Bluetooth hardware address. + List devices = bluetoothHeadset.getConnectedDevices(); + if (devices.isEmpty()) { + bluetoothDevice = null; + bluetoothState = State.HEADSET_UNAVAILABLE; + Log.d(TAG, "No connected bluetooth headset"); + } else { + // Always use first device in list. Android only supports one device. + bluetoothDevice = devices.get(0); + bluetoothState = State.HEADSET_AVAILABLE; + Log.d(TAG, "Connected bluetooth headset: " + + "name=" + bluetoothDevice.getName() + ", " + + "viewState=" + stateToString(bluetoothHeadset.getConnectionState(bluetoothDevice)) + + ", SCO audio=" + bluetoothHeadset.isAudioConnected(bluetoothDevice)); + } + Log.d(TAG, "updateDevice done: BT viewState=" + bluetoothState); + } + + /** + * Stubs for test mocks. + */ + protected AudioManager getAudioManager(Context context) { + return (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + } + + protected void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + apprtcContext.registerReceiver(receiver, filter); + } + + protected void unregisterReceiver(BroadcastReceiver receiver) { + apprtcContext.unregisterReceiver(receiver); + } + + protected boolean getBluetoothProfileProxy( + Context context, BluetoothProfile.ServiceListener listener, int profile) { + return bluetoothAdapter.getProfileProxy(context, listener, profile); + } + + protected boolean hasPermission(Context context, String permission) { + return apprtcContext.checkPermission(permission, Process.myPid(), Process.myUid()) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Logs the viewState of the local Bluetooth adapter. + */ + @SuppressLint("HardwareIds") + protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) { + Log.d(TAG, "BluetoothAdapter: " + + "enabled=" + localAdapter.isEnabled() + ", " + + "viewState=" + stateToString(localAdapter.getState()) + ", " + + "name=" + localAdapter.getName() + ", " + + "address=" + localAdapter.getAddress()); + // Log the set of BluetoothDevice objects that are bonded (paired) to the local adapter. + Set pairedDevices = localAdapter.getBondedDevices(); + if (!pairedDevices.isEmpty()) { + Log.d(TAG, "paired devices:"); + for (BluetoothDevice device : pairedDevices) { + Log.d(TAG, " name=" + device.getName() + ", address=" + device.getAddress()); + } + } + } + + /** + * Ensures that the audio manager updates its list of available audio devices. + */ + private void updateAudioDeviceState() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "updateAudioDeviceState"); + apprtcAudioManager.updateAudioDeviceState(); + } + + /** + * Starts timer which times out after BLUETOOTH_SCO_TIMEOUT_MS milliseconds. + */ + private void startTimer() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "startTimer"); + handler.postDelayed(bluetoothTimeoutRunnable, BLUETOOTH_SCO_TIMEOUT_MS); + } + + /** + * Cancels any outstanding timer tasks. + */ + private void cancelTimer() { + ThreadUtils.checkIsOnMainThread(); + Log.d(TAG, "cancelTimer"); + handler.removeCallbacks(bluetoothTimeoutRunnable); + } + + /** + * Called when start of the BT SCO channel takes too long time. Usually + * happens when the BT device has been turned on during an ongoing call. + */ + private void bluetoothTimeout() { + ThreadUtils.checkIsOnMainThread(); + if (bluetoothState == State.UNINITIALIZED || bluetoothHeadset == null) { + return; + } + Log.d(TAG, "bluetoothTimeout: BT viewState=" + bluetoothState + ", " + + "attempts: " + scoConnectionAttempts + ", " + + "SCO is on: " + isScoOn()); + if (bluetoothState != State.SCO_CONNECTING) { + return; + } + // Bluetooth SCO should be connecting; check the latest result. + boolean scoConnected = false; + List devices = bluetoothHeadset.getConnectedDevices(); + if (devices.size() > 0) { + bluetoothDevice = devices.get(0); + if (bluetoothHeadset.isAudioConnected(bluetoothDevice)) { + Log.d(TAG, "SCO connected with " + bluetoothDevice.getName()); + scoConnected = true; + } else { + Log.d(TAG, "SCO is not connected with " + bluetoothDevice.getName()); + } + } + if (scoConnected) { + // We thought BT had timed out, but it's actually on; updating viewState. + bluetoothState = State.SCO_CONNECTED; + scoConnectionAttempts = 0; + } else { + // Give up and "cancel" our request by calling stopBluetoothSco(). + Log.w(TAG, "BT failed to connect after timeout"); + stopScoAudio(); + } + updateAudioDeviceState(); + Log.d(TAG, "bluetoothTimeout done: BT viewState=" + bluetoothState); + } + + /** + * Checks whether audio uses Bluetooth SCO. + */ + private boolean isScoOn() { + return audioManager.isBluetoothScoOn(); + } + + /** + * Converts BluetoothAdapter states into local string representations. + */ + private String stateToString(int state) { + switch (state) { + case BluetoothAdapter.STATE_DISCONNECTED: + return "DISCONNECTED"; + case BluetoothAdapter.STATE_CONNECTED: + return "CONNECTED"; + case BluetoothAdapter.STATE_CONNECTING: + return "CONNECTING"; + case BluetoothAdapter.STATE_DISCONNECTING: + return "DISCONNECTING"; + case BluetoothAdapter.STATE_OFF: + return "OFF"; + case BluetoothAdapter.STATE_ON: + return "ON"; + case BluetoothAdapter.STATE_TURNING_OFF: + // Indicates the local Bluetooth adapter is turning off. Local clients should immediately + // attempt graceful disconnection of any remote links. + return "TURNING_OFF"; + case BluetoothAdapter.STATE_TURNING_ON: + // Indicates the local Bluetooth adapter is turning on. However local clients should wait + // for STATE_ON before attempting to use the adapter. + return "TURNING_ON"; + default: + return "INVALID"; + } + } + + // Bluetooth connection viewState. + public enum State { + // Bluetooth is not available; no adapter or Bluetooth is off. + UNINITIALIZED, + // Bluetooth error happened when trying to start Bluetooth. + ERROR, + // Bluetooth proxy object for the Headset profile exists, but no connected headset devices, + // SCO is not started or disconnected. + HEADSET_UNAVAILABLE, + // Bluetooth proxy object for the Headset profile connected, connected Bluetooth headset + // present, but SCO is not started or disconnected. + HEADSET_AVAILABLE, + // Bluetooth audio SCO connection with remote device is closing. + SCO_DISCONNECTING, + // Bluetooth audio SCO connection with remote device is initiated. + SCO_CONNECTING, + // Bluetooth audio SCO connection with remote device is established. + SCO_CONNECTED + } + + /** + * Implementation of an interface that notifies BluetoothProfile IPC clients when they have been + * connected to or disconnected from the service. + */ + private class BluetoothServiceListener implements BluetoothProfile.ServiceListener { + @Override + // Called to notify the client when the proxy object has been connected to the service. + // Once we have the profile proxy object, we can use it to monitor the viewState of the + // connection and perform other operations that are relevant to the headset profile. + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (profile != BluetoothProfile.HEADSET || bluetoothState == State.UNINITIALIZED) { + return; + } + Log.d(TAG, "BluetoothServiceListener.onServiceConnected: BT viewState=" + bluetoothState); + // Android only supports one connected Bluetooth Headset at a time. + bluetoothHeadset = (BluetoothHeadset) proxy; + updateAudioDeviceState(); + Log.d(TAG, "onServiceConnected done: BT viewState=" + bluetoothState); } - /** - * Stops and closes all components related to Bluetooth audio. - */ - public void stop() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "stop: BT viewState=" + bluetoothState); - if (bluetoothAdapter == null) { - return; + @Override + /** Notifies the client when the proxy object has been disconnected from the service. */ + public void onServiceDisconnected(int profile) { + if (profile != BluetoothProfile.HEADSET || bluetoothState == State.UNINITIALIZED) { + return; + } + Log.d(TAG, "BluetoothServiceListener.onServiceDisconnected: BT viewState=" + bluetoothState); + stopScoAudio(); + bluetoothHeadset = null; + bluetoothDevice = null; + bluetoothState = State.HEADSET_UNAVAILABLE; + updateAudioDeviceState(); + Log.d(TAG, "onServiceDisconnected done: BT viewState=" + bluetoothState); + } + } + + // Intent broadcast receiver which handles changes in Bluetooth device availability. + // Detects headset changes and Bluetooth SCO viewState changes. + private class BluetoothHeadsetBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (bluetoothState == State.UNINITIALIZED) { + return; + } + final String action = intent.getAction(); + // Change in connection viewState of the Headset profile. Note that the + // change does not tell us anything about whether we're streaming + // audio to BT over SCO. Typically received when user turns on a BT + // headset while audio is active using another audio device. + if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { + final int state = + intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED); + Log.d(TAG, "BluetoothHeadsetBroadcastReceiver.onReceive: " + + "a=ACTION_CONNECTION_STATE_CHANGED, " + + "s=" + stateToString(state) + ", " + + "sb=" + isInitialStickyBroadcast() + ", " + + "BT viewState: " + bluetoothState); + if (state == BluetoothHeadset.STATE_CONNECTED) { + scoConnectionAttempts = 0; + updateAudioDeviceState(); + } else if (state == BluetoothHeadset.STATE_CONNECTING) { + // No action needed. + } else if (state == BluetoothHeadset.STATE_DISCONNECTING) { + // No action needed. + } else if (state == BluetoothHeadset.STATE_DISCONNECTED) { + // Bluetooth is probably powered off during the call. + stopScoAudio(); + updateAudioDeviceState(); } - // Stop BT SCO connection with remote device if needed. - stopScoAudio(); - // Close down remaining BT resources. - if (bluetoothState == State.UNINITIALIZED) { - return; - } - unregisterReceiver(bluetoothHeadsetReceiver); - cancelTimer(); - if (bluetoothHeadset != null) { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHeadset); - bluetoothHeadset = null; - } - bluetoothAdapter = null; - bluetoothDevice = null; - bluetoothState = State.UNINITIALIZED; - Log.d(TAG, "stop done: BT viewState=" + bluetoothState); - } - - /** - * Starts Bluetooth SCO connection with remote device. - * Note that the phone application always has the priority on the usage of the SCO connection - * for telephony. If this method is called while the phone is in call it will be ignored. - * Similarly, if a call is received or sent while an application is using the SCO connection, - * the connection will be lost for the application and NOT returned automatically when the call - * ends. Also note that: up to and including API version JELLY_BEAN_MR1, this method initiates a - * virtual voice call to the Bluetooth headset. After API version JELLY_BEAN_MR2 only a raw SCO - * audio connection is established. - * TODO(henrika): should we add support for virtual voice call to BT headset also for JBMR2 and - * higher. It might be required to initiates a virtual voice call since many devices do not - * accept SCO audio without a "call". - */ - public boolean startScoAudio() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "startSco: BT viewState=" + bluetoothState + ", " - + "attempts: " + scoConnectionAttempts + ", " - + "SCO is on: " + isScoOn()); - if (scoConnectionAttempts >= MAX_SCO_CONNECTION_ATTEMPTS) { - Log.e(TAG, "BT SCO connection fails - no more attempts"); - return false; - } - if (bluetoothState != State.HEADSET_AVAILABLE) { - Log.e(TAG, "BT SCO connection fails - no headset available"); - return false; - } - // Start BT SCO channel and wait for ACTION_AUDIO_STATE_CHANGED. - Log.d(TAG, "Starting Bluetooth SCO and waits for ACTION_AUDIO_STATE_CHANGED..."); - // The SCO connection establishment can take several seconds, hence we cannot rely on the - // connection to be available when the method returns but instead register to receive the - // intent ACTION_SCO_AUDIO_STATE_UPDATED and wait for the viewState to be SCO_AUDIO_STATE_CONNECTED. - bluetoothState = State.SCO_CONNECTING; - audioManager.startBluetoothSco(); - audioManager.setBluetoothScoOn(true); - scoConnectionAttempts++; - startTimer(); - Log.d(TAG, "startScoAudio done: BT viewState=" + bluetoothState + ", " - + "SCO is on: " + isScoOn()); - return true; - } - - /** - * Stops Bluetooth SCO connection with remote device. - */ - public void stopScoAudio() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "stopScoAudio: BT viewState=" + bluetoothState + ", " - + "SCO is on: " + isScoOn()); - if (bluetoothState != State.SCO_CONNECTING && bluetoothState != State.SCO_CONNECTED) { - return; - } - cancelTimer(); - audioManager.stopBluetoothSco(); - audioManager.setBluetoothScoOn(false); - bluetoothState = State.SCO_DISCONNECTING; - Log.d(TAG, "stopScoAudio done: BT viewState=" + bluetoothState + ", " - + "SCO is on: " + isScoOn()); - } - - /** - * Use the BluetoothHeadset proxy object (controls the Bluetooth Headset - * Service via IPC) to update the list of connected devices for the HEADSET - * profile. The internal viewState will change to HEADSET_UNAVAILABLE or to - * HEADSET_AVAILABLE and |bluetoothDevice| will be mapped to the connected - * device if available. - */ - public void updateDevice() { - if (bluetoothState == State.UNINITIALIZED || bluetoothHeadset == null) { - return; - } - Log.d(TAG, "updateDevice"); - // Get connected devices for the headset profile. Returns the set of - // devices which are in viewState STATE_CONNECTED. The BluetoothDevice class - // is just a thin wrapper for a Bluetooth hardware address. - List devices = bluetoothHeadset.getConnectedDevices(); - if (devices.isEmpty()) { - bluetoothDevice = null; - bluetoothState = State.HEADSET_UNAVAILABLE; - Log.d(TAG, "No connected bluetooth headset"); - } else { - // Always use first device in list. Android only supports one device. - bluetoothDevice = devices.get(0); - bluetoothState = State.HEADSET_AVAILABLE; - Log.d(TAG, "Connected bluetooth headset: " - + "name=" + bluetoothDevice.getName() + ", " - + "viewState=" + stateToString(bluetoothHeadset.getConnectionState(bluetoothDevice)) - + ", SCO audio=" + bluetoothHeadset.isAudioConnected(bluetoothDevice)); - } - Log.d(TAG, "updateDevice done: BT viewState=" + bluetoothState); - } - - /** - * Stubs for test mocks. - */ - protected AudioManager getAudioManager(Context context) { - return (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - } - - protected void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { - apprtcContext.registerReceiver(receiver, filter); - } - - protected void unregisterReceiver(BroadcastReceiver receiver) { - apprtcContext.unregisterReceiver(receiver); - } - - protected boolean getBluetoothProfileProxy( - Context context, BluetoothProfile.ServiceListener listener, int profile) { - return bluetoothAdapter.getProfileProxy(context, listener, profile); - } - - protected boolean hasPermission(Context context, String permission) { - return apprtcContext.checkPermission(permission, Process.myPid(), Process.myUid()) - == PackageManager.PERMISSION_GRANTED; - } - - /** - * Logs the viewState of the local Bluetooth adapter. - */ - @SuppressLint("HardwareIds") - protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) { - Log.d(TAG, "BluetoothAdapter: " - + "enabled=" + localAdapter.isEnabled() + ", " - + "viewState=" + stateToString(localAdapter.getState()) + ", " - + "name=" + localAdapter.getName() + ", " - + "address=" + localAdapter.getAddress()); - // Log the set of BluetoothDevice objects that are bonded (paired) to the local adapter. - Set pairedDevices = localAdapter.getBondedDevices(); - if (!pairedDevices.isEmpty()) { - Log.d(TAG, "paired devices:"); - for (BluetoothDevice device : pairedDevices) { - Log.d(TAG, " name=" + device.getName() + ", address=" + device.getAddress()); - } - } - } - - /** - * Ensures that the audio manager updates its list of available audio devices. - */ - private void updateAudioDeviceState() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "updateAudioDeviceState"); - apprtcAudioManager.updateAudioDeviceState(); - } - - /** - * Starts timer which times out after BLUETOOTH_SCO_TIMEOUT_MS milliseconds. - */ - private void startTimer() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "startTimer"); - handler.postDelayed(bluetoothTimeoutRunnable, BLUETOOTH_SCO_TIMEOUT_MS); - } - - /** - * Cancels any outstanding timer tasks. - */ - private void cancelTimer() { - ThreadUtils.checkIsOnMainThread(); - Log.d(TAG, "cancelTimer"); - handler.removeCallbacks(bluetoothTimeoutRunnable); - } - - /** - * Called when start of the BT SCO channel takes too long time. Usually - * happens when the BT device has been turned on during an ongoing call. - */ - private void bluetoothTimeout() { - ThreadUtils.checkIsOnMainThread(); - if (bluetoothState == State.UNINITIALIZED || bluetoothHeadset == null) { - return; - } - Log.d(TAG, "bluetoothTimeout: BT viewState=" + bluetoothState + ", " - + "attempts: " + scoConnectionAttempts + ", " - + "SCO is on: " + isScoOn()); - if (bluetoothState != State.SCO_CONNECTING) { - return; - } - // Bluetooth SCO should be connecting; check the latest result. - boolean scoConnected = false; - List devices = bluetoothHeadset.getConnectedDevices(); - if (devices.size() > 0) { - bluetoothDevice = devices.get(0); - if (bluetoothHeadset.isAudioConnected(bluetoothDevice)) { - Log.d(TAG, "SCO connected with " + bluetoothDevice.getName()); - scoConnected = true; - } else { - Log.d(TAG, "SCO is not connected with " + bluetoothDevice.getName()); - } - } - if (scoConnected) { - // We thought BT had timed out, but it's actually on; updating viewState. + // Change in the audio (SCO) connection viewState of the Headset profile. + // Typically received after call to startScoAudio() has finalized. + } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { + final int state = intent.getIntExtra( + BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED); + Log.d(TAG, "BluetoothHeadsetBroadcastReceiver.onReceive: " + + "a=ACTION_AUDIO_STATE_CHANGED, " + + "s=" + stateToString(state) + ", " + + "sb=" + isInitialStickyBroadcast() + ", " + + "BT viewState: " + bluetoothState); + if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { + cancelTimer(); + if (bluetoothState == State.SCO_CONNECTING) { + Log.d(TAG, "+++ Bluetooth audio SCO is now connected"); bluetoothState = State.SCO_CONNECTED; scoConnectionAttempts = 0; - } else { - // Give up and "cancel" our request by calling stopBluetoothSco(). - Log.w(TAG, "BT failed to connect after timeout"); - stopScoAudio(); - } - updateAudioDeviceState(); - Log.d(TAG, "bluetoothTimeout done: BT viewState=" + bluetoothState); - } - - /** - * Checks whether audio uses Bluetooth SCO. - */ - private boolean isScoOn() { - return audioManager.isBluetoothScoOn(); - } - - /** - * Converts BluetoothAdapter states into local string representations. - */ - private String stateToString(int state) { - switch (state) { - case BluetoothAdapter.STATE_DISCONNECTED: - return "DISCONNECTED"; - case BluetoothAdapter.STATE_CONNECTED: - return "CONNECTED"; - case BluetoothAdapter.STATE_CONNECTING: - return "CONNECTING"; - case BluetoothAdapter.STATE_DISCONNECTING: - return "DISCONNECTING"; - case BluetoothAdapter.STATE_OFF: - return "OFF"; - case BluetoothAdapter.STATE_ON: - return "ON"; - case BluetoothAdapter.STATE_TURNING_OFF: - // Indicates the local Bluetooth adapter is turning off. Local clients should immediately - // attempt graceful disconnection of any remote links. - return "TURNING_OFF"; - case BluetoothAdapter.STATE_TURNING_ON: - // Indicates the local Bluetooth adapter is turning on. However local clients should wait - // for STATE_ON before attempting to use the adapter. - return "TURNING_ON"; - default: - return "INVALID"; - } - } - - // Bluetooth connection viewState. - public enum State { - // Bluetooth is not available; no adapter or Bluetooth is off. - UNINITIALIZED, - // Bluetooth error happened when trying to start Bluetooth. - ERROR, - // Bluetooth proxy object for the Headset profile exists, but no connected headset devices, - // SCO is not started or disconnected. - HEADSET_UNAVAILABLE, - // Bluetooth proxy object for the Headset profile connected, connected Bluetooth headset - // present, but SCO is not started or disconnected. - HEADSET_AVAILABLE, - // Bluetooth audio SCO connection with remote device is closing. - SCO_DISCONNECTING, - // Bluetooth audio SCO connection with remote device is initiated. - SCO_CONNECTING, - // Bluetooth audio SCO connection with remote device is established. - SCO_CONNECTED - } - - /** - * Implementation of an interface that notifies BluetoothProfile IPC clients when they have been - * connected to or disconnected from the service. - */ - private class BluetoothServiceListener implements BluetoothProfile.ServiceListener { - @Override - // Called to notify the client when the proxy object has been connected to the service. - // Once we have the profile proxy object, we can use it to monitor the viewState of the - // connection and perform other operations that are relevant to the headset profile. - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (profile != BluetoothProfile.HEADSET || bluetoothState == State.UNINITIALIZED) { - return; - } - Log.d(TAG, "BluetoothServiceListener.onServiceConnected: BT viewState=" + bluetoothState); - // Android only supports one connected Bluetooth Headset at a time. - bluetoothHeadset = (BluetoothHeadset) proxy; updateAudioDeviceState(); - Log.d(TAG, "onServiceConnected done: BT viewState=" + bluetoothState); - } - - @Override - /** Notifies the client when the proxy object has been disconnected from the service. */ - public void onServiceDisconnected(int profile) { - if (profile != BluetoothProfile.HEADSET || bluetoothState == State.UNINITIALIZED) { - return; - } - Log.d(TAG, "BluetoothServiceListener.onServiceDisconnected: BT viewState=" + bluetoothState); - stopScoAudio(); - bluetoothHeadset = null; - bluetoothDevice = null; - bluetoothState = State.HEADSET_UNAVAILABLE; - updateAudioDeviceState(); - Log.d(TAG, "onServiceDisconnected done: BT viewState=" + bluetoothState); - } - } - - // Intent broadcast receiver which handles changes in Bluetooth device availability. - // Detects headset changes and Bluetooth SCO viewState changes. - private class BluetoothHeadsetBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (bluetoothState == State.UNINITIALIZED) { - return; - } - final String action = intent.getAction(); - // Change in connection viewState of the Headset profile. Note that the - // change does not tell us anything about whether we're streaming - // audio to BT over SCO. Typically received when user turns on a BT - // headset while audio is active using another audio device. - if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { - final int state = - intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED); - Log.d(TAG, "BluetoothHeadsetBroadcastReceiver.onReceive: " - + "a=ACTION_CONNECTION_STATE_CHANGED, " - + "s=" + stateToString(state) + ", " - + "sb=" + isInitialStickyBroadcast() + ", " - + "BT viewState: " + bluetoothState); - if (state == BluetoothHeadset.STATE_CONNECTED) { - scoConnectionAttempts = 0; - updateAudioDeviceState(); - } else if (state == BluetoothHeadset.STATE_CONNECTING) { - // No action needed. - } else if (state == BluetoothHeadset.STATE_DISCONNECTING) { - // No action needed. - } else if (state == BluetoothHeadset.STATE_DISCONNECTED) { - // Bluetooth is probably powered off during the call. - stopScoAudio(); - updateAudioDeviceState(); - } - // Change in the audio (SCO) connection viewState of the Headset profile. - // Typically received after call to startScoAudio() has finalized. - } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { - final int state = intent.getIntExtra( - BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED); - Log.d(TAG, "BluetoothHeadsetBroadcastReceiver.onReceive: " - + "a=ACTION_AUDIO_STATE_CHANGED, " - + "s=" + stateToString(state) + ", " - + "sb=" + isInitialStickyBroadcast() + ", " - + "BT viewState: " + bluetoothState); - if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { - cancelTimer(); - if (bluetoothState == State.SCO_CONNECTING) { - Log.d(TAG, "+++ Bluetooth audio SCO is now connected"); - bluetoothState = State.SCO_CONNECTED; - scoConnectionAttempts = 0; - updateAudioDeviceState(); - } else { - Log.w(TAG, "Unexpected viewState BluetoothHeadset.STATE_AUDIO_CONNECTED"); - } - } else if (state == BluetoothHeadset.STATE_AUDIO_CONNECTING) { - Log.d(TAG, "+++ Bluetooth audio SCO is now connecting..."); - } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { - Log.d(TAG, "+++ Bluetooth audio SCO is now disconnected"); - if (isInitialStickyBroadcast()) { - Log.d(TAG, "Ignore STATE_AUDIO_DISCONNECTED initial sticky broadcast."); - return; - } - updateAudioDeviceState(); - } - } - Log.d(TAG, "onReceive done: BT viewState=" + bluetoothState); + } else { + Log.w(TAG, "Unexpected viewState BluetoothHeadset.STATE_AUDIO_CONNECTED"); + } + } else if (state == BluetoothHeadset.STATE_AUDIO_CONNECTING) { + Log.d(TAG, "+++ Bluetooth audio SCO is now connecting..."); + } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { + Log.d(TAG, "+++ Bluetooth audio SCO is now disconnected"); + if (isInitialStickyBroadcast()) { + Log.d(TAG, "Ignore STATE_AUDIO_DISCONNECTED initial sticky broadcast."); + return; + } + updateAudioDeviceState(); } + } + Log.d(TAG, "onReceive done: BT viewState=" + bluetoothState); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java index 2a916510a..9cdb4da1c 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java @@ -27,15 +27,15 @@ import android.util.Log; import androidx.annotation.Nullable; import autodagger.AutoInjector; import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.models.json.signaling.DataChannelMessage; -import com.nextcloud.talk.models.json.signaling.DataChannelMessageNick; -import com.nextcloud.talk.models.json.signaling.NCIceCandidate; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.MediaStreamEvent; import com.nextcloud.talk.events.PeerConnectionEvent; import com.nextcloud.talk.events.SessionDescriptionSendEvent; import com.nextcloud.talk.events.WebSocketCommunicationEvent; +import com.nextcloud.talk.models.json.signaling.DataChannelMessage; +import com.nextcloud.talk.models.json.signaling.DataChannelMessageNick; +import com.nextcloud.talk.models.json.signaling.NCIceCandidate; import com.nextcloud.talk.utils.LoggingUtils; import java.io.IOException; import java.nio.ByteBuffer; @@ -56,431 +56,454 @@ import org.webrtc.SessionDescription; @AutoInjector(NextcloudTalkApplication.class) public class MagicPeerConnectionWrapper { - private static String TAG = "MagicPeerConnectionWrapper"; - private List iceCandidates = new ArrayList<>(); - private PeerConnection peerConnection; - private String sessionId; - private String nick; - private MediaConstraints sdpConstraints; - private DataChannel magicDataChannel; - private MagicSdpObserver magicSdpObserver; - private MediaStream remoteMediaStream; + private static String TAG = "MagicPeerConnectionWrapper"; + @Inject + Context context; + private List iceCandidates = new ArrayList<>(); + private PeerConnection peerConnection; + private String sessionId; + private String nick; + private MediaConstraints sdpConstraints; + private DataChannel magicDataChannel; + private MagicSdpObserver magicSdpObserver; + private MediaStream remoteMediaStream; + private boolean remoteVideoOn; + private boolean remoteAudioOn; + private boolean hasInitiated; + private MediaStream localMediaStream; + private boolean isMCUPublisher; + private boolean hasMCU; + private String videoStreamType; + private int connectionAttempts = 0; + private PeerConnection.IceConnectionState peerIceConnectionState; - private boolean remoteVideoOn; - private boolean remoteAudioOn; + public MagicPeerConnectionWrapper(PeerConnectionFactory peerConnectionFactory, + List iceServerList, + MediaConstraints sdpConstraints, + String sessionId, String localSession, @Nullable MediaStream mediaStream, + boolean isMCUPublisher, boolean hasMCU, String videoStreamType) { - private boolean hasInitiated; + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - private MediaStream localMediaStream; - private boolean isMCUPublisher; - private boolean hasMCU; - private String videoStreamType; + this.localMediaStream = mediaStream; + this.videoStreamType = videoStreamType; + this.hasMCU = hasMCU; - private int connectionAttempts = 0; - private PeerConnection.IceConnectionState peerIceConnectionState; + this.sessionId = sessionId; + this.sdpConstraints = sdpConstraints; - @Inject - Context context; + magicSdpObserver = new MagicSdpObserver(); + hasInitiated = sessionId.compareTo(localSession) < 0; + this.isMCUPublisher = isMCUPublisher; - public MagicPeerConnectionWrapper(PeerConnectionFactory peerConnectionFactory, - List iceServerList, - MediaConstraints sdpConstraints, - String sessionId, String localSession, @Nullable MediaStream mediaStream, - boolean isMCUPublisher, boolean hasMCU, String videoStreamType) { + peerConnection = peerConnectionFactory.createPeerConnection(iceServerList, sdpConstraints, + new MagicPeerConnectionObserver()); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + if (peerConnection != null) { + if (localMediaStream != null) { + peerConnection.addStream(localMediaStream); + } - this.localMediaStream = mediaStream; - this.videoStreamType = videoStreamType; - this.hasMCU = hasMCU; + if (hasMCU || hasInitiated) { + DataChannel.Init init = new DataChannel.Init(); + init.negotiated = false; + magicDataChannel = peerConnection.createDataChannel("status", init); + magicDataChannel.registerObserver(new MagicDataChannelObserver()); + if (isMCUPublisher) { + peerConnection.createOffer(magicSdpObserver, sdpConstraints); + } else if (hasMCU) { + HashMap hashMap = new HashMap<>(); + hashMap.put("sessionId", sessionId); + EventBus.getDefault() + .post(new WebSocketCommunicationEvent("peerReadyForRequestingOffer", hashMap)); + } else if (hasInitiated) { + peerConnection.createOffer(magicSdpObserver, sdpConstraints); + } + } + } + } - this.sessionId = sessionId; - this.sdpConstraints = sdpConstraints; + public String getVideoStreamType() { + return videoStreamType; + } - magicSdpObserver = new MagicSdpObserver(); - hasInitiated = sessionId.compareTo(localSession) < 0; - this.isMCUPublisher = isMCUPublisher; - - peerConnection = peerConnectionFactory.createPeerConnection(iceServerList, sdpConstraints, - new MagicPeerConnectionObserver()); + public void removePeerConnection() { + if (magicDataChannel != null) { + magicDataChannel.dispose(); + magicDataChannel = null; + } - if (peerConnection != null) { - if (localMediaStream != null) { - peerConnection.addStream(localMediaStream); + if (peerConnection != null) { + if (localMediaStream != null) { + peerConnection.removeStream(localMediaStream); + } + + peerConnection.close(); + peerConnection = null; + } + } + + public void drainIceCandidates() { + + if (peerConnection != null) { + for (IceCandidate iceCandidate : iceCandidates) { + peerConnection.addIceCandidate(iceCandidate); + } + + iceCandidates = new ArrayList<>(); + } + } + + public MagicSdpObserver getMagicSdpObserver() { + return magicSdpObserver; + } + + public void addCandidate(IceCandidate iceCandidate) { + if (peerConnection != null && peerConnection.getRemoteDescription() != null) { + peerConnection.addIceCandidate(iceCandidate); + } else { + iceCandidates.add(iceCandidate); + } + } + + public void sendNickChannelData(DataChannelMessageNick dataChannelMessage) { + ByteBuffer buffer; + if (magicDataChannel != null) { + try { + buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes()); + magicDataChannel.send(new DataChannel.Buffer(buffer, false)); + } catch (IOException e) { + Log.d(TAG, + "Failed to send channel data, attempting regular " + dataChannelMessage.toString()); + } + } + } + + public void sendChannelData(DataChannelMessage dataChannelMessage) { + ByteBuffer buffer; + if (magicDataChannel != null) { + try { + buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes()); + magicDataChannel.send(new DataChannel.Buffer(buffer, false)); + } catch (IOException e) { + Log.d(TAG, + "Failed to send channel data, attempting regular " + dataChannelMessage.toString()); + } + } + } + + public PeerConnection getPeerConnection() { + return peerConnection; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public String getNick() { + if (!TextUtils.isEmpty(nick)) { + return nick; + } else { + return NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_nick_guest); + } + } + + public void setNick(String nick) { + this.nick = nick; + } + + private void sendInitialMediaStatus() { + if (localMediaStream != null) { + if (localMediaStream.videoTracks.size() == 1 && localMediaStream.videoTracks.get(0) + .enabled()) { + sendChannelData(new DataChannelMessage("videoOn")); + } else { + sendChannelData(new DataChannelMessage("videoOff")); + } + + if (localMediaStream.audioTracks.size() == 1 && localMediaStream.audioTracks.get(0) + .enabled()) { + sendChannelData(new DataChannelMessage("audioOn")); + } else { + sendChannelData(new DataChannelMessage("audioOff")); + } + } + } + + public boolean isMCUPublisher() { + return isMCUPublisher; + } + + private void restartIce() { + if (connectionAttempts <= 5) { + if (!hasMCU || isMCUPublisher) { + MediaConstraints.KeyValuePair iceRestartConstraint = + new MediaConstraints.KeyValuePair("IceRestart", "true"); + + if (sdpConstraints.mandatory.contains(iceRestartConstraint)) { + sdpConstraints.mandatory.add(iceRestartConstraint); + } + + peerConnection.createOffer(magicSdpObserver, sdpConstraints); + } else { + // we have an MCU and this is not the publisher + // Do something if we have an MCU + } + + connectionAttempts++; + } + } + + public PeerConnection.IceConnectionState getPeerIceConnectionState() { + return peerIceConnectionState; + } + + private class MagicDataChannelObserver implements DataChannel.Observer { + + @Override + public void onBufferedAmountChange(long l) { + + } + + @Override + public void onStateChange() { + if (magicDataChannel != null && magicDataChannel.state().equals(DataChannel.State.OPEN) && + magicDataChannel.label().equals("status")) { + sendInitialMediaStatus(); + } + } + + @Override + public void onMessage(DataChannel.Buffer buffer) { + if (buffer.binary) { + Log.d(TAG, "Received binary msg over " + TAG + " " + sessionId); + return; + } + + ByteBuffer data = buffer.data; + final byte[] bytes = new byte[data.capacity()]; + data.get(bytes); + String strData = new String(bytes); + Log.d(TAG, "Got msg: " + strData + " over " + TAG + " " + sessionId); + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "Got msg: " + strData + " over " + peerConnection.hashCode() + " " + sessionId); + + try { + DataChannelMessage dataChannelMessage = + LoganSquare.parse(strData, DataChannelMessage.class); + + String internalNick; + if ("nickChanged".equals(dataChannelMessage.getType())) { + if (dataChannelMessage.getPayload() instanceof String) { + internalNick = (String) dataChannelMessage.getPayload(); + if (!internalNick.equals(nick)) { + setNick(nick); + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .NICK_CHANGE, sessionId, getNick(), null, videoStreamType)); } - - if (hasMCU || hasInitiated) { - DataChannel.Init init = new DataChannel.Init(); - init.negotiated = false; - magicDataChannel = peerConnection.createDataChannel("status", init); - magicDataChannel.registerObserver(new MagicDataChannelObserver()); - if (isMCUPublisher) { - peerConnection.createOffer(magicSdpObserver, sdpConstraints); - } else if (hasMCU) { - HashMap hashMap = new HashMap<>(); - hashMap.put("sessionId", sessionId); - EventBus.getDefault().post(new WebSocketCommunicationEvent("peerReadyForRequestingOffer", hashMap)); - } else if (hasInitiated) { - peerConnection.createOffer(magicSdpObserver, sdpConstraints); - - } + } else { + if (dataChannelMessage.getPayload() != null) { + HashMap payloadHashMap = + (HashMap) dataChannelMessage.getPayload(); + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .NICK_CHANGE, payloadHashMap.get("userid"), payloadHashMap.get("name"), null, + videoStreamType)); } + } + } else if ("audioOn".equals(dataChannelMessage.getType())) { + remoteAudioOn = true; + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .AUDIO_CHANGE, sessionId, null, remoteAudioOn, videoStreamType)); + } else if ("audioOff".equals(dataChannelMessage.getType())) { + remoteAudioOn = false; + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .AUDIO_CHANGE, sessionId, null, remoteAudioOn, videoStreamType)); + } else if ("videoOn".equals(dataChannelMessage.getType())) { + remoteVideoOn = true; + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .VIDEO_CHANGE, sessionId, null, remoteVideoOn, videoStreamType)); + } else if ("videoOff".equals(dataChannelMessage.getType())) { + remoteVideoOn = false; + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .VIDEO_CHANGE, sessionId, null, remoteVideoOn, videoStreamType)); } + } catch (IOException e) { + Log.d(TAG, "Failed to parse data channel message"); + } + } + } + + private class MagicPeerConnectionObserver implements PeerConnection.Observer { + private final String TAG = "MagicPeerConnectionObserver"; + + @Override + public void onSignalingChange(PeerConnection.SignalingState signalingState) { } - public String getVideoStreamType() { - return videoStreamType; - } + @Override + public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) { + peerIceConnectionState = iceConnectionState; + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "iceConnectionChangeTo: " + + iceConnectionState.name() + + " over " + + peerConnection.hashCode() + + " " + + sessionId); - public void removePeerConnection() { - if (magicDataChannel != null) { - magicDataChannel.dispose(); - magicDataChannel = null; - } - - if (peerConnection != null) { - if (localMediaStream != null) { - peerConnection.removeStream(localMediaStream); - } - - peerConnection.close(); - peerConnection = null; - } - } - - public void drainIceCandidates() { - - if (peerConnection != null) { - for (IceCandidate iceCandidate : iceCandidates) { - peerConnection.addIceCandidate(iceCandidate); - } - - iceCandidates = new ArrayList<>(); - } - } - - public MagicSdpObserver getMagicSdpObserver() { - return magicSdpObserver; - } - - public void addCandidate(IceCandidate iceCandidate) { - if (peerConnection != null && peerConnection.getRemoteDescription() != null) { - peerConnection.addIceCandidate(iceCandidate); - } else { - iceCandidates.add(iceCandidate); - } - } - - - public void sendNickChannelData(DataChannelMessageNick dataChannelMessage) { - ByteBuffer buffer; - if (magicDataChannel != null) { - try { - buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes()); - magicDataChannel.send(new DataChannel.Buffer(buffer, false)); - } catch (IOException e) { - Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage.toString()); - } - } - } - - public void sendChannelData(DataChannelMessage dataChannelMessage) { - ByteBuffer buffer; - if (magicDataChannel != null) { - try { - buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes()); - magicDataChannel.send(new DataChannel.Buffer(buffer, false)); - } catch (IOException e) { - Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage.toString()); - } - } - } - - public PeerConnection getPeerConnection() { - return peerConnection; - } - - public String getSessionId() { - return sessionId; - } - - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - public String getNick() { - if (!TextUtils.isEmpty(nick)) { - return nick; - } else { - return NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_nick_guest); - } - } - - public void setNick(String nick) { - this.nick = nick; - } - - private void sendInitialMediaStatus() { - if (localMediaStream != null) { - if (localMediaStream.videoTracks.size() == 1 && localMediaStream.videoTracks.get(0).enabled()) { - sendChannelData(new DataChannelMessage("videoOn")); - } else { - sendChannelData(new DataChannelMessage("videoOff")); - } - - if (localMediaStream.audioTracks.size() == 1 && localMediaStream.audioTracks.get(0).enabled()) { - sendChannelData(new DataChannelMessage("audioOn")); - } else { - sendChannelData(new DataChannelMessage("audioOff")); - } - } - } - - public boolean isMCUPublisher() { - return isMCUPublisher; - } - - private class MagicDataChannelObserver implements DataChannel.Observer { - - @Override - public void onBufferedAmountChange(long l) { - - } - - @Override - public void onStateChange() { - if (magicDataChannel != null && magicDataChannel.state().equals(DataChannel.State.OPEN) && - magicDataChannel.label().equals("status")) { - sendInitialMediaStatus(); - } - } - - @Override - public void onMessage(DataChannel.Buffer buffer) { - if (buffer.binary) { - Log.d(TAG, "Received binary msg over " + TAG + " " + sessionId); - return; - } - - ByteBuffer data = buffer.data; - final byte[] bytes = new byte[data.capacity()]; - data.get(bytes); - String strData = new String(bytes); - Log.d(TAG, "Got msg: " + strData + " over " + TAG + " " + sessionId); - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "Got msg: " + strData + " over " + peerConnection.hashCode() + " " + sessionId); - - try { - DataChannelMessage dataChannelMessage = LoganSquare.parse(strData, DataChannelMessage.class); - - String internalNick; - if ("nickChanged".equals(dataChannelMessage.getType())) { - if (dataChannelMessage.getPayload() instanceof String) { - internalNick = (String) dataChannelMessage.getPayload(); - if (!internalNick.equals(nick)) { - setNick(nick); - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .NICK_CHANGE, sessionId, getNick(), null, videoStreamType)); - } - } else { - if (dataChannelMessage.getPayload() != null) { - HashMap payloadHashMap = (HashMap) dataChannelMessage.getPayload(); - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .NICK_CHANGE, payloadHashMap.get("userid"), payloadHashMap.get("name"), null, videoStreamType)); - } - } - - } else if ("audioOn".equals(dataChannelMessage.getType())) { - remoteAudioOn = true; - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .AUDIO_CHANGE, sessionId, null, remoteAudioOn, videoStreamType)); - } else if ("audioOff".equals(dataChannelMessage.getType())) { - remoteAudioOn = false; - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .AUDIO_CHANGE, sessionId, null, remoteAudioOn, videoStreamType)); - } else if ("videoOn".equals(dataChannelMessage.getType())) { - remoteVideoOn = true; - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .VIDEO_CHANGE, sessionId, null, remoteVideoOn, videoStreamType)); - } else if ("videoOff".equals(dataChannelMessage.getType())) { - remoteVideoOn = false; - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .VIDEO_CHANGE, sessionId, null, remoteVideoOn, videoStreamType)); - } - } catch (IOException e) { - Log.d(TAG, "Failed to parse data channel message"); - } - } - } - - private void restartIce() { - if (connectionAttempts <= 5) { - if (!hasMCU || isMCUPublisher) { - MediaConstraints.KeyValuePair iceRestartConstraint = - new MediaConstraints.KeyValuePair("IceRestart", "true"); - - if (sdpConstraints.mandatory.contains(iceRestartConstraint)) { - sdpConstraints.mandatory.add(iceRestartConstraint); - } - - peerConnection.createOffer(magicSdpObserver, sdpConstraints); - } else { - // we have an MCU and this is not the publisher - // Do something if we have an MCU - } - - connectionAttempts++; - } - } - - private class MagicPeerConnectionObserver implements PeerConnection.Observer { - private final String TAG = "MagicPeerConnectionObserver"; - - @Override - public void onSignalingChange(PeerConnection.SignalingState signalingState) { - } - - @Override - public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) { - peerIceConnectionState = iceConnectionState; - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "iceConnectionChangeTo: " + iceConnectionState.name() + " over " + peerConnection.hashCode() + " " + sessionId); - - Log.d("iceConnectionChangeTo: ", iceConnectionState.name() + " over " + peerConnection.hashCode() + " " + sessionId); - if (iceConnectionState.equals(PeerConnection.IceConnectionState.CONNECTED)) { - connectionAttempts = 0; + Log.d("iceConnectionChangeTo: ", + iceConnectionState.name() + " over " + peerConnection.hashCode() + " " + sessionId); + if (iceConnectionState.equals(PeerConnection.IceConnectionState.CONNECTED)) { + connectionAttempts = 0; /*EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType .PEER_CONNECTED, sessionId, null, null));*/ - if (!isMCUPublisher) { - EventBus.getDefault().post(new MediaStreamEvent(remoteMediaStream, sessionId, videoStreamType)); - } + if (!isMCUPublisher) { + EventBus.getDefault() + .post(new MediaStreamEvent(remoteMediaStream, sessionId, videoStreamType)); + } - if (hasInitiated) { - sendInitialMediaStatus(); - } - - } else if (iceConnectionState.equals(PeerConnection.IceConnectionState.CLOSED)) { - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .PEER_CLOSED, sessionId, null, null, videoStreamType)); - connectionAttempts = 0; - } else if (iceConnectionState.equals(PeerConnection.IceConnectionState.FAILED)) { + if (hasInitiated) { + sendInitialMediaStatus(); + } + } else if (iceConnectionState.equals(PeerConnection.IceConnectionState.CLOSED)) { + EventBus.getDefault() + .post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType + .PEER_CLOSED, sessionId, null, null, videoStreamType)); + connectionAttempts = 0; + } else if (iceConnectionState.equals(PeerConnection.IceConnectionState.FAILED)) { /*if (MerlinTheWizard.isConnectedToInternet() && connectionAttempts < 5) { restartIce(); }*/ - if (isMCUPublisher) { - EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED, sessionId, null, null, null)); - } - } - } - - @Override - public void onIceConnectionReceivingChange(boolean b) { - - } - - @Override - public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) { - - } - - @Override - public void onIceCandidate(IceCandidate iceCandidate) { - NCIceCandidate ncIceCandidate = new NCIceCandidate(); - ncIceCandidate.setSdpMid(iceCandidate.sdpMid); - ncIceCandidate.setSdpMLineIndex(iceCandidate.sdpMLineIndex); - ncIceCandidate.setCandidate(iceCandidate.sdp); - EventBus.getDefault().post(new SessionDescriptionSendEvent(null, sessionId, - "candidate", ncIceCandidate, videoStreamType)); - } - - @Override - public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) { - - } - - @Override - public void onAddStream(MediaStream mediaStream) { - remoteMediaStream = mediaStream; - } - - @Override - public void onRemoveStream(MediaStream mediaStream) { - if (!isMCUPublisher) { - EventBus.getDefault().post(new MediaStreamEvent(null, sessionId, videoStreamType)); - } - } - - @Override - public void onDataChannel(DataChannel dataChannel) { - if (dataChannel.label().equals("status")) { - magicDataChannel = dataChannel; - magicDataChannel.registerObserver(new MagicDataChannelObserver()); - } - } - - @Override - public void onRenegotiationNeeded() { - - } - - @Override - public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) { + if (isMCUPublisher) { + EventBus.getDefault() + .post(new PeerConnectionEvent( + PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED, sessionId, null, + null, null)); } + } } - private class MagicSdpObserver implements SdpObserver { - private final String TAG = "MagicSdpObserver"; + @Override + public void onIceConnectionReceivingChange(boolean b) { - @Override - public void onCreateFailure(String s) { - Log.d(TAG, s); - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "SDPObserver createFailure: " + s + " over " + peerConnection.hashCode() + " " + sessionId); - - } - - @Override - public void onSetFailure(String s) { - Log.d(TAG, s); - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "SDPObserver setFailure: " + s + " over " + peerConnection.hashCode() + " " + sessionId); - } - - @Override - public void onCreateSuccess(SessionDescription sessionDescription) { - SessionDescription sessionDescriptionWithPreferredCodec; - String sessionDescriptionStringWithPreferredCodec = MagicWebRTCUtils.preferCodec - (sessionDescription.description, - "H264", false); - sessionDescriptionWithPreferredCodec = new SessionDescription( - sessionDescription.type, - sessionDescriptionStringWithPreferredCodec); - - - EventBus.getDefault().post(new SessionDescriptionSendEvent(sessionDescriptionWithPreferredCodec, sessionId, - sessionDescription.type.canonicalForm().toLowerCase(), null, videoStreamType)); - - if (peerConnection != null) { - peerConnection.setLocalDescription(magicSdpObserver, sessionDescriptionWithPreferredCodec); - } - } - - @Override - public void onSetSuccess() { - if (peerConnection != null) { - if (peerConnection.getLocalDescription() == null) { - peerConnection.createAnswer(magicSdpObserver, sdpConstraints); - } - - if (peerConnection.getRemoteDescription() != null) { - drainIceCandidates(); - } - } - } } - public PeerConnection.IceConnectionState getPeerIceConnectionState() { - return peerIceConnectionState; + @Override + public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) { + } + + @Override + public void onIceCandidate(IceCandidate iceCandidate) { + NCIceCandidate ncIceCandidate = new NCIceCandidate(); + ncIceCandidate.setSdpMid(iceCandidate.sdpMid); + ncIceCandidate.setSdpMLineIndex(iceCandidate.sdpMLineIndex); + ncIceCandidate.setCandidate(iceCandidate.sdp); + EventBus.getDefault().post(new SessionDescriptionSendEvent(null, sessionId, + "candidate", ncIceCandidate, videoStreamType)); + } + + @Override + public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) { + + } + + @Override + public void onAddStream(MediaStream mediaStream) { + remoteMediaStream = mediaStream; + } + + @Override + public void onRemoveStream(MediaStream mediaStream) { + if (!isMCUPublisher) { + EventBus.getDefault().post(new MediaStreamEvent(null, sessionId, videoStreamType)); + } + } + + @Override + public void onDataChannel(DataChannel dataChannel) { + if (dataChannel.label().equals("status")) { + magicDataChannel = dataChannel; + magicDataChannel.registerObserver(new MagicDataChannelObserver()); + } + } + + @Override + public void onRenegotiationNeeded() { + + } + + @Override + public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) { + } + } + + private class MagicSdpObserver implements SdpObserver { + private final String TAG = "MagicSdpObserver"; + + @Override + public void onCreateFailure(String s) { + Log.d(TAG, s); + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "SDPObserver createFailure: " + + s + + " over " + + peerConnection.hashCode() + + " " + + sessionId); + } + + @Override + public void onSetFailure(String s) { + Log.d(TAG, s); + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "SDPObserver setFailure: " + s + " over " + peerConnection.hashCode() + " " + sessionId); + } + + @Override + public void onCreateSuccess(SessionDescription sessionDescription) { + SessionDescription sessionDescriptionWithPreferredCodec; + String sessionDescriptionStringWithPreferredCodec = MagicWebRTCUtils.preferCodec + (sessionDescription.description, + "H264", false); + sessionDescriptionWithPreferredCodec = new SessionDescription( + sessionDescription.type, + sessionDescriptionStringWithPreferredCodec); + + EventBus.getDefault() + .post(new SessionDescriptionSendEvent(sessionDescriptionWithPreferredCodec, sessionId, + sessionDescription.type.canonicalForm().toLowerCase(), null, videoStreamType)); + + if (peerConnection != null) { + peerConnection.setLocalDescription(magicSdpObserver, sessionDescriptionWithPreferredCodec); + } + } + + @Override + public void onSetSuccess() { + if (peerConnection != null) { + if (peerConnection.getLocalDescription() == null) { + peerConnection.createAnswer(magicSdpObserver, sdpConstraints); + } + + if (peerConnection.getRemoteDescription() != null) { + drainIceCandidates(); + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicProximitySensor.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicProximitySensor.java index f7ab61a47..be6ef595d 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicProximitySensor.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicProximitySensor.java @@ -50,134 +50,134 @@ import org.webrtc.ThreadUtils; * Anything less than the threshold value and the sensor returns "NEAR". */ public class MagicProximitySensor implements SensorEventListener { - private static final String TAG = "MagicProximitySensor"; + private static final String TAG = "MagicProximitySensor"; - // This class should be created, started and stopped on one thread - // (e.g. the main thread). We use |nonThreadSafe| to ensure that this is - // the case. Only active when |DEBUG| is set to true. - private final ThreadUtils.ThreadChecker threadChecker = new ThreadUtils.ThreadChecker(); + // This class should be created, started and stopped on one thread + // (e.g. the main thread). We use |nonThreadSafe| to ensure that this is + // the case. Only active when |DEBUG| is set to true. + private final ThreadUtils.ThreadChecker threadChecker = new ThreadUtils.ThreadChecker(); - private final Runnable onSensorStateListener; - private final SensorManager sensorManager; - private Sensor proximitySensor = null; - private boolean lastStateReportIsNear = false; + private final Runnable onSensorStateListener; + private final SensorManager sensorManager; + private Sensor proximitySensor = null; + private boolean lastStateReportIsNear = false; - private MagicProximitySensor(Context context, Runnable sensorStateListener) { - onSensorStateListener = sensorStateListener; - sensorManager = ((SensorManager) context.getSystemService(Context.SENSOR_SERVICE)); + private MagicProximitySensor(Context context, Runnable sensorStateListener) { + onSensorStateListener = sensorStateListener; + sensorManager = ((SensorManager) context.getSystemService(Context.SENSOR_SERVICE)); + } + + /** + * Construction + */ + static MagicProximitySensor create(Context context, Runnable sensorStateListener) { + return new MagicProximitySensor(context, sensorStateListener); + } + + /** + * Activate the proximity sensor. Also do initialization if called for the + * first time. + */ + public boolean start() { + threadChecker.checkIsOnValidThread(); + if (!initDefaultSensor()) { + // Proximity sensor is not supported on this device. + return false; } + sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); + return true; + } - /** - * Construction - */ - static MagicProximitySensor create(Context context, Runnable sensorStateListener) { - return new MagicProximitySensor(context, sensorStateListener); + /** + * Deactivate the proximity sensor. + */ + void stop() { + threadChecker.checkIsOnValidThread(); + if (proximitySensor == null) { + return; } + sensorManager.unregisterListener(this, proximitySensor); + } - /** - * Activate the proximity sensor. Also do initialization if called for the - * first time. - */ - public boolean start() { - threadChecker.checkIsOnValidThread(); - if (!initDefaultSensor()) { - // Proximity sensor is not supported on this device. - return false; - } - sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); - return true; + /** + * Getter for last reported viewState. Set to true if "near" is reported. + */ + boolean sensorReportsNearState() { + threadChecker.checkIsOnValidThread(); + return lastStateReportIsNear; + } + + @Override + public final void onAccuracyChanged(Sensor sensor, int accuracy) { + threadChecker.checkIsOnValidThread(); + if (sensor.getType() == Sensor.TYPE_PROXIMITY && + accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) { + Log.e(TAG, "The values returned by this sensor cannot be trusted"); } + } - /** - * Deactivate the proximity sensor. - */ - void stop() { - threadChecker.checkIsOnValidThread(); - if (proximitySensor == null) { - return; - } - sensorManager.unregisterListener(this, proximitySensor); + @Override + public final void onSensorChanged(SensorEvent event) { + threadChecker.checkIsOnValidThread(); + if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) { + // As a best practice; do as little as possible within this method and + // avoid blocking. + float distanceInCentimeters = event.values[0]; + if (distanceInCentimeters < proximitySensor.getMaximumRange()) { + Log.d(TAG, "Proximity sensor => NEAR viewState"); + lastStateReportIsNear = true; + } else { + Log.d(TAG, "Proximity sensor => FAR viewState"); + lastStateReportIsNear = false; + } + + // Report about new viewState to listening client. Client can then call + // sensorReportsNearState() to query the current viewState (NEAR or FAR). + if (onSensorStateListener != null) { + onSensorStateListener.run(); + } } + } - /** - * Getter for last reported viewState. Set to true if "near" is reported. - */ - boolean sensorReportsNearState() { - threadChecker.checkIsOnValidThread(); - return lastStateReportIsNear; + /** + * Get default proximity sensor if it exists. Tablet devices (e.g. Nexus 7) + * does not support this type of sensor and false will be returned in such + * cases. + */ + private boolean initDefaultSensor() { + if (proximitySensor != null) { + return true; } - - @Override - public final void onAccuracyChanged(Sensor sensor, int accuracy) { - threadChecker.checkIsOnValidThread(); - if (sensor.getType() == Sensor.TYPE_PROXIMITY && - accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) { - Log.e(TAG, "The values returned by this sensor cannot be trusted"); - } + proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + if (proximitySensor == null) { + return false; } + logProximitySensorInfo(); + return true; + } - @Override - public final void onSensorChanged(SensorEvent event) { - threadChecker.checkIsOnValidThread(); - if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) { - // As a best practice; do as little as possible within this method and - // avoid blocking. - float distanceInCentimeters = event.values[0]; - if (distanceInCentimeters < proximitySensor.getMaximumRange()) { - Log.d(TAG, "Proximity sensor => NEAR viewState"); - lastStateReportIsNear = true; - } else { - Log.d(TAG, "Proximity sensor => FAR viewState"); - lastStateReportIsNear = false; - } - - // Report about new viewState to listening client. Client can then call - // sensorReportsNearState() to query the current viewState (NEAR or FAR). - if (onSensorStateListener != null) { - onSensorStateListener.run(); - } - } + /** + * Helper method for logging information about the proximity sensor. + */ + private void logProximitySensorInfo() { + if (proximitySensor == null) { + return; } - - /** - * Get default proximity sensor if it exists. Tablet devices (e.g. Nexus 7) - * does not support this type of sensor and false will be returned in such - * cases. - */ - private boolean initDefaultSensor() { - if (proximitySensor != null) { - return true; - } - proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); - if (proximitySensor == null) { - return false; - } - logProximitySensorInfo(); - return true; - } - - /** - * Helper method for logging information about the proximity sensor. - */ - private void logProximitySensorInfo() { - if (proximitySensor == null) { - return; - } - StringBuilder info = new StringBuilder("Proximity sensor: "); - info.append("name=").append(proximitySensor.getName()) - .append(", vendor: ").append(proximitySensor.getVendor()) - .append(", power: ").append(proximitySensor.getPower()) - .append(", resolution: ").append(proximitySensor.getResolution()) - .append(", max range: ").append(proximitySensor.getMaximumRange()) - .append(", min delay: ").append(proximitySensor.getMinDelay()); - // Added in API level 20. - info.append(", type: ").append(proximitySensor.getStringType()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Added in API level 21. - info.append(", max delay: ").append(proximitySensor.getMaxDelay()) - .append(", reporting mode: ").append(proximitySensor.getReportingMode()) - .append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor()); - } - Log.d(TAG, info.toString()); + StringBuilder info = new StringBuilder("Proximity sensor: "); + info.append("name=").append(proximitySensor.getName()) + .append(", vendor: ").append(proximitySensor.getVendor()) + .append(", power: ").append(proximitySensor.getPower()) + .append(", resolution: ").append(proximitySensor.getResolution()) + .append(", max range: ").append(proximitySensor.getMaximumRange()) + .append(", min delay: ").append(proximitySensor.getMinDelay()); + // Added in API level 20. + info.append(", type: ").append(proximitySensor.getStringType()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // Added in API level 21. + info.append(", max delay: ").append(proximitySensor.getMaxDelay()) + .append(", reporting mode: ").append(proximitySensor.getReportingMode()) + .append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor()); } + Log.d(TAG, info.toString()); + } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java index cad85ca5b..96236b808 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebRTCUtils.java @@ -43,142 +43,140 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; public class MagicWebRTCUtils { - private static final String TAG = "MagicWebRTCUtils"; + private static final String TAG = "MagicWebRTCUtils"; - /* AEC blacklist and SL_ES_WHITELIST are borrowed from Signal - https://github.com/WhisperSystems/Signal-Android/blob/551470123d006b76a68d705d131bb12513a5e683/src/org/thoughtcrime/securesms/ApplicationContext.java - */ - public static Set HARDWARE_AEC_BLACKLIST = new HashSet() {{ - add("D6503"); // Sony Xperia Z2 D6503 - add("ONE A2005"); // OnePlus 2 - add("MotoG3"); // Moto G (3rd Generation) - add("Nexus 6P"); // Nexus 6p - add("Pixel"); // Pixel - add("Pixel XL"); // Pixel XL - add("MI 4LTE"); // Xiami Mi4 - add("Redmi Note 3"); // Redmi Note 3 - add("Redmi Note 4"); // Redmi Note 4 - add("SM-G900F"); // Samsung Galaxy S5 - add("g3_kt_kr"); // LG G3 - add("GT-I9195"); // Samsung Galaxy S4 Mini 4G LTE - add("SM-G930F"); // Samsung Galaxy S7 - add("Xperia SP"); // Sony Xperia SP - add("Nexus 6"); // Nexus 6 - add("ONE E1003"); // OnePlus X - add("One"); // OnePlus One - add("Moto G5"); - add("Moto G (5S) Plus"); - add("Moto G4"); - add("TA-1053"); - add("E5823"); // Sony Z5 Compact - }}; + /* AEC blacklist and SL_ES_WHITELIST are borrowed from Signal + https://github.com/WhisperSystems/Signal-Android/blob/551470123d006b76a68d705d131bb12513a5e683/src/org/thoughtcrime/securesms/ApplicationContext.java + */ + public static Set HARDWARE_AEC_BLACKLIST = new HashSet() {{ + add("D6503"); // Sony Xperia Z2 D6503 + add("ONE A2005"); // OnePlus 2 + add("MotoG3"); // Moto G (3rd Generation) + add("Nexus 6P"); // Nexus 6p + add("Pixel"); // Pixel + add("Pixel XL"); // Pixel XL + add("MI 4LTE"); // Xiami Mi4 + add("Redmi Note 3"); // Redmi Note 3 + add("Redmi Note 4"); // Redmi Note 4 + add("SM-G900F"); // Samsung Galaxy S5 + add("g3_kt_kr"); // LG G3 + add("GT-I9195"); // Samsung Galaxy S4 Mini 4G LTE + add("SM-G930F"); // Samsung Galaxy S7 + add("Xperia SP"); // Sony Xperia SP + add("Nexus 6"); // Nexus 6 + add("ONE E1003"); // OnePlus X + add("One"); // OnePlus One + add("Moto G5"); + add("Moto G (5S) Plus"); + add("Moto G4"); + add("TA-1053"); + add("E5823"); // Sony Z5 Compact + }}; - public static Set OPEN_SL_ES_WHITELIST = new HashSet() {{ - add("Pixel"); - add("Pixel XL"); - }}; + public static Set OPEN_SL_ES_WHITELIST = new HashSet() {{ + add("Pixel"); + add("Pixel XL"); + }}; - private static Set HARDWARE_ACCELERATION_DEVICE_BLACKLIST = new HashSet() {{ - add("GT-I9100"); // Samsung Galaxy S2 - add("GT-N8013"); // Samsung Galaxy Note 10.1 - add("SM-G930F"); // Samsung Galaxy S7 - add("AGS-W09"); // Huawei MediaPad T3 10 - add("MIX 2"); // Xiaomi Mi Mix 2 - add("HUAWEI VNS-L31"); // Huawei P9 Lite - add("ALE-L21"); // Huawei P8 Lite - add("Z380M"); // Asus ZenPad 8.0 - add("XT1097"); // Motorola Moto X (2nd Gen) - }}; + private static Set HARDWARE_ACCELERATION_DEVICE_BLACKLIST = new HashSet() {{ + add("GT-I9100"); // Samsung Galaxy S2 + add("GT-N8013"); // Samsung Galaxy Note 10.1 + add("SM-G930F"); // Samsung Galaxy S7 + add("AGS-W09"); // Huawei MediaPad T3 10 + add("MIX 2"); // Xiaomi Mi Mix 2 + add("HUAWEI VNS-L31"); // Huawei P9 Lite + add("ALE-L21"); // Huawei P8 Lite + add("Z380M"); // Asus ZenPad 8.0 + add("XT1097"); // Motorola Moto X (2nd Gen) + }}; - private static Set HARDWARE_ACCELERATION_VENDOR_BLACKLIST = new HashSet() {{ - add("samsung"); - }}; + private static Set HARDWARE_ACCELERATION_VENDOR_BLACKLIST = new HashSet() {{ + add("samsung"); + }}; + public static boolean shouldEnableVideoHardwareAcceleration() { + return (!HARDWARE_ACCELERATION_VENDOR_BLACKLIST.contains(Build.MANUFACTURER.toLowerCase()) + && !HARDWARE_ACCELERATION_DEVICE_BLACKLIST.contains(Build.MODEL.toUpperCase())); + } - public static boolean shouldEnableVideoHardwareAcceleration() { - return (!HARDWARE_ACCELERATION_VENDOR_BLACKLIST.contains(Build.MANUFACTURER.toLowerCase()) - && !HARDWARE_ACCELERATION_DEVICE_BLACKLIST.contains(Build.MODEL.toUpperCase())); + public static String preferCodec(String sdpDescription, String codec, boolean isAudio) { + final String[] lines = sdpDescription.split("\r\n"); + final int mLineIndex = findMediaDescriptionLine(isAudio, lines); + if (mLineIndex == -1) { + Log.w(TAG, "No mediaDescription line, so can't prefer " + codec); + return sdpDescription; + } + // A list with all the payload types with name |codec|. The payload types are integers in the + // range 96-127, but they are stored as strings here. + final List codecPayloadTypes = new ArrayList(); + // a=rtpmap: / [/] + final Pattern codecPattern = Pattern.compile("^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$"); + for (int i = 0; i < lines.length; ++i) { + Matcher codecMatcher = codecPattern.matcher(lines[i]); + if (codecMatcher.matches()) { + codecPayloadTypes.add(codecMatcher.group(1)); + } + } + if (codecPayloadTypes.isEmpty()) { + Log.w(TAG, "No payload types with name " + codec); + return sdpDescription; } - public static String preferCodec(String sdpDescription, String codec, boolean isAudio) { - final String[] lines = sdpDescription.split("\r\n"); - final int mLineIndex = findMediaDescriptionLine(isAudio, lines); - if (mLineIndex == -1) { - Log.w(TAG, "No mediaDescription line, so can't prefer " + codec); - return sdpDescription; - } - // A list with all the payload types with name |codec|. The payload types are integers in the - // range 96-127, but they are stored as strings here. - final List codecPayloadTypes = new ArrayList(); - // a=rtpmap: / [/] - final Pattern codecPattern = Pattern.compile("^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$"); - for (int i = 0; i < lines.length; ++i) { - Matcher codecMatcher = codecPattern.matcher(lines[i]); - if (codecMatcher.matches()) { - codecPayloadTypes.add(codecMatcher.group(1)); - } - } - if (codecPayloadTypes.isEmpty()) { - Log.w(TAG, "No payload types with name " + codec); - return sdpDescription; - } - - final String newMLine = movePayloadTypesToFront(codecPayloadTypes, lines[mLineIndex]); - if (newMLine == null) { - return sdpDescription; - } - Log.d(TAG, "Change media description from: " + lines[mLineIndex] + " to " + newMLine); - lines[mLineIndex] = newMLine; - return joinString(Arrays.asList(lines), "\r\n", true); + final String newMLine = movePayloadTypesToFront(codecPayloadTypes, lines[mLineIndex]); + if (newMLine == null) { + return sdpDescription; } + Log.d(TAG, "Change media description from: " + lines[mLineIndex] + " to " + newMLine); + lines[mLineIndex] = newMLine; + return joinString(Arrays.asList(lines), "\r\n", true); + } - /** - * Returns the line number containing "m=audio|video", or -1 if no such line exists. - */ - private static int findMediaDescriptionLine(boolean isAudio, String[] sdpLines) { - final String mediaDescription = isAudio ? "m=audio " : "m=video "; - for (int i = 0; i < sdpLines.length; ++i) { - if (sdpLines[i].startsWith(mediaDescription)) { - return i; - } - } - return -1; + /** + * Returns the line number containing "m=audio|video", or -1 if no such line exists. + */ + private static int findMediaDescriptionLine(boolean isAudio, String[] sdpLines) { + final String mediaDescription = isAudio ? "m=audio " : "m=video "; + for (int i = 0; i < sdpLines.length; ++i) { + if (sdpLines[i].startsWith(mediaDescription)) { + return i; + } } + return -1; + } - private static String movePayloadTypesToFront(List preferredPayloadTypes, String mLine) { - // The format of the media description line should be: m= ... - final List origLineParts = Arrays.asList(mLine.split(" ")); - if (origLineParts.size() <= 3) { - Log.e(TAG, "Wrong SDP media description format: " + mLine); - return null; - } - final List header = origLineParts.subList(0, 3); - final List unpreferredPayloadTypes = - new ArrayList(origLineParts.subList(3, origLineParts.size())); - unpreferredPayloadTypes.removeAll(preferredPayloadTypes); - // Reconstruct the line with |preferredPayloadTypes| moved to the beginning of the payload - // types. - final List newLineParts = new ArrayList(); - newLineParts.addAll(header); - newLineParts.addAll(preferredPayloadTypes); - newLineParts.addAll(unpreferredPayloadTypes); - return joinString(newLineParts, " ", false /* delimiterAtEnd */); + private static String movePayloadTypesToFront(List preferredPayloadTypes, String mLine) { + // The format of the media description line should be: m= ... + final List origLineParts = Arrays.asList(mLine.split(" ")); + if (origLineParts.size() <= 3) { + Log.e(TAG, "Wrong SDP media description format: " + mLine); + return null; } + final List header = origLineParts.subList(0, 3); + final List unpreferredPayloadTypes = + new ArrayList(origLineParts.subList(3, origLineParts.size())); + unpreferredPayloadTypes.removeAll(preferredPayloadTypes); + // Reconstruct the line with |preferredPayloadTypes| moved to the beginning of the payload + // types. + final List newLineParts = new ArrayList(); + newLineParts.addAll(header); + newLineParts.addAll(preferredPayloadTypes); + newLineParts.addAll(unpreferredPayloadTypes); + return joinString(newLineParts, " ", false /* delimiterAtEnd */); + } - private static String joinString( - Iterable s, String delimiter, boolean delimiterAtEnd) { - Iterator iter = s.iterator(); - if (!iter.hasNext()) { - return ""; - } - StringBuilder buffer = new StringBuilder(iter.next()); - while (iter.hasNext()) { - buffer.append(delimiter).append(iter.next()); - } - if (delimiterAtEnd) { - buffer.append(delimiter); - } - return buffer.toString(); + private static String joinString( + Iterable s, String delimiter, boolean delimiterAtEnd) { + Iterator iter = s.iterator(); + if (!iter.hasNext()) { + return ""; } - + StringBuilder buffer = new StringBuilder(iter.next()); + while (iter.hasNext()) { + buffer.append(delimiter).append(iter.next()); + } + if (delimiterAtEnd) { + buffer.append(delimiter); + } + return buffer.toString(); + } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java index aafcd9b0e..bfcc0e709 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java @@ -61,365 +61,412 @@ import org.greenrobot.eventbus.ThreadMode; @AutoInjector(NextcloudTalkApplication.class) public class MagicWebSocketInstance extends WebSocketListener { - private static final String TAG = "MagicWebSocketInstance"; + private static final String TAG = "MagicWebSocketInstance"; - @Inject - OkHttpClient okHttpClient; + @Inject + OkHttpClient okHttpClient; - @Inject - EventBus eventBus; + @Inject + EventBus eventBus; - @Inject - Context context; + @Inject + Context context; - private UserEntity conversationUser; - private String webSocketTicket; - private String resumeId; - private String sessionId; - private boolean hasMCU; - private boolean connected; - private WebSocketConnectionHelper webSocketConnectionHelper; - private WebSocket internalWebSocket; - private MagicMap magicMap; - private String connectionUrl; + private UserEntity conversationUser; + private String webSocketTicket; + private String resumeId; + private String sessionId; + private boolean hasMCU; + private boolean connected; + private WebSocketConnectionHelper webSocketConnectionHelper; + private WebSocket internalWebSocket; + private MagicMap magicMap; + private String connectionUrl; - private String currentRoomToken; - private int restartCount = 0; - private boolean reconnecting = false; + private String currentRoomToken; + private int restartCount = 0; + private boolean reconnecting = false; - private HashMap usersHashMap; + private HashMap usersHashMap; - private List messagesQueue = new ArrayList<>(); + private List messagesQueue = new ArrayList<>(); - MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl, String webSocketTicket) { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl, + String webSocketTicket) { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); - this.connectionUrl = connectionUrl; - this.conversationUser = conversationUser; - this.webSocketTicket = webSocketTicket; - this.webSocketConnectionHelper = new WebSocketConnectionHelper(); - this.usersHashMap = new HashMap<>(); - magicMap = new MagicMap(); + this.connectionUrl = connectionUrl; + this.conversationUser = conversationUser; + this.webSocketTicket = webSocketTicket; + this.webSocketConnectionHelper = new WebSocketConnectionHelper(); + this.usersHashMap = new HashMap<>(); + magicMap = new MagicMap(); - connected = false; - eventBus.register(this); + connected = false; + eventBus.register(this); - restartWebSocket(); + restartWebSocket(); + } + + private void sendHello() { + try { + if (TextUtils.isEmpty(resumeId)) { + internalWebSocket.send(LoganSquare.serialize( + webSocketConnectionHelper.getAssembledHelloModel(conversationUser, webSocketTicket))); + } else { + internalWebSocket.send(LoganSquare.serialize( + webSocketConnectionHelper.getAssembledHelloModelForResume(resumeId))); + } + } catch (IOException e) { + Log.e(TAG, "Failed to serialize hello model"); + } + } + + @Override + public void onOpen(WebSocket webSocket, Response response) { + internalWebSocket = webSocket; + sendHello(); + } + + private void closeWebSocket(WebSocket webSocket) { + webSocket.close(1000, null); + webSocket.cancel(); + if (webSocket == internalWebSocket) { + connected = false; + messagesQueue = new ArrayList<>(); } - private void sendHello() { - try { - if (TextUtils.isEmpty(resumeId)) { - internalWebSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModel(conversationUser, webSocketTicket))); - } else { - internalWebSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModelForResume(resumeId))); + restartWebSocket(); + } + + public void clearResumeId() { + resumeId = ""; + } + + public void restartWebSocket() { + reconnecting = true; + + Request request = new Request.Builder().url(connectionUrl).build(); + okHttpClient.newWebSocket(request, this); + restartCount++; + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + if (webSocket == internalWebSocket) { + Log.d(TAG, "Receiving : " + webSocket.toString() + " " + text); + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "WebSocket " + webSocket.hashCode() + " receiving: " + text); + + try { + BaseWebSocketMessage baseWebSocketMessage = + LoganSquare.parse(text, BaseWebSocketMessage.class); + String messageType = baseWebSocketMessage.getType(); + switch (messageType) { + case "hello": + connected = true; + reconnecting = false; + restartCount = 0; + String oldResumeId = resumeId; + HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage = + LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class); + resumeId = + helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId(); + sessionId = + helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId(); + hasMCU = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage() + .serverHasMCUSupport(); + + for (int i = 0; i < messagesQueue.size(); i++) { + webSocket.send(messagesQueue.get(i)); } - } catch (IOException e) { - Log.e(TAG, "Failed to serialize hello model"); - } - } - @Override - public void onOpen(WebSocket webSocket, Response response) { - internalWebSocket = webSocket; - sendHello(); - } - - private void closeWebSocket(WebSocket webSocket) { - webSocket.close(1000, null); - webSocket.cancel(); - if (webSocket == internalWebSocket) { - connected = false; messagesQueue = new ArrayList<>(); - } - - restartWebSocket(); - } - - - public void clearResumeId() { - resumeId = ""; - } - - public void restartWebSocket() { - reconnecting = true; - - Request request = new Request.Builder().url(connectionUrl).build(); - okHttpClient.newWebSocket(request, this); - restartCount++; - } - - @Override - public void onMessage(WebSocket webSocket, String text) { - if (webSocket == internalWebSocket) { - Log.d(TAG, "Receiving : " + webSocket.toString() + " " + text); - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "WebSocket " + webSocket.hashCode() + " receiving: " + text); - - try { - BaseWebSocketMessage baseWebSocketMessage = LoganSquare.parse(text, BaseWebSocketMessage.class); - String messageType = baseWebSocketMessage.getType(); - switch (messageType) { - case "hello": - connected = true; - reconnecting = false; - restartCount = 0; - String oldResumeId = resumeId; - HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage = LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class); - resumeId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId(); - sessionId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId(); - hasMCU = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().serverHasMCUSupport(); - - for (int i = 0; i < messagesQueue.size(); i++) { - webSocket.send(messagesQueue.get(i)); - } - - messagesQueue = new ArrayList<>(); - HashMap helloHasHap = new HashMap<>(); - if (!TextUtils.isEmpty(oldResumeId)) { - helloHasHap.put("oldResumeId", oldResumeId); - } else { - currentRoomToken = ""; - } - - if (!TextUtils.isEmpty(currentRoomToken)) { - helloHasHap.put("roomToken", currentRoomToken); - } - eventBus.post(new WebSocketCommunicationEvent("hello", helloHasHap)); - break; - case "error": - ErrorOverallWebSocketMessage errorOverallWebSocketMessage = LoganSquare.parse(text, ErrorOverallWebSocketMessage.class); - if (("no_such_session").equals(errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) { - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "WebSocket " + webSocket.hashCode() + " resumeID " + resumeId + " expired"); - resumeId = ""; - currentRoomToken = ""; - restartWebSocket(); - } else if (("hello_expected").equals(errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) { - restartWebSocket(); - } - - break; - case "room": - JoinedRoomOverallWebSocketMessage joinedRoomOverallWebSocketMessage = LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage.class); - currentRoomToken = joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage().getRoomId(); - if (joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage().getRoomPropertiesWebSocketMessage() != null && !TextUtils.isEmpty(currentRoomToken)) { - sendRoomJoinedEvent(); - } - break; - case "event": - EventOverallWebSocketMessage eventOverallWebSocketMessage = LoganSquare.parse(text, EventOverallWebSocketMessage.class); - if (eventOverallWebSocketMessage.getEventMap() != null) { - String target = (String) eventOverallWebSocketMessage.getEventMap().get("target"); - switch (target) { - case "room": - if (eventOverallWebSocketMessage.getEventMap().get("type").equals("message")) { - Map messageHashMap = - (Map) eventOverallWebSocketMessage.getEventMap().get("message"); - if (messageHashMap.containsKey("data")) { - Map dataHashMap = (Map) messageHashMap.get( - "data"); - if (dataHashMap.containsKey("chat")) { - boolean shouldRefreshChat; - Map chatMap = (Map) dataHashMap.get("chat"); - if (chatMap.containsKey("refresh")) { - shouldRefreshChat = (boolean) chatMap.get("refresh"); - if (shouldRefreshChat) { - HashMap refreshChatHashMap = new HashMap<>(); - refreshChatHashMap.put(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), (String) messageHashMap.get("roomid")); - refreshChatHashMap.put(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), Long.toString(conversationUser.getId())); - eventBus.post(new WebSocketCommunicationEvent("refreshChat", refreshChatHashMap)); - } - } - } - } - } else if (eventOverallWebSocketMessage.getEventMap().get("type").equals("join")) { - List> joinEventMap = (List>) eventOverallWebSocketMessage.getEventMap().get("join"); - HashMap internalHashMap; - Participant participant; - for (int i = 0; i < joinEventMap.size(); i++) { - internalHashMap = joinEventMap.get(i); - HashMap userMap = (HashMap) internalHashMap.get("user"); - participant = new Participant(); - participant.setUserId((String) internalHashMap.get("userid")); - participant.setDisplayName((String) userMap.get("displayname")); - usersHashMap.put((String) internalHashMap.get("sessionid"), participant); - } - } - break; - case "participants": - if (eventOverallWebSocketMessage.getEventMap().get("type").equals("update")) { - HashMap refreshChatHashMap = new HashMap<>(); - HashMap updateEventMap = (HashMap) eventOverallWebSocketMessage.getEventMap().get("update"); - refreshChatHashMap.put("roomToken", (String) updateEventMap.get("roomid")); - refreshChatHashMap.put("jobId", Integer.toString(magicMap.add(updateEventMap.get("users")))); - eventBus.post(new WebSocketCommunicationEvent("participantsUpdate", refreshChatHashMap)); - } - break; - } - } - break; - case "message": - CallOverallWebSocketMessage callOverallWebSocketMessage = LoganSquare.parse(text, CallOverallWebSocketMessage.class); - NCSignalingMessage ncSignalingMessage = callOverallWebSocketMessage.getCallWebSocketMessage().getNcSignalingMessage(); - if (TextUtils.isEmpty(ncSignalingMessage.getFrom()) && callOverallWebSocketMessage.getCallWebSocketMessage().getSenderWebSocketMessage() != null) { - ncSignalingMessage.setFrom(callOverallWebSocketMessage.getCallWebSocketMessage().getSenderWebSocketMessage().getSessionId()); - } - - if (!TextUtils.isEmpty(ncSignalingMessage.getFrom())) { - HashMap messageHashMap = new HashMap<>(); - messageHashMap.put("jobId", Integer.toString(magicMap.add(ncSignalingMessage))); - eventBus.post(new WebSocketCommunicationEvent("signalingMessage", messageHashMap)); - } - break; - case "bye": - connected = false; - resumeId = ""; - default: - break; - } - } catch (IOException e) { - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "WebSocket " + webSocket.hashCode() + " IOException: " + e.getMessage()); - Log.e(TAG, "Failed to recognize WebSocket message"); + HashMap helloHasHap = new HashMap<>(); + if (!TextUtils.isEmpty(oldResumeId)) { + helloHasHap.put("oldResumeId", oldResumeId); + } else { + currentRoomToken = ""; } + + if (!TextUtils.isEmpty(currentRoomToken)) { + helloHasHap.put("roomToken", currentRoomToken); + } + eventBus.post(new WebSocketCommunicationEvent("hello", helloHasHap)); + break; + case "error": + ErrorOverallWebSocketMessage errorOverallWebSocketMessage = + LoganSquare.parse(text, ErrorOverallWebSocketMessage.class); + if (("no_such_session").equals( + errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) { + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "WebSocket " + webSocket.hashCode() + " resumeID " + resumeId + " expired"); + resumeId = ""; + currentRoomToken = ""; + restartWebSocket(); + } else if (("hello_expected").equals( + errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) { + restartWebSocket(); + } + + break; + case "room": + JoinedRoomOverallWebSocketMessage joinedRoomOverallWebSocketMessage = + LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage.class); + currentRoomToken = + joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage().getRoomId(); + if (joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage() + .getRoomPropertiesWebSocketMessage() != null && !TextUtils.isEmpty( + currentRoomToken)) { + sendRoomJoinedEvent(); + } + break; + case "event": + EventOverallWebSocketMessage eventOverallWebSocketMessage = + LoganSquare.parse(text, EventOverallWebSocketMessage.class); + if (eventOverallWebSocketMessage.getEventMap() != null) { + String target = (String) eventOverallWebSocketMessage.getEventMap().get("target"); + switch (target) { + case "room": + if (eventOverallWebSocketMessage.getEventMap().get("type").equals("message")) { + Map messageHashMap = + (Map) eventOverallWebSocketMessage.getEventMap() + .get("message"); + if (messageHashMap.containsKey("data")) { + Map dataHashMap = (Map) messageHashMap.get( + "data"); + if (dataHashMap.containsKey("chat")) { + boolean shouldRefreshChat; + Map chatMap = (Map) dataHashMap.get("chat"); + if (chatMap.containsKey("refresh")) { + shouldRefreshChat = (boolean) chatMap.get("refresh"); + if (shouldRefreshChat) { + HashMap refreshChatHashMap = new HashMap<>(); + refreshChatHashMap.put(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), + (String) messageHashMap.get("roomid")); + refreshChatHashMap.put(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), + Long.toString(conversationUser.getId())); + eventBus.post( + new WebSocketCommunicationEvent("refreshChat", refreshChatHashMap)); + } + } + } + } + } else if (eventOverallWebSocketMessage.getEventMap() + .get("type") + .equals("join")) { + List> joinEventMap = + (List>) eventOverallWebSocketMessage.getEventMap() + .get("join"); + HashMap internalHashMap; + Participant participant; + for (int i = 0; i < joinEventMap.size(); i++) { + internalHashMap = joinEventMap.get(i); + HashMap userMap = + (HashMap) internalHashMap.get("user"); + participant = new Participant(); + participant.setUserId((String) internalHashMap.get("userid")); + participant.setDisplayName((String) userMap.get("displayname")); + usersHashMap.put((String) internalHashMap.get("sessionid"), participant); + } + } + break; + case "participants": + if (eventOverallWebSocketMessage.getEventMap().get("type").equals("update")) { + HashMap refreshChatHashMap = new HashMap<>(); + HashMap updateEventMap = + (HashMap) eventOverallWebSocketMessage.getEventMap() + .get("update"); + refreshChatHashMap.put("roomToken", (String) updateEventMap.get("roomid")); + refreshChatHashMap.put("jobId", + Integer.toString(magicMap.add(updateEventMap.get("users")))); + eventBus.post( + new WebSocketCommunicationEvent("participantsUpdate", refreshChatHashMap)); + } + break; + } + } + break; + case "message": + CallOverallWebSocketMessage callOverallWebSocketMessage = + LoganSquare.parse(text, CallOverallWebSocketMessage.class); + NCSignalingMessage ncSignalingMessage = + callOverallWebSocketMessage.getCallWebSocketMessage().getNcSignalingMessage(); + if (TextUtils.isEmpty(ncSignalingMessage.getFrom()) + && callOverallWebSocketMessage.getCallWebSocketMessage().getSenderWebSocketMessage() + != null) { + ncSignalingMessage.setFrom(callOverallWebSocketMessage.getCallWebSocketMessage() + .getSenderWebSocketMessage() + .getSessionId()); + } + + if (!TextUtils.isEmpty(ncSignalingMessage.getFrom())) { + HashMap messageHashMap = new HashMap<>(); + messageHashMap.put("jobId", Integer.toString(magicMap.add(ncSignalingMessage))); + eventBus.post(new WebSocketCommunicationEvent("signalingMessage", messageHashMap)); + } + break; + case "bye": + connected = false; + resumeId = ""; + default: + break; } - } - - private void sendRoomJoinedEvent() { - HashMap joinRoomHashMap = new HashMap<>(); - joinRoomHashMap.put("roomToken", currentRoomToken); - eventBus.post(new WebSocketCommunicationEvent("roomJoined", joinRoomHashMap)); - } - - @Override - public void onMessage(WebSocket webSocket, ByteString bytes) { - Log.d(TAG, "Receiving bytes : " + bytes.hex()); - } - - @Override - public void onClosing(WebSocket webSocket, int code, String reason) { - Log.d(TAG, "Closing : " + code + " / " + reason); + } catch (IOException e) { LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "WebSocket " + webSocket.hashCode() + " Closing: " + reason); + "WebSocket " + webSocket.hashCode() + " IOException: " + e.getMessage()); + Log.e(TAG, "Failed to recognize WebSocket message"); + } } + } - @Override - public void onFailure(WebSocket webSocket, Throwable t, Response response) { - Log.d(TAG, "Error : " + t.getMessage()); - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "WebSocket " + webSocket.hashCode() + " onFailure: " + t.getMessage()); - closeWebSocket(webSocket); - } + private void sendRoomJoinedEvent() { + HashMap joinRoomHashMap = new HashMap<>(); + joinRoomHashMap.put("roomToken", currentRoomToken); + eventBus.post(new WebSocketCommunicationEvent("roomJoined", joinRoomHashMap)); + } - public String getSessionId() { - return sessionId; - } + @Override + public void onMessage(WebSocket webSocket, ByteString bytes) { + Log.d(TAG, "Receiving bytes : " + bytes.hex()); + } - public boolean hasMCU() { - return hasMCU; - } + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + Log.d(TAG, "Closing : " + code + " / " + reason); + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "WebSocket " + webSocket.hashCode() + " Closing: " + reason); + } - public void joinRoomWithRoomTokenAndSession(String roomToken, String normalBackendSession) { - try { - String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession)); - if (!connected || reconnecting) { - messagesQueue.add(message); - } else { - if (roomToken.equals(currentRoomToken)) { - sendRoomJoinedEvent(); - } else { - internalWebSocket.send(message); - } - } - } catch (IOException e) { - e.printStackTrace(); + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + Log.d(TAG, "Error : " + t.getMessage()); + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "WebSocket " + webSocket.hashCode() + " onFailure: " + t.getMessage()); + closeWebSocket(webSocket); + } + + public String getSessionId() { + return sessionId; + } + + public boolean hasMCU() { + return hasMCU; + } + + public void joinRoomWithRoomTokenAndSession(String roomToken, String normalBackendSession) { + try { + String message = LoganSquare.serialize( + webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, + normalBackendSession)); + if (!connected || reconnecting) { + messagesQueue.add(message); + } else { + if (roomToken.equals(currentRoomToken)) { + sendRoomJoinedEvent(); + } else { + internalWebSocket.send(message); } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void sendCallMessage(NCMessageWrapper ncMessageWrapper) { + try { + String message = LoganSquare.serialize( + webSocketConnectionHelper.getAssembledCallMessageModel(ncMessageWrapper)); + if (!connected || reconnecting) { + messagesQueue.add(message); + } else { + internalWebSocket.send(message); + } + } catch (IOException e) { + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "WebSocket sendCalLMessage: " + e.getMessage() + "\n" + ncMessageWrapper.toString()); + Log.e(TAG, "Failed to serialize signaling message"); + } + } + + public Object getJobWithId(Integer id) { + Object copyJob = magicMap.get(id); + magicMap.remove(id); + return copyJob; + } + + public void requestOfferForSessionIdWithType(String sessionIdParam, String roomType) { + try { + String message = LoganSquare.serialize( + webSocketConnectionHelper.getAssembledRequestOfferModel(sessionIdParam, roomType)); + if (!connected || reconnecting) { + messagesQueue.add(message); + } else { + internalWebSocket.send(message); + } + } catch (IOException e) { + LoggingUtils.INSTANCE.writeLogEntryToFile(context, + "WebSocket requestOfferForSessionIdWithType: " + + e.getMessage() + + "\n" + + sessionIdParam + + " " + + roomType); + Log.e(TAG, "Failed to offer request"); + } + } + + void sendBye() { + if (connected) { + try { + ByeWebSocketMessage byeWebSocketMessage = new ByeWebSocketMessage(); + byeWebSocketMessage.setType("bye"); + byeWebSocketMessage.setBye(new HashMap<>()); + internalWebSocket.send(LoganSquare.serialize(byeWebSocketMessage)); + } catch (IOException e) { + Log.e(TAG, "Failed to serialize bye message"); + } + } + } + + public boolean isConnected() { + return connected; + } + + public String getDisplayNameForSession(String session) { + if (usersHashMap.containsKey(session)) { + return usersHashMap.get(session).getDisplayName(); } - public void sendCallMessage(NCMessageWrapper ncMessageWrapper) { - try { - String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledCallMessageModel(ncMessageWrapper)); - if (!connected || reconnecting) { - messagesQueue.add(message); - } else { - internalWebSocket.send(message); - } - } catch (IOException e) { - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "WebSocket sendCalLMessage: " + e.getMessage() + "\n" + ncMessageWrapper.toString()); - Log.e(TAG, "Failed to serialize signaling message"); - } + return NextcloudTalkApplication.Companion.getSharedApplication() + .getString(R.string.nc_nick_guest); + } + + public String getSessionForUserId(String userId) { + for (String session : usersHashMap.keySet()) { + if (userId.equals(usersHashMap.get(session).getUserId())) { + return session; + } } - public Object getJobWithId(Integer id) { - Object copyJob = magicMap.get(id); - magicMap.remove(id); - return copyJob; + return ""; + } + + public String getUserIdForSession(String session) { + if (usersHashMap.containsKey(session)) { + return usersHashMap.get(session).getUserId(); } - public void requestOfferForSessionIdWithType(String sessionIdParam, String roomType) { - try { - String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledRequestOfferModel(sessionIdParam, roomType)); - if (!connected || reconnecting) { - messagesQueue.add(message); - } else { - internalWebSocket.send(message); - } - } catch (IOException e) { - LoggingUtils.INSTANCE.writeLogEntryToFile(context, - "WebSocket requestOfferForSessionIdWithType: " + e.getMessage() + "\n" + sessionIdParam + " " + roomType); - Log.e(TAG, "Failed to offer request"); - } - } - - void sendBye() { - if (connected) { - try { - ByeWebSocketMessage byeWebSocketMessage = new ByeWebSocketMessage(); - byeWebSocketMessage.setType("bye"); - byeWebSocketMessage.setBye(new HashMap<>()); - internalWebSocket.send(LoganSquare.serialize(byeWebSocketMessage)); - } catch (IOException e) { - Log.e(TAG, "Failed to serialize bye message"); - } - } - } - - public boolean isConnected() { - return connected; - } - - public String getDisplayNameForSession(String session) { - if (usersHashMap.containsKey(session)) { - return usersHashMap.get(session).getDisplayName(); - } - - return NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_nick_guest); - } - - public String getSessionForUserId(String userId) { - for (String session : usersHashMap.keySet()) { - if (userId.equals(usersHashMap.get(session).getUserId())) { - return session; - } - } - - return ""; - } - - public String getUserIdForSession(String session) { - if (usersHashMap.containsKey(session)) { - return usersHashMap.get(session).getUserId(); - } - - return ""; - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(NetworkEvent networkEvent) { - if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED) && !isConnected()) { - restartWebSocket(); - } + return ""; + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(NetworkEvent networkEvent) { + if (networkEvent.getNetworkConnectionEvent() + .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED) && !isConnected()) { + restartWebSocket(); } + } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java index 1be7a785f..cf901ff0b 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java @@ -44,128 +44,140 @@ import okhttp3.OkHttpClient; @AutoInjector(NextcloudTalkApplication.class) public class WebSocketConnectionHelper { - private static Map magicWebSocketInstanceMap = new HashMap<>(); + private static Map magicWebSocketInstanceMap = new HashMap<>(); - @Inject - OkHttpClient okHttpClient; + @Inject + OkHttpClient okHttpClient; + public WebSocketConnectionHelper() { + NextcloudTalkApplication.Companion.getSharedApplication() + .getComponentApplication() + .inject(this); + } - public WebSocketConnectionHelper() { - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + public static synchronized MagicWebSocketInstance getMagicWebSocketInstanceForUserId( + long userId) { + if (userId != -1 && magicWebSocketInstanceMap.containsKey(userId)) { + return magicWebSocketInstanceMap.get(userId); } - public static synchronized MagicWebSocketInstance getMagicWebSocketInstanceForUserId(long userId) { - if (userId != -1 && magicWebSocketInstanceMap.containsKey(userId)) { - return magicWebSocketInstanceMap.get(userId); - } + return null; + } - return null; + public static synchronized MagicWebSocketInstance getExternalSignalingInstanceForServer( + String url, UserEntity userEntity, String webSocketTicket, boolean isGuest) { + String generatedURL = url.replace("https://", "wss://").replace("http://", "ws://"); + + if (generatedURL.endsWith("/")) { + generatedURL += "spreed"; + } else { + generatedURL += "/spreed"; } - public static synchronized MagicWebSocketInstance getExternalSignalingInstanceForServer(String url, UserEntity userEntity, String webSocketTicket, boolean isGuest) { - String generatedURL = url.replace("https://", "wss://").replace("http://", "ws://"); + long userId = isGuest ? -1 : userEntity.getId(); - if (generatedURL.endsWith("/")) { - generatedURL += "spreed"; - } else { - generatedURL += "/spreed"; - } - - long userId = isGuest ? -1 : userEntity.getId(); - - - MagicWebSocketInstance magicWebSocketInstance; - if (userId != -1 && magicWebSocketInstanceMap.containsKey(userEntity.getId()) && (magicWebSocketInstance = magicWebSocketInstanceMap.get(userEntity.getId())) != null) { - return magicWebSocketInstance; - } else { - if (userId == -1) { - deleteExternalSignalingInstanceForUserEntity(userId); - } - magicWebSocketInstance = new MagicWebSocketInstance(userEntity, generatedURL, webSocketTicket); - magicWebSocketInstanceMap.put(userEntity.getId(), magicWebSocketInstance); - return magicWebSocketInstance; - } + MagicWebSocketInstance magicWebSocketInstance; + if (userId != -1 + && magicWebSocketInstanceMap.containsKey(userEntity.getId()) + && (magicWebSocketInstance = magicWebSocketInstanceMap.get(userEntity.getId())) != null) { + return magicWebSocketInstance; + } else { + if (userId == -1) { + deleteExternalSignalingInstanceForUserEntity(userId); + } + magicWebSocketInstance = + new MagicWebSocketInstance(userEntity, generatedURL, webSocketTicket); + magicWebSocketInstanceMap.put(userEntity.getId(), magicWebSocketInstance); + return magicWebSocketInstance; } + } - public static synchronized void deleteExternalSignalingInstanceForUserEntity(long id) { - MagicWebSocketInstance magicWebSocketInstance; - if ((magicWebSocketInstance = magicWebSocketInstanceMap.get(id)) != null) { - if (magicWebSocketInstance.isConnected()) { - magicWebSocketInstance.sendBye(); - magicWebSocketInstanceMap.remove(id); - } - } + public static synchronized void deleteExternalSignalingInstanceForUserEntity(long id) { + MagicWebSocketInstance magicWebSocketInstance; + if ((magicWebSocketInstance = magicWebSocketInstanceMap.get(id)) != null) { + if (magicWebSocketInstance.isConnected()) { + magicWebSocketInstance.sendBye(); + magicWebSocketInstanceMap.remove(id); + } } + } - HelloOverallWebSocketMessage getAssembledHelloModel(UserEntity userEntity, String ticket) { - HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage(); - helloOverallWebSocketMessage.setType("hello"); - HelloWebSocketMessage helloWebSocketMessage = new HelloWebSocketMessage(); - helloWebSocketMessage.setVersion("1.0"); - AuthWebSocketMessage authWebSocketMessage = new AuthWebSocketMessage(); - authWebSocketMessage.setUrl(ApiUtils.getUrlForExternalServerAuthBackend(userEntity.getBaseUrl())); - AuthParametersWebSocketMessage authParametersWebSocketMessage = new AuthParametersWebSocketMessage(); - authParametersWebSocketMessage.setTicket(ticket); - authParametersWebSocketMessage.setUserid(userEntity.getUserId()); - authWebSocketMessage.setAuthParametersWebSocketMessage(authParametersWebSocketMessage); - helloWebSocketMessage.setAuthWebSocketMessage(authWebSocketMessage); - helloOverallWebSocketMessage.setHelloWebSocketMessage(helloWebSocketMessage); - return helloOverallWebSocketMessage; - } + HelloOverallWebSocketMessage getAssembledHelloModel(UserEntity userEntity, String ticket) { + HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage(); + helloOverallWebSocketMessage.setType("hello"); + HelloWebSocketMessage helloWebSocketMessage = new HelloWebSocketMessage(); + helloWebSocketMessage.setVersion("1.0"); + AuthWebSocketMessage authWebSocketMessage = new AuthWebSocketMessage(); + authWebSocketMessage.setUrl( + ApiUtils.getUrlForExternalServerAuthBackend(userEntity.getBaseUrl())); + AuthParametersWebSocketMessage authParametersWebSocketMessage = + new AuthParametersWebSocketMessage(); + authParametersWebSocketMessage.setTicket(ticket); + authParametersWebSocketMessage.setUserid(userEntity.getUserId()); + authWebSocketMessage.setAuthParametersWebSocketMessage(authParametersWebSocketMessage); + helloWebSocketMessage.setAuthWebSocketMessage(authWebSocketMessage); + helloOverallWebSocketMessage.setHelloWebSocketMessage(helloWebSocketMessage); + return helloOverallWebSocketMessage; + } - HelloOverallWebSocketMessage getAssembledHelloModelForResume(String resumeId) { - HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage(); - helloOverallWebSocketMessage.setType("hello"); - HelloWebSocketMessage helloWebSocketMessage = new HelloWebSocketMessage(); - helloWebSocketMessage.setVersion("1.0"); - helloWebSocketMessage.setResumeid(resumeId); - helloOverallWebSocketMessage.setHelloWebSocketMessage(helloWebSocketMessage); - return helloOverallWebSocketMessage; - } + HelloOverallWebSocketMessage getAssembledHelloModelForResume(String resumeId) { + HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage(); + helloOverallWebSocketMessage.setType("hello"); + HelloWebSocketMessage helloWebSocketMessage = new HelloWebSocketMessage(); + helloWebSocketMessage.setVersion("1.0"); + helloWebSocketMessage.setResumeid(resumeId); + helloOverallWebSocketMessage.setHelloWebSocketMessage(helloWebSocketMessage); + return helloOverallWebSocketMessage; + } - RoomOverallWebSocketMessage getAssembledJoinOrLeaveRoomModel(String roomId, String sessionId) { - RoomOverallWebSocketMessage roomOverallWebSocketMessage = new RoomOverallWebSocketMessage(); - roomOverallWebSocketMessage.setType("room"); - RoomWebSocketMessage roomWebSocketMessage = new RoomWebSocketMessage(); - roomWebSocketMessage.setRoomId(roomId); - roomWebSocketMessage.setSessiondId(sessionId); - roomOverallWebSocketMessage.setRoomWebSocketMessage(roomWebSocketMessage); - return roomOverallWebSocketMessage; - } + RoomOverallWebSocketMessage getAssembledJoinOrLeaveRoomModel(String roomId, String sessionId) { + RoomOverallWebSocketMessage roomOverallWebSocketMessage = new RoomOverallWebSocketMessage(); + roomOverallWebSocketMessage.setType("room"); + RoomWebSocketMessage roomWebSocketMessage = new RoomWebSocketMessage(); + roomWebSocketMessage.setRoomId(roomId); + roomWebSocketMessage.setSessiondId(sessionId); + roomOverallWebSocketMessage.setRoomWebSocketMessage(roomWebSocketMessage); + return roomOverallWebSocketMessage; + } - RequestOfferOverallWebSocketMessage getAssembledRequestOfferModel(String sessionId, String roomType) { - RequestOfferOverallWebSocketMessage requestOfferOverallWebSocketMessage = new RequestOfferOverallWebSocketMessage(); - requestOfferOverallWebSocketMessage.setType("message"); + RequestOfferOverallWebSocketMessage getAssembledRequestOfferModel(String sessionId, + String roomType) { + RequestOfferOverallWebSocketMessage requestOfferOverallWebSocketMessage = + new RequestOfferOverallWebSocketMessage(); + requestOfferOverallWebSocketMessage.setType("message"); - RequestOfferSignalingMessage requestOfferSignalingMessage = new RequestOfferSignalingMessage(); + RequestOfferSignalingMessage requestOfferSignalingMessage = new RequestOfferSignalingMessage(); - ActorWebSocketMessage actorWebSocketMessage = new ActorWebSocketMessage(); - actorWebSocketMessage.setType("session"); - actorWebSocketMessage.setSessionId(sessionId); - requestOfferSignalingMessage.setActorWebSocketMessage(actorWebSocketMessage); + ActorWebSocketMessage actorWebSocketMessage = new ActorWebSocketMessage(); + actorWebSocketMessage.setType("session"); + actorWebSocketMessage.setSessionId(sessionId); + requestOfferSignalingMessage.setActorWebSocketMessage(actorWebSocketMessage); - SignalingDataWebSocketMessageForOffer signalingDataWebSocketMessageForOffer = new SignalingDataWebSocketMessageForOffer(); - signalingDataWebSocketMessageForOffer.setRoomType(roomType); - signalingDataWebSocketMessageForOffer.setType("requestoffer"); - requestOfferSignalingMessage.setSignalingDataWebSocketMessageForOffer(signalingDataWebSocketMessageForOffer); + SignalingDataWebSocketMessageForOffer signalingDataWebSocketMessageForOffer = + new SignalingDataWebSocketMessageForOffer(); + signalingDataWebSocketMessageForOffer.setRoomType(roomType); + signalingDataWebSocketMessageForOffer.setType("requestoffer"); + requestOfferSignalingMessage.setSignalingDataWebSocketMessageForOffer( + signalingDataWebSocketMessageForOffer); - requestOfferOverallWebSocketMessage.setRequestOfferOverallWebSocketMessage(requestOfferSignalingMessage); - return requestOfferOverallWebSocketMessage; - } + requestOfferOverallWebSocketMessage.setRequestOfferOverallWebSocketMessage( + requestOfferSignalingMessage); + return requestOfferOverallWebSocketMessage; + } - CallOverallWebSocketMessage getAssembledCallMessageModel(NCMessageWrapper ncMessageWrapper) { - CallOverallWebSocketMessage callOverallWebSocketMessage = new CallOverallWebSocketMessage(); - callOverallWebSocketMessage.setType("message"); + CallOverallWebSocketMessage getAssembledCallMessageModel(NCMessageWrapper ncMessageWrapper) { + CallOverallWebSocketMessage callOverallWebSocketMessage = new CallOverallWebSocketMessage(); + callOverallWebSocketMessage.setType("message"); - CallWebSocketMessage callWebSocketMessage = new CallWebSocketMessage(); + CallWebSocketMessage callWebSocketMessage = new CallWebSocketMessage(); - ActorWebSocketMessage actorWebSocketMessage = new ActorWebSocketMessage(); - actorWebSocketMessage.setType("session"); - actorWebSocketMessage.setSessionId(ncMessageWrapper.getSignalingMessage().getTo()); - callWebSocketMessage.setRecipientWebSocketMessage(actorWebSocketMessage); - callWebSocketMessage.setNcSignalingMessage(ncMessageWrapper.getSignalingMessage()); + ActorWebSocketMessage actorWebSocketMessage = new ActorWebSocketMessage(); + actorWebSocketMessage.setType("session"); + actorWebSocketMessage.setSessionId(ncMessageWrapper.getSignalingMessage().getTo()); + callWebSocketMessage.setRecipientWebSocketMessage(actorWebSocketMessage); + callWebSocketMessage.setNcSignalingMessage(ncMessageWrapper.getSignalingMessage()); - callOverallWebSocketMessage.setCallWebSocketMessage(callWebSocketMessage); - return callOverallWebSocketMessage; - } + callOverallWebSocketMessage.setCallWebSocketMessage(callWebSocketMessage); + return callOverallWebSocketMessage; + } }