mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-21 04:29:45 +01:00
Koin implementation & EventBus cleanup
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
158f8a0d9c
commit
91e62d9de2
@ -137,6 +137,7 @@ android {
|
|||||||
|
|
||||||
ext {
|
ext {
|
||||||
workVersion = "1.0.1"
|
workVersion = "1.0.1"
|
||||||
|
koin_version = "2.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -150,6 +151,17 @@ configurations.all {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*'], dir: 'libs')
|
implementation fileTree(include: ['*'], dir: 'libs')
|
||||||
|
|
||||||
|
// Koin for Android
|
||||||
|
implementation "org.koin:koin-android:$koin_version"
|
||||||
|
// Koin AndroidX Scope features
|
||||||
|
implementation "org.koin:koin-androidx-scope:$koin_version"
|
||||||
|
// Koin AndroidX ViewModel features
|
||||||
|
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
||||||
|
// Koin AndroidX Experimental features
|
||||||
|
implementation "org.koin:koin-androidx-ext:$koin_version"
|
||||||
|
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
|
||||||
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.1.0-beta01'
|
implementation 'com.google.android.material:material:1.1.0-beta01'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.nextcloud.talk.application
|
package com.nextcloud.talk.application
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -48,6 +49,9 @@ import com.nextcloud.talk.jobs.AccountRemovalWorker
|
|||||||
import com.nextcloud.talk.jobs.CapabilitiesWorker
|
import com.nextcloud.talk.jobs.CapabilitiesWorker
|
||||||
import com.nextcloud.talk.jobs.PushRegistrationWorker
|
import com.nextcloud.talk.jobs.PushRegistrationWorker
|
||||||
import com.nextcloud.talk.jobs.SignalingSettingsWorker
|
import com.nextcloud.talk.jobs.SignalingSettingsWorker
|
||||||
|
import com.nextcloud.talk.newarch.di.module.CommunicationModule
|
||||||
|
import com.nextcloud.talk.newarch.di.module.NetworkModule
|
||||||
|
import com.nextcloud.talk.newarch.di.module.StorageModule
|
||||||
import com.nextcloud.talk.utils.ClosedInterfaceImpl
|
import com.nextcloud.talk.utils.ClosedInterfaceImpl
|
||||||
import com.nextcloud.talk.utils.DeviceUtils
|
import com.nextcloud.talk.utils.DeviceUtils
|
||||||
import com.nextcloud.talk.utils.DisplayUtils
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
@ -62,6 +66,9 @@ import de.cotech.hw.SecurityKeyManager
|
|||||||
import de.cotech.hw.SecurityKeyManagerConfig
|
import de.cotech.hw.SecurityKeyManagerConfig
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.conscrypt.Conscrypt
|
import org.conscrypt.Conscrypt
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
import org.koin.android.ext.koin.androidLogger
|
||||||
|
import org.koin.core.context.startKoin
|
||||||
import org.webrtc.PeerConnectionFactory
|
import org.webrtc.PeerConnectionFactory
|
||||||
import org.webrtc.voiceengine.WebRtcAudioManager
|
import org.webrtc.voiceengine.WebRtcAudioManager
|
||||||
import org.webrtc.voiceengine.WebRtcAudioUtils
|
import org.webrtc.voiceengine.WebRtcAudioUtils
|
||||||
@ -73,7 +80,7 @@ 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
|
@Singleton
|
||||||
@AutoInjector(NextcloudTalkApplication::class)
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
|
class NextcloudTalkApplication : Application(), LifecycleObserver {
|
||||||
//region Fields (components)
|
//region Fields (components)
|
||||||
lateinit var componentApplication: NextcloudTalkApplicationComponent
|
lateinit var componentApplication: NextcloudTalkApplicationComponent
|
||||||
private set
|
private set
|
||||||
@ -182,6 +189,12 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
|
|||||||
.userModule(UserModule())
|
.userModule(UserModule())
|
||||||
.arbitraryStorageModule(ArbitraryStorageModule())
|
.arbitraryStorageModule(ArbitraryStorageModule())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
startKoin {
|
||||||
|
androidContext(this@NextcloudTalkApplication)
|
||||||
|
androidLogger()
|
||||||
|
modules(listOf(CommunicationModule, StorageModule, NetworkModule))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
|
@ -196,7 +196,7 @@ public class BrowserController extends BaseController implements ListingInterfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTitle() {
|
public String getTitle() {
|
||||||
return currentPath;
|
return currentPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,9 +84,6 @@ public class AccountVerificationController extends BaseController {
|
|||||||
@Inject
|
@Inject
|
||||||
AppPreferences appPreferences;
|
AppPreferences appPreferences;
|
||||||
|
|
||||||
@Inject
|
|
||||||
EventBus eventBus;
|
|
||||||
|
|
||||||
@BindView(R.id.progress_text)
|
@BindView(R.id.progress_text)
|
||||||
TextView progressText;
|
TextView progressText;
|
||||||
|
|
||||||
@ -113,18 +110,6 @@ public class AccountVerificationController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAttach(@NonNull View view) {
|
|
||||||
super.onAttach(view);
|
|
||||||
eventBus.register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetach(@NonNull View view) {
|
|
||||||
super.onDetach(view);
|
|
||||||
eventBus.unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||||
return inflater.inflate(R.layout.controller_account_verification, container, false);
|
return inflater.inflate(R.layout.controller_account_verification, container, false);
|
||||||
@ -174,7 +159,7 @@ public class AccountVerificationController extends BaseController {
|
|||||||
ncApi.getServerStatus(queryUrl)
|
ncApi.getServerStatus(queryUrl)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<Status>() {
|
.subscribe(new Observer<Status>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -217,7 +202,7 @@ public class AccountVerificationController extends BaseController {
|
|||||||
private void findServerTalkApp(String credentials) {
|
private void findServerTalkApp(String credentials) {
|
||||||
ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl))
|
ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<RoomsOverall>() {
|
.subscribe(new Observer<RoomsOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -254,7 +239,7 @@ public class AccountVerificationController extends BaseController {
|
|||||||
userId, null, null,
|
userId, null, null,
|
||||||
appPreferences.getTemporaryClientCertAlias(), null)
|
appPreferences.getTemporaryClientCertAlias(), null)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<UserEntity>() {
|
.subscribe(new Observer<UserEntity>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -294,7 +279,7 @@ public class AccountVerificationController extends BaseController {
|
|||||||
ncApi.getUserProfile(credentials,
|
ncApi.getUserProfile(credentials,
|
||||||
ApiUtils.getUrlForUserProfile(baseUrl))
|
ApiUtils.getUrlForUserProfile(baseUrl))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<UserProfileOverall>() {
|
.subscribe(new Observer<UserProfileOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
|
@ -192,8 +192,6 @@ public class CallController extends BaseController {
|
|||||||
@Inject
|
@Inject
|
||||||
NcApi ncApi;
|
NcApi ncApi;
|
||||||
@Inject
|
@Inject
|
||||||
EventBus eventBus;
|
|
||||||
@Inject
|
|
||||||
UserUtils userUtils;
|
UserUtils userUtils;
|
||||||
@Inject
|
@Inject
|
||||||
AppPreferences appPreferences;
|
AppPreferences appPreferences;
|
||||||
@ -2259,18 +2257,6 @@ public class CallController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAttach(@NonNull View view) {
|
|
||||||
super.onAttach(view);
|
|
||||||
eventBus.register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetach(@NonNull View view) {
|
|
||||||
super.onDetach(view);
|
|
||||||
eventBus.unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MicrophoneButtonTouchListener implements View.OnTouchListener {
|
private class MicrophoneButtonTouchListener implements View.OnTouchListener {
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@ -200,7 +200,7 @@ public class CallNotificationController extends BaseController {
|
|||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.takeWhile(observable -> !leavingScreen)
|
.takeWhile(observable -> !leavingScreen)
|
||||||
.retry(3)
|
.retry(3)
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<ParticipantsOverall>() {
|
.subscribe(new Observer<ParticipantsOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -249,7 +249,7 @@ public class CallNotificationController extends BaseController {
|
|||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.retry(3)
|
.retry(3)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<RoomsOverall>() {
|
.subscribe(new Observer<RoomsOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -390,19 +390,6 @@ public class CallNotificationController extends BaseController {
|
|||||||
avatarImageView.setLayoutParams(layoutParams);
|
avatarImageView.setLayoutParams(layoutParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetach(@NonNull View view) {
|
|
||||||
super.onDetach(view);
|
|
||||||
eventBus.unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAttach(@NonNull View view) {
|
|
||||||
super.onAttach(view);
|
|
||||||
eventBus.register(this);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadAvatar() {
|
private void loadAvatar() {
|
||||||
switch (currentConversation.getType()) {
|
switch (currentConversation.getType()) {
|
||||||
case ROOM_TYPE_ONE_TO_ONE_CALL:
|
case ROOM_TYPE_ONE_TO_ONE_CALL:
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.controllers
|
package com.nextcloud.talk.controllers
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
@ -63,11 +62,6 @@ import com.facebook.datasource.DataSource
|
|||||||
import com.facebook.drawee.backends.pipeline.Fresco
|
import com.facebook.drawee.backends.pipeline.Fresco
|
||||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
|
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
|
||||||
import com.facebook.imagepipeline.image.CloseableImage
|
import com.facebook.imagepipeline.image.CloseableImage
|
||||||
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.mention.Mention
|
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.activities.MagicCallActivity
|
import com.nextcloud.talk.activities.MagicCallActivity
|
||||||
import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
|
||||||
@ -85,6 +79,11 @@ import com.nextcloud.talk.events.WebSocketCommunicationEvent
|
|||||||
import com.nextcloud.talk.models.database.UserEntity
|
import com.nextcloud.talk.models.database.UserEntity
|
||||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||||
import com.nextcloud.talk.models.json.chat.ChatOverall
|
import com.nextcloud.talk.models.json.chat.ChatOverall
|
||||||
|
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.mention.Mention
|
||||||
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
|
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
import com.nextcloud.talk.utils.ConductorRemapping
|
import com.nextcloud.talk.utils.ConductorRemapping
|
||||||
@ -95,7 +94,6 @@ import com.nextcloud.talk.utils.MagicCharPolicy
|
|||||||
import com.nextcloud.talk.utils.NotificationUtils
|
import com.nextcloud.talk.utils.NotificationUtils
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
import com.nextcloud.talk.utils.database.user.UserUtils
|
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
|
||||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
||||||
import com.nextcloud.talk.utils.text.Spans
|
import com.nextcloud.talk.utils.text.Spans
|
||||||
import com.nextcloud.talk.webrtc.MagicWebSocketInstance
|
import com.nextcloud.talk.webrtc.MagicWebSocketInstance
|
||||||
@ -115,7 +113,6 @@ import io.reactivex.Observer
|
|||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import org.parceler.Parcels
|
import org.parceler.Parcels
|
||||||
@ -138,15 +135,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
@Inject
|
@Inject
|
||||||
@JvmField
|
@JvmField
|
||||||
var userUtils: UserUtils? = null
|
var userUtils: UserUtils? = null
|
||||||
@Inject
|
|
||||||
@JvmField
|
|
||||||
var appPreferences: AppPreferences? = null
|
|
||||||
@Inject
|
|
||||||
@JvmField
|
|
||||||
var context: Context? = null
|
|
||||||
@Inject
|
|
||||||
@JvmField
|
|
||||||
var eventBus: EventBus? = null
|
|
||||||
@BindView(R.id.messagesListView)
|
@BindView(R.id.messagesListView)
|
||||||
@JvmField
|
@JvmField
|
||||||
var messagesListView: MessagesList? = null
|
var messagesListView: MessagesList? = null
|
||||||
@ -215,7 +203,9 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
|
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
|
||||||
|
|
||||||
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
||||||
this.currentConversation = Parcels.unwrap<Conversation>(args.getParcelable<Parcelable>(BundleKeys.KEY_ACTIVE_CONVERSATION))
|
this.currentConversation = Parcels.unwrap<Conversation>(
|
||||||
|
args.getParcelable<Parcelable>(BundleKeys.KEY_ACTIVE_CONVERSATION)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "")
|
this.roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "")
|
||||||
@ -241,7 +231,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
|
|
||||||
if (conversationUser != null) {
|
if (conversationUser != null) {
|
||||||
ncApi?.getRoom(credentials, ApiUtils.getRoom(conversationUser.baseUrl, roomToken))?.subscribeOn(Schedulers.io())
|
ncApi?.getRoom(credentials, ApiUtils.getRoom(conversationUser.baseUrl, roomToken))
|
||||||
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
||||||
?.subscribe(object : Observer<RoomOverall> {
|
?.subscribe(object : Observer<RoomOverall> {
|
||||||
@ -282,7 +273,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
private fun handleFromNotification() {
|
private fun handleFromNotification() {
|
||||||
ncApi?.getRooms(credentials, ApiUtils.getUrlForGetRooms(conversationUser?.baseUrl))
|
ncApi?.getRooms(credentials, ApiUtils.getUrlForGetRooms(conversationUser?.baseUrl))
|
||||||
?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())
|
?.subscribeOn(Schedulers.io())
|
||||||
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
||||||
?.subscribe(object : Observer<RoomsOverall> {
|
?.subscribe(object : Observer<RoomsOverall> {
|
||||||
override fun onSubscribe(d: Disposable) {
|
override fun onSubscribe(d: Disposable) {
|
||||||
@ -310,19 +302,30 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
override fun inflateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup
|
||||||
|
): View {
|
||||||
return inflater.inflate(R.layout.controller_chat, container, false)
|
return inflater.inflate(R.layout.controller_chat, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAvatarForStatusBar() {
|
private fun loadAvatarForStatusBar() {
|
||||||
if (currentConversation != null && currentConversation?.type != null &&
|
if (currentConversation != null && currentConversation?.type != null &&
|
||||||
currentConversation?.type == Conversation.ConversationType
|
currentConversation?.type == Conversation.ConversationType
|
||||||
.ROOM_TYPE_ONE_TO_ONE_CALL && activity != null && conversationVoiceCallMenuItem != null) {
|
.ROOM_TYPE_ONE_TO_ONE_CALL && activity != null && conversationVoiceCallMenuItem != null
|
||||||
val avatarSize = DisplayUtils.convertDpToPixel(conversationVoiceCallMenuItem?.icon!!
|
) {
|
||||||
.intrinsicWidth.toFloat(), activity).toInt()
|
val avatarSize = DisplayUtils.convertDpToPixel(
|
||||||
|
conversationVoiceCallMenuItem?.icon!!
|
||||||
|
.intrinsicWidth.toFloat(), activity
|
||||||
|
)
|
||||||
|
.toInt()
|
||||||
|
|
||||||
val imageRequest = DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameAndPixels(conversationUser?.baseUrl,
|
val imageRequest = DisplayUtils.getImageRequestForUrl(
|
||||||
currentConversation?.name, avatarSize / 2), null)
|
ApiUtils.getUrlForAvatarWithNameAndPixels(
|
||||||
|
conversationUser?.baseUrl,
|
||||||
|
currentConversation?.name, avatarSize / 2
|
||||||
|
), null
|
||||||
|
)
|
||||||
|
|
||||||
val imagePipeline = Fresco.getImagePipeline()
|
val imagePipeline = Fresco.getImagePipeline()
|
||||||
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
|
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
|
||||||
@ -354,21 +357,36 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
adapterWasNull = true
|
adapterWasNull = true
|
||||||
|
|
||||||
val messageHolders = MessageHolders()
|
val messageHolders = MessageHolders()
|
||||||
messageHolders.setIncomingTextConfig(MagicIncomingTextMessageViewHolder::class.java, R.layout.item_custom_incoming_text_message)
|
messageHolders.setIncomingTextConfig(
|
||||||
messageHolders.setOutcomingTextConfig(MagicOutcomingTextMessageViewHolder::class.java, R.layout.item_custom_outcoming_text_message)
|
MagicIncomingTextMessageViewHolder::class.java, R.layout.item_custom_incoming_text_message
|
||||||
|
)
|
||||||
|
messageHolders.setOutcomingTextConfig(
|
||||||
|
MagicOutcomingTextMessageViewHolder::class.java,
|
||||||
|
R.layout.item_custom_outcoming_text_message
|
||||||
|
)
|
||||||
|
|
||||||
messageHolders.setIncomingImageConfig(MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_incoming_preview_message)
|
messageHolders.setIncomingImageConfig(
|
||||||
messageHolders.setOutcomingImageConfig(MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_outcoming_preview_message)
|
MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_incoming_preview_message
|
||||||
|
)
|
||||||
|
messageHolders.setOutcomingImageConfig(
|
||||||
|
MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_outcoming_preview_message
|
||||||
|
)
|
||||||
|
|
||||||
messageHolders.registerContentType(CONTENT_TYPE_SYSTEM_MESSAGE, MagicSystemMessageViewHolder::class.java,
|
messageHolders.registerContentType(
|
||||||
R.layout.item_system_message, MagicSystemMessageViewHolder::class.java, R.layout.item_system_message,
|
CONTENT_TYPE_SYSTEM_MESSAGE, MagicSystemMessageViewHolder::class.java,
|
||||||
this)
|
R.layout.item_system_message, MagicSystemMessageViewHolder::class.java,
|
||||||
|
R.layout.item_system_message,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
messageHolders.registerContentType(CONTENT_TYPE_UNREAD_NOTICE_MESSAGE,
|
messageHolders.registerContentType(
|
||||||
|
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE,
|
||||||
MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header,
|
MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header,
|
||||||
MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, this)
|
MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, this
|
||||||
|
)
|
||||||
|
|
||||||
adapter = MessagesListAdapter(conversationUser?.userId, messageHolders, ImageLoader { imageView, url, payload ->
|
adapter = MessagesListAdapter(
|
||||||
|
conversationUser?.userId, messageHolders, ImageLoader { imageView, url, payload ->
|
||||||
val draweeController = Fresco.newDraweeControllerBuilder()
|
val draweeController = Fresco.newDraweeControllerBuilder()
|
||||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser))
|
.setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser))
|
||||||
.setControllerListener(DisplayUtils.getImageControllerListener(imageView))
|
.setControllerListener(DisplayUtils.getImageControllerListener(imageView))
|
||||||
@ -403,13 +421,17 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
messagesListView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
messagesListView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
override fun onScrollStateChanged(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
newState: Int
|
||||||
|
) {
|
||||||
super.onScrollStateChanged(recyclerView, newState)
|
super.onScrollStateChanged(recyclerView, newState)
|
||||||
|
|
||||||
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
|
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
|
||||||
if (newMessagesCount != 0 && layoutManager != null) {
|
if (newMessagesCount != 0 && layoutManager != null) {
|
||||||
if (layoutManager!!.findFirstCompletelyVisibleItemPosition() <
|
if (layoutManager!!.findFirstCompletelyVisibleItemPosition() <
|
||||||
newMessagesCount) {
|
newMessagesCount
|
||||||
|
) {
|
||||||
newMessagesCount = 0
|
newMessagesCount = 0
|
||||||
|
|
||||||
if (popupBubble != null && popupBubble!!.isShown) {
|
if (popupBubble != null && popupBubble!!.isShown) {
|
||||||
@ -421,7 +443,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
val filters = arrayOfNulls<InputFilter>(1)
|
val filters = arrayOfNulls<InputFilter>(1)
|
||||||
val lengthFilter = conversationUser?.messageMaxLength ?: 1000
|
val lengthFilter = conversationUser?.messageMaxLength ?: 1000
|
||||||
|
|
||||||
@ -430,28 +451,48 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
messageInput?.filters = filters
|
messageInput?.filters = filters
|
||||||
|
|
||||||
messageInput?.addTextChangedListener(object : TextWatcher {
|
messageInput?.addTextChangedListener(object : TextWatcher {
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
override fun beforeTextChanged(
|
||||||
|
s: CharSequence,
|
||||||
|
start: Int,
|
||||||
|
count: Int,
|
||||||
|
after: Int
|
||||||
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
override fun onTextChanged(
|
||||||
|
s: CharSequence,
|
||||||
|
start: Int,
|
||||||
|
before: Int,
|
||||||
|
count: Int
|
||||||
|
) {
|
||||||
if (s.length >= lengthFilter) {
|
if (s.length >= lengthFilter) {
|
||||||
messageInput?.error = String.format(Objects.requireNonNull<Resources>
|
messageInput?.error = String.format(
|
||||||
(resources).getString(R.string.nc_limit_hit), Integer.toString(lengthFilter))
|
Objects.requireNonNull<Resources>
|
||||||
|
(resources).getString(R.string.nc_limit_hit), Integer.toString(lengthFilter)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
messageInput?.error = null
|
messageInput?.error = null
|
||||||
}
|
}
|
||||||
|
|
||||||
val editable = messageInput?.editableText
|
val editable = messageInput?.editableText
|
||||||
if (editable != null && messageInput != null) {
|
if (editable != null && messageInput != null) {
|
||||||
val mentionSpans = editable.getSpans(0, messageInput!!.length(),
|
val mentionSpans = editable.getSpans(
|
||||||
Spans.MentionChipSpan::class.java)
|
0, messageInput!!.length(),
|
||||||
|
Spans.MentionChipSpan::class.java
|
||||||
|
)
|
||||||
var mentionSpan: Spans.MentionChipSpan
|
var mentionSpan: Spans.MentionChipSpan
|
||||||
for (i in mentionSpans.indices) {
|
for (i in mentionSpans.indices) {
|
||||||
mentionSpan = mentionSpans[i]
|
mentionSpan = mentionSpans[i]
|
||||||
if (start >= editable.getSpanStart(mentionSpan) && start < editable.getSpanEnd(mentionSpan)) {
|
if (start >= editable.getSpanStart(mentionSpan) && start < editable.getSpanEnd(
|
||||||
if (editable.subSequence(editable.getSpanStart(mentionSpan),
|
mentionSpan
|
||||||
editable.getSpanEnd(mentionSpan)).toString().trim { it <= ' ' } != mentionSpan.label) {
|
)
|
||||||
|
) {
|
||||||
|
if (editable.subSequence(
|
||||||
|
editable.getSpanStart(mentionSpan),
|
||||||
|
editable.getSpanEnd(mentionSpan)
|
||||||
|
).toString().trim { it <= ' ' } != mentionSpan.label
|
||||||
|
) {
|
||||||
editable.removeSpan(mentionSpan)
|
editable.removeSpan(mentionSpan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,14 +506,18 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
})
|
})
|
||||||
|
|
||||||
messageInputView?.setAttachmentsListener {
|
messageInputView?.setAttachmentsListener {
|
||||||
showBrowserScreen(BrowserController
|
showBrowserScreen(
|
||||||
.BrowserType.DAV_BROWSER)
|
BrowserController
|
||||||
|
.BrowserType.DAV_BROWSER
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
messageInputView?.button?.setOnClickListener { v -> submitMessage() }
|
messageInputView?.button?.setOnClickListener { v -> submitMessage() }
|
||||||
|
|
||||||
messageInputView?.button?.contentDescription = resources?.getString(R.string
|
messageInputView?.button?.contentDescription = resources?.getString(
|
||||||
.nc_description_send_message_button)
|
R.string
|
||||||
|
.nc_description_send_message_button
|
||||||
|
)
|
||||||
|
|
||||||
if (currentConversation != null && currentConversation?.roomId != null) {
|
if (currentConversation != null && currentConversation?.roomId != null) {
|
||||||
loadAvatarForStatusBar()
|
loadAvatarForStatusBar()
|
||||||
@ -490,10 +535,12 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun checkReadOnlyState() {
|
private fun checkReadOnlyState() {
|
||||||
if (currentConversation != null) {
|
if (currentConversation != null) {
|
||||||
if (currentConversation?.shouldShowLobby(conversationUser)?: false || currentConversation?.conversationReadOnlyState != null && currentConversation?.conversationReadOnlyState == Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY) {
|
if (currentConversation?.shouldShowLobby(
|
||||||
|
conversationUser
|
||||||
|
) ?: false || currentConversation?.conversationReadOnlyState != null && currentConversation?.conversationReadOnlyState == Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY
|
||||||
|
) {
|
||||||
|
|
||||||
conversationVoiceCallMenuItem?.icon?.alpha = 99
|
conversationVoiceCallMenuItem?.icon?.alpha = 99
|
||||||
conversationVideoMenuItem?.icon?.alpha = 99
|
conversationVideoMenuItem?.icon?.alpha = 99
|
||||||
@ -509,7 +556,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentConversation != null && currentConversation!!.shouldShowLobby
|
if (currentConversation != null && currentConversation!!.shouldShowLobby
|
||||||
(conversationUser)) {
|
(conversationUser)
|
||||||
|
) {
|
||||||
messageInputView?.visibility = View.GONE
|
messageInputView?.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
messageInputView?.visibility = View.VISIBLE
|
messageInputView?.visibility = View.VISIBLE
|
||||||
@ -519,7 +567,10 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkLobbyState() {
|
private fun checkLobbyState() {
|
||||||
if (currentConversation != null && currentConversation?.isLobbyViewApplicable(conversationUser) ?: false) {
|
if (currentConversation != null && currentConversation?.isLobbyViewApplicable(
|
||||||
|
conversationUser
|
||||||
|
) ?: false
|
||||||
|
) {
|
||||||
|
|
||||||
if (!checkingLobbyStatus) {
|
if (!checkingLobbyStatus) {
|
||||||
getRoomInfo()
|
getRoomInfo()
|
||||||
@ -532,9 +583,15 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
loadingProgressBar?.visibility = View.GONE
|
loadingProgressBar?.visibility = View.GONE
|
||||||
|
|
||||||
if (currentConversation?.lobbyTimer != null && currentConversation?.lobbyTimer !=
|
if (currentConversation?.lobbyTimer != null && currentConversation?.lobbyTimer !=
|
||||||
0L) {
|
0L
|
||||||
conversationLobbyText?.text = String.format(resources!!.getString(R.string.nc_lobby_waiting_with_date), DateUtils.getLocalDateStringFromTimestampForLobby(currentConversation?.lobbyTimer
|
) {
|
||||||
?: 0))
|
conversationLobbyText?.text = String.format(
|
||||||
|
resources!!.getString(R.string.nc_lobby_waiting_with_date),
|
||||||
|
DateUtils.getLocalDateStringFromTimestampForLobby(
|
||||||
|
currentConversation?.lobbyTimer
|
||||||
|
?: 0
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
conversationLobbyText?.setText(R.string.nc_lobby_waiting)
|
conversationLobbyText?.setText(R.string.nc_lobby_waiting)
|
||||||
}
|
}
|
||||||
@ -552,29 +609,37 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
private fun showBrowserScreen(browserType: BrowserController.BrowserType) {
|
private fun showBrowserScreen(browserType: BrowserController.BrowserType) {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType))
|
bundle.putParcelable(
|
||||||
|
BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType)
|
||||||
|
)
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserEntity>(conversationUser))
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserEntity>(conversationUser))
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||||
router.pushController(RouterTransaction.with(BrowserController(bundle))
|
router.pushController(
|
||||||
|
RouterTransaction.with(BrowserController(bundle))
|
||||||
.pushChangeHandler(VerticalChangeHandler())
|
.pushChangeHandler(VerticalChangeHandler())
|
||||||
.popChangeHandler(VerticalChangeHandler()))
|
.popChangeHandler(VerticalChangeHandler())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showConversationInfoScreen() {
|
private fun showConversationInfoScreen() {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||||
router.pushController(RouterTransaction.with(ConversationInfoController(bundle))
|
router.pushController(
|
||||||
|
RouterTransaction.with(ConversationInfoController(bundle))
|
||||||
.pushChangeHandler(HorizontalChangeHandler())
|
.pushChangeHandler(HorizontalChangeHandler())
|
||||||
.popChangeHandler(HorizontalChangeHandler()))
|
.popChangeHandler(HorizontalChangeHandler())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupMentionAutocomplete() {
|
private fun setupMentionAutocomplete() {
|
||||||
val elevation = 6f
|
val elevation = 6f
|
||||||
val backgroundDrawable = ColorDrawable(resources!!.getColor(R.color.bg_default))
|
val backgroundDrawable = ColorDrawable(resources!!.getColor(R.color.bg_default))
|
||||||
val presenter = MentionAutocompletePresenter(applicationContext, roomToken)
|
val presenter = MentionAutocompletePresenter(applicationContext, roomToken)
|
||||||
val callback = MentionAutocompleteCallback(activity,
|
val callback = MentionAutocompleteCallback(
|
||||||
conversationUser, messageInput)
|
activity,
|
||||||
|
conversationUser, messageInput
|
||||||
|
)
|
||||||
|
|
||||||
if (mentionAutocomplete == null && messageInput != null) {
|
if (mentionAutocomplete == null && messageInput != null) {
|
||||||
mentionAutocomplete = Autocomplete.on<Mention>(messageInput)
|
mentionAutocomplete = Autocomplete.on<Mention>(messageInput)
|
||||||
@ -589,31 +654,46 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
override fun onAttach(view: View) {
|
override fun onAttach(view: View) {
|
||||||
super.onAttach(view)
|
super.onAttach(view)
|
||||||
eventBus?.register(this)
|
|
||||||
|
|
||||||
if (conversationUser?.userId != "?" && conversationUser?.hasSpreedFeatureCapability("mention-flag") ?: false && activity != null) {
|
if (conversationUser?.userId != "?" && conversationUser?.hasSpreedFeatureCapability(
|
||||||
activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener { v ->
|
"mention-flag"
|
||||||
|
) ?: false && activity != null
|
||||||
|
) {
|
||||||
|
activity?.findViewById<View>(R.id.toolbar)
|
||||||
|
?.setOnClickListener { v ->
|
||||||
showConversationInfoScreen()
|
showConversationInfoScreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isLeavingForConversation = false
|
isLeavingForConversation = false
|
||||||
ApplicationWideCurrentRoomHolder.getInstance().currentRoomId = roomId
|
ApplicationWideCurrentRoomHolder.getInstance()
|
||||||
ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = roomId
|
.currentRoomId = roomId
|
||||||
ApplicationWideCurrentRoomHolder.getInstance().isInCall = false
|
ApplicationWideCurrentRoomHolder.getInstance()
|
||||||
ApplicationWideCurrentRoomHolder.getInstance().userInRoom = conversationUser
|
.currentRoomToken = roomId
|
||||||
|
ApplicationWideCurrentRoomHolder.getInstance()
|
||||||
|
.isInCall = false
|
||||||
|
ApplicationWideCurrentRoomHolder.getInstance()
|
||||||
|
.userInRoom = conversationUser
|
||||||
|
|
||||||
isLinkPreviewAllowed = appPreferences?.areLinkPreviewsAllowed ?: false
|
isLinkPreviewAllowed = appPreferences?.areLinkPreviewsAllowed ?: false
|
||||||
|
|
||||||
emojiPopup = messageInput?.let {
|
emojiPopup = messageInput?.let {
|
||||||
EmojiPopup.Builder.fromRootView(view).setOnEmojiPopupShownListener {
|
EmojiPopup.Builder.fromRootView(view)
|
||||||
|
.setOnEmojiPopupShownListener {
|
||||||
if (resources != null) {
|
if (resources != null) {
|
||||||
smileyButton?.setColorFilter(resources!!.getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN)
|
smileyButton?.setColorFilter(
|
||||||
|
resources!!.getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}.setOnEmojiPopupDismissListener {
|
}
|
||||||
smileyButton?.setColorFilter(resources!!.getColor(R.color.emoji_icons),
|
.setOnEmojiPopupDismissListener {
|
||||||
PorterDuff.Mode.SRC_IN)
|
smileyButton?.setColorFilter(
|
||||||
}.setOnEmojiClickListener { emoji, imageView -> messageInput?.editableText?.append(" ") }.build(it)
|
resources!!.getColor(R.color.emoji_icons),
|
||||||
|
PorterDuff.Mode.SRC_IN
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.setOnEmojiClickListener { emoji, imageView -> messageInput?.editableText?.append(" ") }
|
||||||
|
.build(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
@ -634,26 +714,32 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
private fun cancelNotificationsForCurrentConversation() {
|
private fun cancelNotificationsForCurrentConversation() {
|
||||||
if (conversationUser != null) {
|
if (conversationUser != null) {
|
||||||
if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) {
|
if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) {
|
||||||
NotificationUtils.cancelExistingNotificationsForRoom(applicationContext,
|
NotificationUtils.cancelExistingNotificationsForRoom(
|
||||||
conversationUser, roomId)
|
applicationContext,
|
||||||
|
conversationUser, roomId
|
||||||
|
)
|
||||||
} else if (!TextUtils.isEmpty(roomToken)) {
|
} else if (!TextUtils.isEmpty(roomToken)) {
|
||||||
NotificationUtils.cancelExistingNotificationsForRoom(applicationContext,
|
NotificationUtils.cancelExistingNotificationsForRoom(
|
||||||
conversationUser, roomToken!!)
|
applicationContext,
|
||||||
|
conversationUser, roomToken!!
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetach(view: View) {
|
override fun onDetach(view: View) {
|
||||||
super.onDetach(view)
|
super.onDetach(view)
|
||||||
ApplicationWideCurrentRoomHolder.getInstance().clear()
|
ApplicationWideCurrentRoomHolder.getInstance()
|
||||||
eventBus?.unregister(this)
|
.clear()
|
||||||
|
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener(null)
|
activity?.findViewById<View>(R.id.toolbar)
|
||||||
|
?.setOnClickListener(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversationUser != null && conversationUser?.hasSpreedFeatureCapability("no-ping")
|
if (conversationUser != null && conversationUser?.hasSpreedFeatureCapability("no-ping")
|
||||||
&& activity != null && !activity?.isChangingConfigurations!! && !isLeavingForConversation) {
|
&& activity != null && !activity?.isChangingConfigurations!! && !isLeavingForConversation
|
||||||
|
) {
|
||||||
wasDetached = true
|
wasDetached = true
|
||||||
leaveRoom()
|
leaveRoom()
|
||||||
}
|
}
|
||||||
@ -665,7 +751,9 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
override fun getTitle(): String? {
|
override fun getTitle(): String? {
|
||||||
if (currentConversation != null && currentConversation?.displayName != null) {
|
if (currentConversation != null && currentConversation?.displayName != null) {
|
||||||
return EmojiCompat.get().process(currentConversation!!.displayName).toString()
|
return EmojiCompat.get()
|
||||||
|
.process(currentConversation!!.displayName)
|
||||||
|
.toString()
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -675,7 +763,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener(null)
|
activity?.findViewById<View>(R.id.toolbar)
|
||||||
|
?.setOnClickListener(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
@ -688,8 +777,12 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
private fun startPing() {
|
private fun startPing() {
|
||||||
if (conversationUser != null && !conversationUser.hasSpreedFeatureCapability("no-ping")) {
|
if (conversationUser != null && !conversationUser.hasSpreedFeatureCapability("no-ping")) {
|
||||||
ncApi?.pingCall(credentials, ApiUtils.getUrlForCallPing(conversationUser.baseUrl,
|
ncApi?.pingCall(
|
||||||
roomToken))
|
credentials, ApiUtils.getUrlForCallPing(
|
||||||
|
conversationUser.baseUrl,
|
||||||
|
roomToken
|
||||||
|
)
|
||||||
|
)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) }
|
?.repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) }
|
||||||
@ -719,9 +812,13 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
private fun joinRoomWithPassword() {
|
private fun joinRoomWithPassword() {
|
||||||
|
|
||||||
if (currentConversation == null || TextUtils.isEmpty(currentConversation?.sessionId) ||
|
if (currentConversation == null || TextUtils.isEmpty(currentConversation?.sessionId) ||
|
||||||
currentConversation?.sessionId == "0") {
|
currentConversation?.sessionId == "0"
|
||||||
ncApi?.joinRoom(credentials,
|
) {
|
||||||
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl, roomToken), roomPassword)
|
ncApi?.joinRoom(
|
||||||
|
credentials,
|
||||||
|
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl, roomToken),
|
||||||
|
roomPassword
|
||||||
|
)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.retry(3)
|
?.retry(3)
|
||||||
@ -734,7 +831,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
inConversation = true
|
inConversation = true
|
||||||
currentConversation?.sessionId = roomOverall.ocs.data.sessionId
|
currentConversation?.sessionId = roomOverall.ocs.data.sessionId
|
||||||
|
|
||||||
ApplicationWideCurrentRoomHolder.getInstance().session =
|
ApplicationWideCurrentRoomHolder.getInstance()
|
||||||
|
.session =
|
||||||
currentConversation?.sessionId
|
currentConversation?.sessionId
|
||||||
startPing()
|
startPing()
|
||||||
|
|
||||||
@ -748,7 +846,9 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (magicWebSocketInstance != null) {
|
if (magicWebSocketInstance != null) {
|
||||||
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(roomToken, currentConversation?.sessionId)
|
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
|
||||||
|
roomToken, currentConversation?.sessionId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (startCallFromNotification != null && startCallFromNotification ?: false) {
|
if (startCallFromNotification != null && startCallFromNotification ?: false) {
|
||||||
startCallFromNotification = false
|
startCallFromNotification = false
|
||||||
@ -766,10 +866,13 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
inConversation = true
|
inConversation = true
|
||||||
ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation?.sessionId
|
ApplicationWideCurrentRoomHolder.getInstance()
|
||||||
|
.session = currentConversation?.sessionId
|
||||||
if (magicWebSocketInstance != null) {
|
if (magicWebSocketInstance != null) {
|
||||||
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(roomToken,
|
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
|
||||||
currentConversation?.sessionId)
|
roomToken,
|
||||||
|
currentConversation?.sessionId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
startPing()
|
startPing()
|
||||||
if (isFirstMessagesProcessing) {
|
if (isFirstMessagesProcessing) {
|
||||||
@ -781,9 +884,13 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun leaveRoom() {
|
private fun leaveRoom() {
|
||||||
ncApi?.leaveRoom(credentials,
|
ncApi?.leaveRoom(
|
||||||
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl,
|
credentials,
|
||||||
roomToken))
|
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(
|
||||||
|
conversationUser?.baseUrl,
|
||||||
|
roomToken
|
||||||
|
)
|
||||||
|
)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.subscribe(object : Observer<GenericOverall> {
|
?.subscribe(object : Observer<GenericOverall> {
|
||||||
@ -798,8 +905,10 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (magicWebSocketInstance != null && currentConversation != null) {
|
if (magicWebSocketInstance != null && currentConversation != null) {
|
||||||
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession("",
|
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
|
||||||
currentConversation?.sessionId)
|
"",
|
||||||
|
currentConversation?.sessionId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDestroyed && !isBeingDestroyed && !wasDetached) {
|
if (!isDestroyed && !isBeingDestroyed && !wasDetached) {
|
||||||
@ -830,8 +939,10 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
private fun submitMessage() {
|
private fun submitMessage() {
|
||||||
if (messageInput != null) {
|
if (messageInput != null) {
|
||||||
val editable = messageInput!!.editableText
|
val editable = messageInput!!.editableText
|
||||||
val mentionSpans = editable.getSpans(0, editable.length,
|
val mentionSpans = editable.getSpans(
|
||||||
Spans.MentionChipSpan::class.java)
|
0, editable.length,
|
||||||
|
Spans.MentionChipSpan::class.java
|
||||||
|
)
|
||||||
var mentionSpan: Spans.MentionChipSpan
|
var mentionSpan: Spans.MentionChipSpan
|
||||||
for (i in mentionSpans.indices) {
|
for (i in mentionSpans.indices) {
|
||||||
mentionSpan = mentionSpans[i]
|
mentionSpan = mentionSpans[i]
|
||||||
@ -839,7 +950,9 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
if (mentionId.contains(" ") || mentionId.startsWith("guest/")) {
|
if (mentionId.contains(" ") || mentionId.startsWith("guest/")) {
|
||||||
mentionId = "\"" + mentionId + "\""
|
mentionId = "\"" + mentionId + "\""
|
||||||
}
|
}
|
||||||
editable.replace(editable.getSpanStart(mentionSpan), editable.getSpanEnd(mentionSpan), "@$mentionId")
|
editable.replace(
|
||||||
|
editable.getSpanStart(mentionSpan), editable.getSpanEnd(mentionSpan), "@$mentionId"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
messageInput?.setText("")
|
messageInput?.setText("")
|
||||||
@ -850,9 +963,13 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
private fun sendMessage(message: CharSequence) {
|
private fun sendMessage(message: CharSequence) {
|
||||||
|
|
||||||
if (conversationUser != null) {
|
if (conversationUser != null) {
|
||||||
ncApi?.sendChatMessage(credentials, ApiUtils.getUrlForChat(conversationUser?.baseUrl,
|
ncApi?.sendChatMessage(
|
||||||
roomToken),
|
credentials, ApiUtils.getUrlForChat(
|
||||||
message, conversationUser.displayName)
|
conversationUser?.baseUrl,
|
||||||
|
roomToken
|
||||||
|
),
|
||||||
|
message, conversationUser.displayName
|
||||||
|
)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
||||||
@ -895,8 +1012,12 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
private fun setupWebsocket() {
|
private fun setupWebsocket() {
|
||||||
if (conversationUser != null) {
|
if (conversationUser != null) {
|
||||||
if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id) != null) {
|
if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(
|
||||||
magicWebSocketInstance = WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id)
|
conversationUser.id
|
||||||
|
) != null
|
||||||
|
) {
|
||||||
|
magicWebSocketInstance =
|
||||||
|
WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id)
|
||||||
} else {
|
} else {
|
||||||
magicWebSocketInstance = null
|
magicWebSocketInstance = null
|
||||||
}
|
}
|
||||||
@ -948,7 +1069,9 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
if (!wasDetached) {
|
if (!wasDetached) {
|
||||||
if (lookIntoFuture > 0) {
|
if (lookIntoFuture > 0) {
|
||||||
val finalTimeout = timeout
|
val finalTimeout = timeout
|
||||||
ncApi?.pullChatMessages(credentials, ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
|
ncApi?.pullChatMessages(
|
||||||
|
credentials, ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap
|
||||||
|
)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.takeWhile { observable -> inConversation && !wasDetached }
|
?.takeWhile { observable -> inConversation && !wasDetached }
|
||||||
@ -975,8 +1098,10 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
})
|
})
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ncApi?.pullChatMessages(credentials,
|
ncApi?.pullChatMessages(
|
||||||
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
|
credentials,
|
||||||
|
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap
|
||||||
|
)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.retry(3) { observable -> inConversation && !wasDetached }
|
?.retry(3) { observable -> inConversation && !wasDetached }
|
||||||
@ -1002,8 +1127,13 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processMessages(response: Response<*>, isFromTheFuture: Boolean, timeout: Int) {
|
private fun processMessages(
|
||||||
val xChatLastGivenHeader: String? = response.headers().get("X-Chat-Last-Given")
|
response: Response<*>,
|
||||||
|
isFromTheFuture: Boolean,
|
||||||
|
timeout: Int
|
||||||
|
) {
|
||||||
|
val xChatLastGivenHeader: String? = response.headers()
|
||||||
|
.get("X-Chat-Last-Given")
|
||||||
if (response.headers().size > 0 && !TextUtils.isEmpty(xChatLastGivenHeader)) {
|
if (response.headers().size > 0 && !TextUtils.isEmpty(xChatLastGivenHeader)) {
|
||||||
|
|
||||||
val header = Integer.parseInt(xChatLastGivenHeader!!)
|
val header = Integer.parseInt(xChatLastGivenHeader!!)
|
||||||
@ -1041,8 +1171,11 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
if (TextUtils.isEmpty(chatMessageList[i].systemMessage) &&
|
if (TextUtils.isEmpty(chatMessageList[i].systemMessage) &&
|
||||||
TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) &&
|
TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) &&
|
||||||
chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
|
chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
|
||||||
countGroupedMessages < 4 && DateFormatter.isSameDay(chatMessageList[i].createdAt,
|
countGroupedMessages < 4 && DateFormatter.isSameDay(
|
||||||
chatMessageList[i + 1].createdAt)) {
|
chatMessageList[i].createdAt,
|
||||||
|
chatMessageList[i + 1].createdAt
|
||||||
|
)
|
||||||
|
) {
|
||||||
chatMessageList[i].isGrouped = true;
|
chatMessageList[i].isGrouped = true;
|
||||||
countGroupedMessages++
|
countGroupedMessages++
|
||||||
} else {
|
} else {
|
||||||
@ -1051,7 +1184,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
val chatMessage = chatMessageList[i]
|
val chatMessage = chatMessageList[i]
|
||||||
chatMessage.isOneToOneConversation = currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
chatMessage.isOneToOneConversation =
|
||||||
|
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||||
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
|
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
|
||||||
chatMessage.activeUser = conversationUser
|
chatMessage.activeUser = conversationUser
|
||||||
|
|
||||||
@ -1069,7 +1203,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
var chatMessage: ChatMessage
|
var chatMessage: ChatMessage
|
||||||
|
|
||||||
val shouldAddNewMessagesNotice = timeout == 0 && adapter?.itemCount ?: 0 > 0 && chatMessageList.size > 0
|
val shouldAddNewMessagesNotice =
|
||||||
|
timeout == 0 && adapter?.itemCount ?: 0 > 0 && chatMessageList.size > 0
|
||||||
|
|
||||||
if (shouldAddNewMessagesNotice) {
|
if (shouldAddNewMessagesNotice) {
|
||||||
val unreadChatMessage = ChatMessage()
|
val unreadChatMessage = ChatMessage()
|
||||||
@ -1080,7 +1215,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
adapter?.addToStart(unreadChatMessage, false)
|
adapter?.addToStart(unreadChatMessage, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isThereANewNotice = shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1
|
val isThereANewNotice =
|
||||||
|
shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1
|
||||||
|
|
||||||
for (i in chatMessageList.indices) {
|
for (i in chatMessageList.indices) {
|
||||||
chatMessage = chatMessageList[i]
|
chatMessage = chatMessageList[i]
|
||||||
@ -1089,14 +1225,18 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
|
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
|
||||||
|
|
||||||
// if credentials are empty, we're acting as a guest
|
// if credentials are empty, we're acting as a guest
|
||||||
if (TextUtils.isEmpty(credentials) && myFirstMessage != null && !TextUtils.isEmpty(myFirstMessage?.toString())) {
|
if (TextUtils.isEmpty(credentials) && myFirstMessage != null && !TextUtils.isEmpty(
|
||||||
|
myFirstMessage?.toString()
|
||||||
|
)
|
||||||
|
) {
|
||||||
if (chatMessage.actorType == "guests") {
|
if (chatMessage.actorType == "guests") {
|
||||||
conversationUser?.userId = chatMessage.actorId
|
conversationUser?.userId = chatMessage.actorId
|
||||||
setSenderId()
|
setSenderId()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val shouldScroll = !isThereANewNotice && !shouldAddNewMessagesNotice && layoutManager?.findFirstVisibleItemPosition() == 0 || adapter != null && adapter?.itemCount == 0
|
val shouldScroll =
|
||||||
|
!isThereANewNotice && !shouldAddNewMessagesNotice && layoutManager?.findFirstVisibleItemPosition() == 0 || adapter != null && adapter?.itemCount == 0
|
||||||
|
|
||||||
if (!shouldAddNewMessagesNotice && !shouldScroll && popupBubble != null) {
|
if (!shouldAddNewMessagesNotice && !shouldScroll && popupBubble != null) {
|
||||||
if (!popupBubble!!.isShown) {
|
if (!popupBubble!!.isShown) {
|
||||||
@ -1110,16 +1250,21 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
chatMessage.isGrouped = (adapter!!.isPreviousSameAuthor(chatMessage
|
chatMessage.isGrouped = (adapter!!.isPreviousSameAuthor(
|
||||||
.actorId, -1) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0)
|
chatMessage
|
||||||
chatMessage.isOneToOneConversation = (currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
|
.actorId, -1
|
||||||
|
) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0)
|
||||||
|
chatMessage.isOneToOneConversation =
|
||||||
|
(currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
|
||||||
adapter?.addToStart(chatMessage, shouldScroll)
|
adapter?.addToStart(chatMessage, shouldScroll)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldAddNewMessagesNotice && adapter != null && messagesListView != null) {
|
if (shouldAddNewMessagesNotice && adapter != null && messagesListView != null) {
|
||||||
layoutManager?.scrollToPositionWithOffset(adapter!!.getMessagePositionByIdInReverse("-1"), messagesListView!!.height / 2)
|
layoutManager?.scrollToPositionWithOffset(
|
||||||
|
adapter!!.getMessagePositionByIdInReverse("-1"), messagesListView!!.height / 2
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1143,13 +1288,15 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadMore(page: Int, totalItemsCount: Int) {
|
override fun onLoadMore(
|
||||||
|
page: Int,
|
||||||
|
totalItemsCount: Int
|
||||||
|
) {
|
||||||
if (!historyRead && inConversation) {
|
if (!historyRead && inConversation) {
|
||||||
pullChatMessages(0)
|
pullChatMessages(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun format(date: Date): String {
|
override fun format(date: Date): String {
|
||||||
return if (DateFormatter.isToday(date)) {
|
return if (DateFormatter.isToday(date)) {
|
||||||
resources!!.getString(R.string.nc_date_header_today)
|
resources!!.getString(R.string.nc_date_header_today)
|
||||||
@ -1160,7 +1307,10 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(
|
||||||
|
menu: Menu,
|
||||||
|
inflater: MenuInflater
|
||||||
|
) {
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
inflater.inflate(R.menu.menu_conversation, menu)
|
inflater.inflate(R.menu.menu_conversation, menu)
|
||||||
if (conversationUser?.userId == "?") {
|
if (conversationUser?.userId == "?") {
|
||||||
@ -1183,7 +1333,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
@ -1255,14 +1404,19 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
override fun onMessageLongClick(message: IMessage) {
|
override fun onMessageLongClick(message: IMessage) {
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
val clipboardManager = activity?.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
val clipboardManager =
|
||||||
|
activity?.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
||||||
val clipData = android.content.ClipData.newPlainText(
|
val clipData = android.content.ClipData.newPlainText(
|
||||||
resources?.getString(R.string.nc_app_name), message.text)
|
resources?.getString(R.string.nc_app_name), message.text
|
||||||
|
)
|
||||||
clipboardManager.setPrimaryClip(clipData)
|
clipboardManager.setPrimaryClip(clipData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasContentFor(message: IMessage, type: Byte): Boolean {
|
override fun hasContentFor(
|
||||||
|
message: IMessage,
|
||||||
|
type: Byte
|
||||||
|
): Boolean {
|
||||||
when (type) {
|
when (type) {
|
||||||
CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage)
|
CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage)
|
||||||
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> return message.id == "-1"
|
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> return message.id == "-1"
|
||||||
@ -1291,12 +1445,17 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) {
|
fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) {
|
||||||
if (currentConversation?.type != Conversation.ConversationType
|
if (currentConversation?.type != Conversation.ConversationType
|
||||||
.ROOM_TYPE_ONE_TO_ONE_CALL || currentConversation?.name !=
|
.ROOM_TYPE_ONE_TO_ONE_CALL || currentConversation?.name !=
|
||||||
userMentionClickEvent.userId) {
|
userMentionClickEvent.userId
|
||||||
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(conversationUser?.baseUrl, "1",
|
) {
|
||||||
userMentionClickEvent.userId, null)
|
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||||
|
conversationUser?.baseUrl, "1",
|
||||||
|
userMentionClickEvent.userId, null
|
||||||
|
)
|
||||||
|
|
||||||
ncApi?.createRoom(credentials,
|
ncApi?.createRoom(
|
||||||
retrofitBucket.url, retrofitBucket.queryMap)
|
credentials,
|
||||||
|
retrofitBucket.url, retrofitBucket.queryMap
|
||||||
|
)
|
||||||
?.subscribeOn(Schedulers.io())
|
?.subscribeOn(Schedulers.io())
|
||||||
?.observeOn(AndroidSchedulers.mainThread())
|
?.observeOn(AndroidSchedulers.mainThread())
|
||||||
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
?.`as`(AutoDispose.autoDisposable(scopeProvider))
|
||||||
@ -1314,12 +1473,16 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
if (conversationUser != null) {
|
if (conversationUser != null) {
|
||||||
if (conversationUser.hasSpreedFeatureCapability("chat-v2")) {
|
if (conversationUser.hasSpreedFeatureCapability("chat-v2")) {
|
||||||
bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION,
|
bundle.putParcelable(
|
||||||
Parcels.wrap(roomOverall.ocs.data))
|
BundleKeys.KEY_ACTIVE_CONVERSATION,
|
||||||
|
Parcels.wrap(roomOverall.ocs.data)
|
||||||
|
)
|
||||||
conversationIntent.putExtras(bundle)
|
conversationIntent.putExtras(bundle)
|
||||||
|
|
||||||
ConductorRemapping.remapChatController(router, conversationUser.id,
|
ConductorRemapping.remapChatController(
|
||||||
roomOverall.ocs.data.token, bundle, false)
|
router, conversationUser.id,
|
||||||
|
roomOverall.ocs.data.token, bundle, false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -213,7 +213,6 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
|||||||
@Override
|
@Override
|
||||||
protected void onAttach(@NonNull View view) {
|
protected void onAttach(@NonNull View view) {
|
||||||
super.onAttach(view);
|
super.onAttach(view);
|
||||||
eventBus.register(this);
|
|
||||||
|
|
||||||
if (isNewConversationView) {
|
if (isNewConversationView) {
|
||||||
toggleNewCallHeaderVisibility(!isPublicCall);
|
toggleNewCallHeaderVisibility(!isPublicCall);
|
||||||
@ -283,7 +282,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
|||||||
retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
|
retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<RoomOverall>() {
|
.subscribe(new Observer<RoomOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -304,7 +303,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
|||||||
roomOverall.getOcs().getData().getToken()))
|
roomOverall.getOcs().getData().getToken()))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<RoomOverall>() {
|
.subscribe(new Observer<RoomOverall>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -501,7 +500,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
|||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.retry(3)
|
.retry(3)
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<ResponseBody>() {
|
.subscribe(new Observer<ResponseBody>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -787,7 +786,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTitle() {
|
public String getTitle() {
|
||||||
if (!isNewConversationView && !isAddingParticipantsView) {
|
if (!isNewConversationView && !isAddingParticipantsView) {
|
||||||
return getResources().getString(R.string.nc_app_name);
|
return getResources().getString(R.string.nc_app_name);
|
||||||
} else {
|
} else {
|
||||||
@ -855,12 +854,6 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetach(@NonNull View view) {
|
|
||||||
super.onDetach(view);
|
|
||||||
eventBus.unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onItemClick(View view, int position) {
|
public boolean onItemClick(View view, int position) {
|
||||||
if (adapter.getItem(position) instanceof UserItem) {
|
if (adapter.getItem(position) instanceof UserItem) {
|
||||||
@ -878,7 +871,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
|||||||
retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
|
retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<RoomOverall>() {
|
.subscribe(new Observer<RoomOverall>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
|
@ -130,10 +130,6 @@ class ConversationInfoController(args: Bundle) : BaseController(), FlexibleAdapt
|
|||||||
|
|
||||||
@set:Inject
|
@set:Inject
|
||||||
lateinit var ncApi: NcApi
|
lateinit var ncApi: NcApi
|
||||||
@set:Inject
|
|
||||||
lateinit var context: Context
|
|
||||||
@set:Inject
|
|
||||||
lateinit var eventBus: EventBus
|
|
||||||
|
|
||||||
private val conversationToken: String?
|
private val conversationToken: String?
|
||||||
private val conversationUser: UserEntity?
|
private val conversationUser: UserEntity?
|
||||||
@ -185,7 +181,6 @@ class ConversationInfoController(args: Bundle) : BaseController(), FlexibleAdapt
|
|||||||
|
|
||||||
override fun onAttach(view: View) {
|
override fun onAttach(view: View) {
|
||||||
super.onAttach(view)
|
super.onAttach(view)
|
||||||
eventBus.register(this)
|
|
||||||
|
|
||||||
if (databaseStorageModule == null) {
|
if (databaseStorageModule == null) {
|
||||||
databaseStorageModule = DatabaseStorageModule(conversationUser!!, conversationToken)
|
databaseStorageModule = DatabaseStorageModule(conversationUser!!, conversationToken)
|
||||||
@ -306,11 +301,6 @@ class ConversationInfoController(args: Bundle) : BaseController(), FlexibleAdapt
|
|||||||
getListOfParticipants()
|
getListOfParticipants()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetach(view: View) {
|
|
||||||
super.onDetach(view)
|
|
||||||
eventBus.unregister(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showDeleteConversationDialog(savedInstanceState: Bundle?) {
|
private fun showDeleteConversationDialog(savedInstanceState: Bundle?) {
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL)
|
LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL)
|
||||||
|
@ -236,8 +236,6 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
@Override
|
@Override
|
||||||
protected void onAttach(@NonNull View view) {
|
protected void onAttach(@NonNull View view) {
|
||||||
super.onAttach(view);
|
super.onAttach(view);
|
||||||
eventBus.register(this);
|
|
||||||
|
|
||||||
currentUser = userUtils.getCurrentUser();
|
currentUser = userUtils.getCurrentUser();
|
||||||
|
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
@ -247,11 +245,6 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetach(@NonNull View view) {
|
|
||||||
super.onDetach(view);
|
|
||||||
eventBus.unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initSearchView() {
|
private void initSearchView() {
|
||||||
if (getActivity() != null) {
|
if (getActivity() != null) {
|
||||||
@ -318,7 +311,7 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(currentUser.getBaseUrl()))
|
ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(currentUser.getBaseUrl()))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(roomsOverall -> {
|
.subscribe(roomsOverall -> {
|
||||||
|
|
||||||
if (adapterWasNull) {
|
if (adapterWasNull) {
|
||||||
@ -574,7 +567,7 @@ public class ConversationsListController extends BaseController implements Searc
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTitle() {
|
public String getTitle() {
|
||||||
return getResources().getString(R.string.nc_app_name);
|
return getResources().getString(R.string.nc_app_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ public class RingtoneSelectionController extends BaseController implements Flexi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTitle() {
|
public String getTitle() {
|
||||||
return getResources().getString(R.string.nc_settings_notification_sounds);
|
return getResources().getString(R.string.nc_settings_notification_sounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ public class ServerSelectionController extends BaseController {
|
|||||||
ncApi.getServerStatus(queryUrl)
|
ncApi.getServerStatus(queryUrl)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(status -> {
|
.subscribe(status -> {
|
||||||
String productName = getResources().getString(R.string.nc_server_product_name);
|
String productName = getResources().getString(R.string.nc_server_product_name);
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ public class SettingsController extends BaseController {
|
|||||||
ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl()))
|
ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl()))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(userProfileOverall -> {
|
.subscribe(userProfileOverall -> {
|
||||||
|
|
||||||
String displayName = null;
|
String displayName = null;
|
||||||
@ -533,7 +533,7 @@ public class SettingsController extends BaseController {
|
|||||||
null, currentUser.getId(), null, null, null)
|
null, currentUser.getId(), null, null, null)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(userEntityResult -> {
|
.subscribe(userEntityResult -> {
|
||||||
displayNameTextView.setText(userEntityResult.getDisplayName());
|
displayNameTextView.setText(userEntityResult.getDisplayName());
|
||||||
},
|
},
|
||||||
@ -667,7 +667,7 @@ public class SettingsController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTitle() {
|
public String getTitle() {
|
||||||
return getResources().getString(R.string.nc_settings);
|
return getResources().getString(R.string.nc_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ public class SwitchAccountController extends BaseController {
|
|||||||
userUtils.createOrUpdateUser(null,
|
userUtils.createOrUpdateUser(null,
|
||||||
null, null, null,
|
null, null, null,
|
||||||
null, true, null, userEntity.getId(), null, null, null)
|
null, true, null, userEntity.getId(), null, null, null)
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(new Observer<UserEntity>() {
|
.subscribe(new Observer<UserEntity>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Disposable d) {
|
public void onSubscribe(Disposable d) {
|
||||||
@ -243,7 +243,7 @@ public class SwitchAccountController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTitle() {
|
public String getTitle() {
|
||||||
return getResources().getString(R.string.nc_select_an_account);
|
return getResources().getString(R.string.nc_select_an_account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,7 +392,7 @@ public class WebViewLoginController extends BaseController {
|
|||||||
null, currentUser.getId(), null, appPreferences.getTemporaryClientCertAlias(), null)
|
null, currentUser.getId(), null, appPreferences.getTemporaryClientCertAlias(), null)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.as(AutoDispose.autoDisposable(scopeProvider))
|
.as(AutoDispose.autoDisposable(getScopeProvider()))
|
||||||
.subscribe(userEntity -> {
|
.subscribe(userEntity -> {
|
||||||
if (finalMessageType != null) {
|
if (finalMessageType != null) {
|
||||||
ApplicationWideMessageHolder.getInstance().setMessageType(finalMessageType);
|
ApplicationWideMessageHolder.getInstance().setMessageType(finalMessageType);
|
||||||
|
@ -1,166 +0,0 @@
|
|||||||
/**
|
|
||||||
* Nextcloud Talk application
|
|
||||||
*
|
|
||||||
* @author BlueLine Labs, Inc.
|
|
||||||
* Copyright (C) 2016 BlueLine Labs, Inc.
|
|
||||||
* <p>
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* <p>
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* <p>
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.nextcloud.talk.controllers.base;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import autodagger.AutoInjector;
|
|
||||||
import com.bluelinelabs.conductor.Controller;
|
|
||||||
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider;
|
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
|
||||||
import com.nextcloud.talk.controllers.AccountVerificationController;
|
|
||||||
import com.nextcloud.talk.controllers.ServerSelectionController;
|
|
||||||
import com.nextcloud.talk.controllers.SwitchAccountController;
|
|
||||||
import com.nextcloud.talk.controllers.WebViewLoginController;
|
|
||||||
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider;
|
|
||||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
|
||||||
import com.uber.autodispose.lifecycle.LifecycleScopeProvider;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication.class)
|
|
||||||
public abstract class BaseController extends ButterKnifeController {
|
|
||||||
|
|
||||||
private static final String TAG = "BaseController";
|
|
||||||
|
|
||||||
public final LifecycleScopeProvider scopeProvider = ControllerScopeProvider.from(this);
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AppPreferences appPreferences;
|
|
||||||
@Inject
|
|
||||||
Context context;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onContextAvailable(Context context) {
|
|
||||||
super.onContextAvailable(context);
|
|
||||||
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
|
|
||||||
cleanTempCertPreference();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case android.R.id.home:
|
|
||||||
getRouter().popCurrentController();
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanTempCertPreference() {
|
|
||||||
List<String> temporaryClassNames = new ArrayList<>();
|
|
||||||
temporaryClassNames.add(ServerSelectionController.class.getName());
|
|
||||||
temporaryClassNames.add(AccountVerificationController.class.getName());
|
|
||||||
temporaryClassNames.add(WebViewLoginController.class.getName());
|
|
||||||
temporaryClassNames.add(SwitchAccountController.class.getName());
|
|
||||||
|
|
||||||
if (!temporaryClassNames.contains(getClass().getName())) {
|
|
||||||
appPreferences.removeTemporaryClientCertAlias();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onViewBound(@NonNull View view) {
|
|
||||||
super.onViewBound(view);
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.getIsKeyboardIncognito()) {
|
|
||||||
disableKeyboardPersonalisedLearning((ViewGroup) view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: This is just a quick demo of how an ActionBar *can* be accessed, not necessarily how it *should*
|
|
||||||
// be accessed. In a production app, this would use Dagger instead.
|
|
||||||
protected ActionBar getActionBar() {
|
|
||||||
ActionBarProvider actionBarProvider = null;
|
|
||||||
try {
|
|
||||||
actionBarProvider = ((ActionBarProvider) getActivity());
|
|
||||||
} catch (Exception exception) {
|
|
||||||
Log.d(TAG, "Failed to fetch the action bar provider");
|
|
||||||
}
|
|
||||||
return actionBarProvider != null ? actionBarProvider.getSupportActionBar() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAttach(@NonNull View view) {
|
|
||||||
super.onAttach(view);
|
|
||||||
|
|
||||||
setTitle();
|
|
||||||
if (getActionBar() != null) {
|
|
||||||
getActionBar().setDisplayHomeAsUpEnabled(getParentController() != null || getRouter().getBackstackSize() > 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetach(@NonNull View view) {
|
|
||||||
super.onDetach(view);
|
|
||||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
if (imm != null) {
|
|
||||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setTitle() {
|
|
||||||
Controller parentController = getParentController();
|
|
||||||
while (parentController != null) {
|
|
||||||
if (parentController instanceof BaseController && ((BaseController) parentController).getTitle() != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
parentController = parentController.getParentController();
|
|
||||||
}
|
|
||||||
|
|
||||||
String title = getTitle();
|
|
||||||
ActionBar actionBar = getActionBar();
|
|
||||||
if (title != null && actionBar != null) {
|
|
||||||
actionBar.setTitle(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getTitle() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
private void disableKeyboardPersonalisedLearning(final ViewGroup viewGroup) {
|
|
||||||
View view;
|
|
||||||
EditText editText;
|
|
||||||
|
|
||||||
for (int i = 0; i < viewGroup.getChildCount(); i++) {
|
|
||||||
view = viewGroup.getChildAt(i);
|
|
||||||
if (view instanceof EditText) {
|
|
||||||
editText = (EditText) view;
|
|
||||||
editText.setImeOptions(editText.getImeOptions() | EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING);
|
|
||||||
} else if (view instanceof ViewGroup) {
|
|
||||||
disableKeyboardPersonalisedLearning((ViewGroup) view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author BlueLine Labs, Inc.
|
||||||
|
* Copyright (C) 2016 BlueLine Labs, Inc.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.nextcloud.talk.controllers.base
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.EditText
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.appcompat.app.ActionBar
|
||||||
|
import autodagger.AutoInjector
|
||||||
|
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
|
import com.nextcloud.talk.controllers.AccountVerificationController
|
||||||
|
import com.nextcloud.talk.controllers.ServerSelectionController
|
||||||
|
import com.nextcloud.talk.controllers.SwitchAccountController
|
||||||
|
import com.nextcloud.talk.controllers.WebViewLoginController
|
||||||
|
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
|
||||||
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
|
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.koin.core.Koin
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.inject
|
||||||
|
import org.koin.core.qualifier.named
|
||||||
|
import org.koin.core.scope.Scope
|
||||||
|
import java.util.ArrayList
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
|
abstract class BaseController : ButterKnifeController(), KoinComponent {
|
||||||
|
|
||||||
|
val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this)
|
||||||
|
|
||||||
|
val appPreferences: AppPreferences by inject()
|
||||||
|
val context: Context by inject()
|
||||||
|
val eventBus: EventBus by inject()
|
||||||
|
|
||||||
|
// Note: This is just a quick demo of how an ActionBar *can* be accessed, not necessarily how it *should*
|
||||||
|
// be accessed. In a production app, this would use Dagger instead.
|
||||||
|
protected val actionBar: ActionBar?
|
||||||
|
get() {
|
||||||
|
var actionBarProvider: ActionBarProvider? = null
|
||||||
|
try {
|
||||||
|
actionBarProvider = activity as ActionBarProvider?
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.d(TAG, "Failed to fetch the action bar provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionBarProvider?.supportActionBar
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getKoin(): Koin {
|
||||||
|
return super.getKoin()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
router.popCurrentController()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else -> return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanTempCertPreference() {
|
||||||
|
val temporaryClassNames = ArrayList<String>()
|
||||||
|
temporaryClassNames.add(ServerSelectionController::class.java.name)
|
||||||
|
temporaryClassNames.add(AccountVerificationController::class.java.name)
|
||||||
|
temporaryClassNames.add(WebViewLoginController::class.java.name)
|
||||||
|
temporaryClassNames.add(SwitchAccountController::class.java.name)
|
||||||
|
|
||||||
|
if (!temporaryClassNames.contains(javaClass.name)) {
|
||||||
|
appPreferences.removeTemporaryClientCertAlias()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewBound(view: View) {
|
||||||
|
super.onViewBound(view)
|
||||||
|
cleanTempCertPreference()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) {
|
||||||
|
disableKeyboardPersonalisedLearning(view as ViewGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(view: View) {
|
||||||
|
super.onAttach(view)
|
||||||
|
eventBus.register(this)
|
||||||
|
|
||||||
|
setTitle()
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar!!.setDisplayHomeAsUpEnabled(parentController != null || router.backstackSize > 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetach(view: View) {
|
||||||
|
eventBus.unregister(this)
|
||||||
|
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(view.windowToken, 0)
|
||||||
|
super.onDetach(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun setTitle() {
|
||||||
|
var parentController = parentController
|
||||||
|
while (parentController != null) {
|
||||||
|
if (parentController is BaseController && parentController.getTitle() != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parentController = parentController.parentController
|
||||||
|
}
|
||||||
|
|
||||||
|
val title = getTitle()
|
||||||
|
val actionBar = actionBar
|
||||||
|
if (title != null && actionBar != null) {
|
||||||
|
actionBar.title = title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
private fun disableKeyboardPersonalisedLearning(viewGroup: ViewGroup) {
|
||||||
|
var view: View
|
||||||
|
var editText: EditText
|
||||||
|
|
||||||
|
for (i in 0 until viewGroup.childCount) {
|
||||||
|
view = viewGroup.getChildAt(i)
|
||||||
|
if (view is EditText) {
|
||||||
|
editText = view
|
||||||
|
editText.imeOptions = editText.imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
||||||
|
} else if (view is ViewGroup) {
|
||||||
|
disableKeyboardPersonalisedLearning(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val TAG = "BaseController"
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun getTitle(): String? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,7 @@ import dagger.Provides;
|
|||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
|
import java.net.CookiePolicy;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
@ -159,14 +160,15 @@ public class RestModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
CookieManager provideCookieManager() {
|
CookieManager provideCookieManager() {
|
||||||
return new CookieManager();
|
CookieManager cookieManager = new CookieManager();
|
||||||
|
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_NONE);
|
||||||
|
return cookieManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
Cache provideCache() {
|
Cache provideCache() {
|
||||||
int cacheSize = 128 * 1024 * 1024; // 128 MB
|
int cacheSize = 128 * 1024 * 1024; // 128 MB
|
||||||
|
|
||||||
return new Cache(NextcloudTalkApplication.Companion.getSharedApplication().getCacheDir(), cacheSize);
|
return new Cache(NextcloudTalkApplication.Companion.getSharedApplication().getCacheDir(), cacheSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Inspired by: https://github.com/ZahraHeydari/Android-Clean-Arch-Coroutines-Koin/blob/c63e62bf8fb9c099a983fcc7d1b5616a7318aa3f/app/src/main/java/com/android/post/data/model/ErrorModel.kt
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.data.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class designed to show different types of errors through error status & message
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
private const val NO_CONNECTION_ERROR_MESSAGE = "No connection"
|
||||||
|
private const val BAD_RESPONSE_ERROR_MESSAGE = "Bad response"
|
||||||
|
private const val TIME_OUT_ERROR_MESSAGE = "Time out"
|
||||||
|
private const val EMPTY_RESPONSE_ERROR_MESSAGE = "Empty response"
|
||||||
|
private const val NOT_DEFINED_ERROR_MESSAGE = "Not defined"
|
||||||
|
private const val UNAUTHORIZED_ERROR_MESSAGE = "Unauthorized"
|
||||||
|
|
||||||
|
data class ErrorModel(
|
||||||
|
val message: String?,
|
||||||
|
val code: Int?,
|
||||||
|
var errorStatus: ErrorStatus
|
||||||
|
) {
|
||||||
|
constructor(errorStatus: ErrorStatus) : this(null, null, errorStatus)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
message: String?,
|
||||||
|
errorStatus: ErrorStatus
|
||||||
|
) : this(message, null, errorStatus)
|
||||||
|
|
||||||
|
fun getErrorMessage(): String {
|
||||||
|
return when (errorStatus) {
|
||||||
|
ErrorStatus.NO_CONNECTION -> NO_CONNECTION_ERROR_MESSAGE
|
||||||
|
ErrorStatus.BAD_RESPONSE -> BAD_RESPONSE_ERROR_MESSAGE
|
||||||
|
ErrorStatus.TIMEOUT -> TIME_OUT_ERROR_MESSAGE
|
||||||
|
ErrorStatus.EMPTY_RESPONSE -> EMPTY_RESPONSE_ERROR_MESSAGE
|
||||||
|
ErrorStatus.NOT_DEFINED -> NOT_DEFINED_ERROR_MESSAGE
|
||||||
|
ErrorStatus.UNAUTHORIZED -> UNAUTHORIZED_ERROR_MESSAGE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* various error status to know what happened if something goes wrong with a repository call
|
||||||
|
*/
|
||||||
|
enum class ErrorStatus {
|
||||||
|
/**
|
||||||
|
* error in connecting to repository (Server or Database)
|
||||||
|
*/
|
||||||
|
NO_CONNECTION,
|
||||||
|
/**
|
||||||
|
* error in getting value (Json Error, Server Error, etc)
|
||||||
|
*/
|
||||||
|
BAD_RESPONSE,
|
||||||
|
/**
|
||||||
|
* Time out error
|
||||||
|
*/
|
||||||
|
TIMEOUT,
|
||||||
|
/**
|
||||||
|
* no data available in repository
|
||||||
|
*/
|
||||||
|
EMPTY_RESPONSE,
|
||||||
|
/**
|
||||||
|
* an unexpected error
|
||||||
|
*/
|
||||||
|
NOT_DEFINED,
|
||||||
|
/**
|
||||||
|
* bad credentials
|
||||||
|
*/
|
||||||
|
UNAUTHORIZED
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.data.repository
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.database.UserEntity
|
||||||
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||||
|
import com.nextcloud.talk.newarch.data.source.remote.ApiService
|
||||||
|
import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository
|
||||||
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
|
||||||
|
class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : NextcloudTalkRepository {
|
||||||
|
override suspend fun getConversations(user: UserEntity): List<Conversation> {
|
||||||
|
return apiService.getConversations(ApiUtils.getCredentials(user.username, user.token),
|
||||||
|
ApiUtils.getUrlForGetRooms(user.baseUrl)).ocs.data
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Inspired by https://github.com/ZahraHeydari/Android-Clean-Arch-Coroutines-Koin/blob
|
||||||
|
* /cfed12ecbfa425de53de505e76b3ec06c3bbdaca/app/src/main/java/com/android/post/data/source/remote/ApiErrorHandle.kt
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.data.source.remote
|
||||||
|
|
||||||
|
import com.nextcloud.talk.newarch.data.model.ErrorModel
|
||||||
|
import okhttp3.ResponseBody
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class trace exceptions(api call or parse data or connection errors) &
|
||||||
|
* depending on what exception returns a [ErrorModel]
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
class ApiErrorHandler {
|
||||||
|
|
||||||
|
fun traceErrorException(throwable: Throwable?): ErrorModel {
|
||||||
|
val errorModel: ErrorModel? = when (throwable) {
|
||||||
|
|
||||||
|
// if throwable is an instance of HttpException
|
||||||
|
// then attempt to parse error data from response body
|
||||||
|
is HttpException -> {
|
||||||
|
// handle UNAUTHORIZED situation (when token expired)
|
||||||
|
if (throwable.code() == 401) {
|
||||||
|
ErrorModel(throwable.message(), throwable.code(), ErrorModel.ErrorStatus.UNAUTHORIZED)
|
||||||
|
} else {
|
||||||
|
getHttpError(throwable.response()?.errorBody())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle api call timeout error
|
||||||
|
is SocketTimeoutException -> {
|
||||||
|
ErrorModel(throwable.message, ErrorModel.ErrorStatus.TIMEOUT)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle connection error
|
||||||
|
is IOException -> {
|
||||||
|
ErrorModel(throwable.message, ErrorModel.ErrorStatus.NO_CONNECTION)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return errorModel ?: ErrorModel("No Defined Error!", 0, ErrorModel.ErrorStatus.BAD_RESPONSE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* attempts to parse http response body and get error data from it
|
||||||
|
*
|
||||||
|
* @param body retrofit response body
|
||||||
|
* @return returns an instance of [ErrorModel] with parsed data or NOT_DEFINED status
|
||||||
|
*/
|
||||||
|
private fun getHttpError(body: ResponseBody?): ErrorModel {
|
||||||
|
return try {
|
||||||
|
// use response body to get error detail
|
||||||
|
ErrorModel(body.toString(), 400, ErrorModel.ErrorStatus.BAD_RESPONSE)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
ErrorModel(message = e.message, errorStatus = ErrorModel.ErrorStatus.NOT_DEFINED)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.data.source.remote
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Header
|
||||||
|
import retrofit2.http.Url
|
||||||
|
|
||||||
|
interface ApiService {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
suspend fun getConversations(
|
||||||
|
@Header(
|
||||||
|
"Authorization"
|
||||||
|
) authorization: String, @Url url: String
|
||||||
|
): RoomsOverall
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.di.module
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val CommunicationModule = module {
|
||||||
|
single { createEventBus() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createEventBus() : EventBus {
|
||||||
|
return EventBus.getDefault();
|
||||||
|
}
|
@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.di.module
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import com.github.aurae.retrofit2.LoganSquareConverterFactory
|
||||||
|
import com.nextcloud.talk.BuildConfig
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
|
import com.nextcloud.talk.newarch.data.repository.NextcloudTalkRepositoryImpl
|
||||||
|
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
|
||||||
|
import com.nextcloud.talk.newarch.data.source.remote.ApiService
|
||||||
|
import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository
|
||||||
|
import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase
|
||||||
|
import com.nextcloud.talk.newarch.utils.NetworkUtils
|
||||||
|
import com.nextcloud.talk.newarch.utils.NetworkUtils.GetProxyRunnable
|
||||||
|
import com.nextcloud.talk.newarch.utils.NetworkUtils.MagicAuthenticator
|
||||||
|
import com.nextcloud.talk.utils.LoggingUtils
|
||||||
|
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||||
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
|
import com.nextcloud.talk.utils.ssl.MagicKeyManager
|
||||||
|
import com.nextcloud.talk.utils.ssl.MagicTrustManager
|
||||||
|
import com.nextcloud.talk.utils.ssl.SSLSocketFactoryCompat
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import okhttp3.Cache
|
||||||
|
import okhttp3.Credentials
|
||||||
|
import okhttp3.Dispatcher
|
||||||
|
import okhttp3.JavaNetCookieJar
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.internal.tls.OkHostnameVerifier
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor.Logger
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.CookieManager
|
||||||
|
import java.net.CookiePolicy.ACCEPT_NONE
|
||||||
|
import java.net.Proxy
|
||||||
|
import java.security.KeyStore
|
||||||
|
import java.security.KeyStoreException
|
||||||
|
import java.security.NoSuchAlgorithmException
|
||||||
|
import java.security.UnrecoverableKeyException
|
||||||
|
import java.security.cert.CertificateException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.net.ssl.KeyManagerFactory
|
||||||
|
import javax.net.ssl.X509KeyManager
|
||||||
|
|
||||||
|
val NetworkModule = module {
|
||||||
|
single { createService(get()) }
|
||||||
|
single { createRetrofit(get()) }
|
||||||
|
single { createOkHttpClient(androidContext(), get(), get(), get(), get(), get(), get(), get()) }
|
||||||
|
factory { createGetConversationsUseCase(get(), get()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCookieManager(): CookieManager {
|
||||||
|
val cookieManager = CookieManager()
|
||||||
|
cookieManager.setCookiePolicy(ACCEPT_NONE)
|
||||||
|
return cookieManager
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createOkHttpClient(
|
||||||
|
context: Context,
|
||||||
|
proxy: Proxy,
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
magicTrustManager: MagicTrustManager,
|
||||||
|
sslSocketFactoryCompat: SSLSocketFactoryCompat,
|
||||||
|
cache: Cache,
|
||||||
|
cookieManager: CookieManager,
|
||||||
|
dispatcher: Dispatcher
|
||||||
|
): OkHttpClient {
|
||||||
|
val httpClient = OkHttpClient.Builder()
|
||||||
|
|
||||||
|
httpClient.retryOnConnectionFailure(true)
|
||||||
|
httpClient.connectTimeout(45, TimeUnit.SECONDS)
|
||||||
|
httpClient.readTimeout(45, TimeUnit.SECONDS)
|
||||||
|
httpClient.writeTimeout(45, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
httpClient.cookieJar(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))
|
||||||
|
|
||||||
|
httpClient.dispatcher(dispatcher)
|
||||||
|
if (Proxy.NO_PROXY != proxy) {
|
||||||
|
httpClient.proxy(proxy)
|
||||||
|
|
||||||
|
if (appPreferences.proxyCredentials &&
|
||||||
|
!TextUtils.isEmpty(appPreferences.proxyUsername) &&
|
||||||
|
!TextUtils.isEmpty(appPreferences.proxyPassword)
|
||||||
|
) {
|
||||||
|
httpClient.proxyAuthenticator(
|
||||||
|
MagicAuthenticator(
|
||||||
|
Credentials.basic(
|
||||||
|
appPreferences.proxyUsername,
|
||||||
|
appPreferences.proxyPassword
|
||||||
|
), "Proxy-Authorization"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient.addInterceptor(NetworkUtils.HeadersInterceptor())
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG && !context.getResources().getBoolean(R.bool.nc_is_debug)) {
|
||||||
|
val loggingInterceptor = HttpLoggingInterceptor()
|
||||||
|
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
|
||||||
|
loggingInterceptor.redactHeader("Authorization")
|
||||||
|
loggingInterceptor.redactHeader("Proxy-Authorization")
|
||||||
|
loggingInterceptor.redactHeader("Cookie")
|
||||||
|
httpClient.addInterceptor(loggingInterceptor)
|
||||||
|
} else if (context.getResources().getBoolean(R.bool.nc_is_debug)) {
|
||||||
|
|
||||||
|
val fileLogger = HttpLoggingInterceptor(object : Logger {
|
||||||
|
override fun log(message: String) {
|
||||||
|
LoggingUtils.writeLogEntryToFile(context, message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fileLogger.level = HttpLoggingInterceptor.Level.BODY
|
||||||
|
fileLogger.redactHeader("Authorization")
|
||||||
|
fileLogger.redactHeader("Proxy-Authorization")
|
||||||
|
fileLogger.redactHeader("Cookie")
|
||||||
|
httpClient.addInterceptor(fileLogger)
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createRetrofit(okHttpClient: OkHttpClient): Retrofit {
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.client(okHttpClient)
|
||||||
|
.baseUrl("https://nextcloud.com")
|
||||||
|
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
|
||||||
|
.addConverterFactory(LoganSquareConverterFactory.create())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTrustManager(): MagicTrustManager {
|
||||||
|
return MagicTrustManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSslSocketFactory(magicKeyManager: MagicKeyManager, magicTrustManager:
|
||||||
|
MagicTrustManager) : SSLSocketFactoryCompat {
|
||||||
|
return SSLSocketFactoryCompat(magicKeyManager, magicTrustManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createKeyManager(
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
userUtils: UserUtils
|
||||||
|
): MagicKeyManager? {
|
||||||
|
val keyStore: KeyStore?
|
||||||
|
try {
|
||||||
|
keyStore = KeyStore.getInstance("AndroidKeyStore")
|
||||||
|
keyStore.load(null)
|
||||||
|
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
|
||||||
|
kmf.init(keyStore, null)
|
||||||
|
val origKm = kmf.keyManagers[0] as X509KeyManager
|
||||||
|
return MagicKeyManager(origKm, userUtils, appPreferences)
|
||||||
|
} catch (e: KeyStoreException) {
|
||||||
|
Log.e("NetworkModule", "KeyStoreException " + e.localizedMessage!!)
|
||||||
|
} catch (e: CertificateException) {
|
||||||
|
Log.e("NetworkModule", "CertificateException " + e.localizedMessage!!)
|
||||||
|
} catch (e: NoSuchAlgorithmException) {
|
||||||
|
Log.e("NetworkModule", "NoSuchAlgorithmException " + e.localizedMessage!!)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e("NetworkModule", "IOException " + e.localizedMessage!!)
|
||||||
|
} catch (e: UnrecoverableKeyException) {
|
||||||
|
Log.e("NetworkModule", "UnrecoverableKeyException " + e.localizedMessage!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createProxy(appPreferences: AppPreferences): Proxy {
|
||||||
|
if (!TextUtils.isEmpty(appPreferences.proxyType) && "No proxy" != appPreferences.proxyType
|
||||||
|
&& !TextUtils.isEmpty(appPreferences.proxyHost)
|
||||||
|
) {
|
||||||
|
val getProxyRunnable = GetProxyRunnable(appPreferences)
|
||||||
|
val getProxyThread = Thread(getProxyRunnable)
|
||||||
|
getProxyThread.start()
|
||||||
|
try {
|
||||||
|
getProxyThread.join()
|
||||||
|
return getProxyRunnable.proxyValue
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
Log.e("NetworkModule", "Failed to join the thread while getting proxy: " + e.localizedMessage)
|
||||||
|
return Proxy.NO_PROXY
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return Proxy.NO_PROXY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDispatcher() : Dispatcher {
|
||||||
|
val dispatcher = Dispatcher()
|
||||||
|
dispatcher.maxRequestsPerHost = 100
|
||||||
|
dispatcher.maxRequests = 100
|
||||||
|
return dispatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCache(androidApplication: NextcloudTalkApplication) : Cache {
|
||||||
|
val cacheSize = 128 * 1024 * 1024 // 128 MB
|
||||||
|
return Cache(androidApplication.cacheDir, cacheSize.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createApiErrorHandler(): ApiErrorHandler {
|
||||||
|
return ApiErrorHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createService(retrofit: Retrofit): ApiService {
|
||||||
|
return retrofit.create(ApiService::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createNextcloudTalkRepository(apiService: ApiService): NextcloudTalkRepository {
|
||||||
|
return NextcloudTalkRepositoryImpl(apiService)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createGetConversationsUseCase(
|
||||||
|
nextcloudTalkRepository: NextcloudTalkRepository,
|
||||||
|
apiErrorHandler: ApiErrorHandler
|
||||||
|
): GetConversationsUseCase {
|
||||||
|
return GetConversationsUseCase(nextcloudTalkRepository, apiErrorHandler)
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.di.module
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.nextcloud.talk.R.string
|
||||||
|
import com.nextcloud.talk.models.database.Models
|
||||||
|
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||||
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
|
import io.requery.Persistable
|
||||||
|
import io.requery.android.sqlcipher.SqlCipherDatabaseSource
|
||||||
|
import io.requery.reactivex.ReactiveEntityStore
|
||||||
|
import io.requery.reactivex.ReactiveSupport
|
||||||
|
import io.requery.sql.EntityDataStore
|
||||||
|
import net.orange_box.storebox.StoreBox
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val StorageModule = module {
|
||||||
|
single { createPreferences(androidContext()) }
|
||||||
|
single { createSqlCipherDatabaseSource(androidContext()) }
|
||||||
|
single { createDataStore(get()) }
|
||||||
|
single { createUserUtils(get()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPreferences(context: Context): AppPreferences {
|
||||||
|
return StoreBox.create<AppPreferences>(context, AppPreferences::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createSqlCipherDatabaseSource(context: Context): SqlCipherDatabaseSource {
|
||||||
|
return SqlCipherDatabaseSource(context, Models.DEFAULT,
|
||||||
|
context.resources.getString(string.nc_app_name).toLowerCase()
|
||||||
|
.replace(" ", "_").trim { it <= ' ' } + ".sqlite",
|
||||||
|
context.getString(string.nc_talk_database_encryption_key), 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDataStore(sqlCipherDatabaseSource: SqlCipherDatabaseSource): ReactiveEntityStore<Persistable> {
|
||||||
|
val configuration = sqlCipherDatabaseSource.configuration
|
||||||
|
return ReactiveSupport.toReactiveStore(EntityDataStore(configuration))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createUserUtils(dataStore : ReactiveEntityStore<Persistable>) : UserUtils {
|
||||||
|
return UserUtils(dataStore);
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.domain.repository
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.database.UserEntity
|
||||||
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||||
|
|
||||||
|
interface NextcloudTalkRepository {
|
||||||
|
suspend fun getConversations(user: UserEntity): List<Conversation>
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.domain.usecases
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||||
|
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
|
||||||
|
import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository
|
||||||
|
import com.nextcloud.talk.newarch.domain.usecases.base.UseCase
|
||||||
|
|
||||||
|
class GetConversationsUseCase constructor(
|
||||||
|
private val nextcloudTalkRepository: NextcloudTalkRepository,
|
||||||
|
apiErrorHandler: ApiErrorHandler?
|
||||||
|
) : UseCase<List<Conversation>, Any?>(apiErrorHandler) {
|
||||||
|
|
||||||
|
override suspend fun run(params: Any?): List<Conversation> {
|
||||||
|
return nextcloudTalkRepository.getConversations(user);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.domain.usecases.base
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.database.UserEntity
|
||||||
|
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import retrofit2.HttpException
|
||||||
|
|
||||||
|
abstract class UseCase<Type, in Params>(private val apiErrorHandler: ApiErrorHandler?) where Type : Any {
|
||||||
|
|
||||||
|
abstract suspend fun run(params: Params? = null): Type
|
||||||
|
lateinit var user: UserEntity
|
||||||
|
|
||||||
|
fun invoke(
|
||||||
|
scope: CoroutineScope,
|
||||||
|
params: Params?,
|
||||||
|
onResult: (UseCaseResponse<Type>)
|
||||||
|
) {
|
||||||
|
val backgroundJob = scope.async { run(params) }
|
||||||
|
scope.launch {
|
||||||
|
backgroundJob.await().let {
|
||||||
|
try {
|
||||||
|
onResult.onSuccess(it)
|
||||||
|
} catch (e: HttpException) {
|
||||||
|
onResult.onError(apiErrorHandler?.traceErrorException(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.domain.usecases.base
|
||||||
|
|
||||||
|
import com.nextcloud.talk.newarch.data.model.ErrorModel
|
||||||
|
|
||||||
|
interface UseCaseResponse<Type> {
|
||||||
|
fun onSuccess(result: Type)
|
||||||
|
fun onError(errorModel: ErrorModel?)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.utils
|
||||||
|
|
||||||
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
|
import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder
|
||||||
|
import okhttp3.Authenticator
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.Route
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.Proxy
|
||||||
|
import java.net.Proxy.NO_PROXY
|
||||||
|
import java.net.Proxy.Type
|
||||||
|
import java.net.Proxy.Type.SOCKS
|
||||||
|
|
||||||
|
class NetworkUtils {
|
||||||
|
class HeadersInterceptor : Interceptor {
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val original = chain.request()
|
||||||
|
val request = original.newBuilder()
|
||||||
|
.header("User-Agent", ApiUtils.getUserAgent())
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header("OCS-APIRequest", "true")
|
||||||
|
.method(original.method, original.body)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val response = chain.proceed(request)
|
||||||
|
|
||||||
|
if (request.url.encodedPath.contains("/avatar/")) {
|
||||||
|
AvatarStatusCodeHolder.getInstance()
|
||||||
|
.statusCode = response.code
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MagicAuthenticator(
|
||||||
|
private val credentials: String,
|
||||||
|
private val authenticatorType: String
|
||||||
|
) : Authenticator {
|
||||||
|
|
||||||
|
override fun authenticate(
|
||||||
|
route: Route?,
|
||||||
|
response: Response
|
||||||
|
): Request? {
|
||||||
|
if (response.request.header(authenticatorType) != null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
var countedResponse: Response? = response
|
||||||
|
|
||||||
|
var attemptsCount = 0
|
||||||
|
|
||||||
|
|
||||||
|
while ({ countedResponse = countedResponse?.priorResponse; countedResponse }() != null) {
|
||||||
|
attemptsCount++
|
||||||
|
if (attemptsCount == 3) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response.request.newBuilder()
|
||||||
|
.header(authenticatorType, credentials)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GetProxyRunnable internal constructor(private val appPreferences: AppPreferences) :
|
||||||
|
Runnable {
|
||||||
|
@Volatile internal var proxyValue: Proxy = NO_PROXY
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
if (SOCKS == Type.valueOf(appPreferences.proxyType)) {
|
||||||
|
proxyValue = Proxy(
|
||||||
|
Type.valueOf(appPreferences.proxyType),
|
||||||
|
InetSocketAddress.createUnresolved(
|
||||||
|
appPreferences.proxyHost, Integer.parseInt(
|
||||||
|
appPreferences.proxyPort
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
proxyValue = Proxy(
|
||||||
|
Type.valueOf(appPreferences.proxyType),
|
||||||
|
InetSocketAddress(
|
||||||
|
appPreferences.proxyHost,
|
||||||
|
Integer.parseInt(appPreferences.proxyPort)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -36,7 +36,7 @@ import java.util.List;
|
|||||||
public class UserUtils {
|
public class UserUtils {
|
||||||
private ReactiveEntityStore<Persistable> dataStore;
|
private ReactiveEntityStore<Persistable> dataStore;
|
||||||
|
|
||||||
UserUtils(ReactiveEntityStore<Persistable> dataStore) {
|
public UserUtils(ReactiveEntityStore<Persistable> dataStore) {
|
||||||
this.dataStore = dataStore;
|
this.dataStore = dataStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user