From a695a8448ddf6f24cfc274c06f26fb4e80731dfb Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Sun, 8 Dec 2019 15:15:22 +0100 Subject: [PATCH] Further progress on user migration --- .../1.json | 8 +- .../nextcloud/talk/activities/MainActivity.kt | 43 +- .../AccountVerificationController.java | 497 ------------------ .../AccountVerificationController.kt | 453 ++++++++++++++++ .../talk/controllers/ChatController.kt | 6 +- .../talk/controllers/ContactsController.kt | 6 +- .../controllers/ConversationInfoController.kt | 2 +- .../jobs/AddParticipantsToConversation.kt | 2 +- .../nextcloud/talk/jobs/CapabilitiesWorker.kt | 4 +- .../talk/jobs/LeaveConversationWorker.kt | 2 +- .../nextcloud/talk/jobs/NotificationWorker.kt | 6 +- .../talk/jobs/SignalingSettingsWorker.kt | 20 +- .../repository/offline/UsersRepositoryImpl.kt | 8 + .../repository/offline/UsersRepository.kt | 2 + .../ConversationListViewModelFactory.kt | 3 +- .../ConversationsListView.kt | 33 +- .../ConversationsListViewModel.kt | 16 +- .../di/module/ConversationsListModule.kt | 5 +- .../local/converters/UserStatusConverter.kt | 16 +- .../talk/newarch/local/dao/UsersDao.kt | 75 ++- .../talk/newarch/local/models/UserNgEntity.kt | 2 +- .../com/nextcloud/talk/utils/PushUtils.kt | 6 +- .../DatabaseStorageModule.kt | 2 +- .../talk/webrtc/MagicWebSocketInstance.kt | 2 +- .../talk/webrtc/WebSocketConnectionHelper.kt | 2 +- gradle.properties | 2 +- 26 files changed, 611 insertions(+), 612 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java create mode 100644 app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt diff --git a/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json b/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json index 7e2c62963..5bc9f32b0 100644 --- a/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json +++ b/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "c7b1b47572d7ace1b422d0a3887a54e1", + "identityHash": "3cabfd9fea2cd5a25c6c1c8103ef65fb", "entities": [ { "tableName": "conversations", @@ -287,13 +287,13 @@ }, { "tableName": "users", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `user_id` TEXT NOT NULL, `username` TEXT NOT NULL, `base_url` TEXT NOT NULL, `token` TEXT, `display_name` TEXT, `push_configuration` TEXT, `capabilities` TEXT, `client_auth_cert` TEXT, `external_signaling` TEXT, `status` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user_id` TEXT NOT NULL, `username` TEXT NOT NULL, `base_url` TEXT NOT NULL, `token` TEXT, `display_name` TEXT, `push_configuration` TEXT, `capabilities` TEXT, `client_auth_cert` TEXT, `external_signaling` TEXT, `status` INTEGER)", "fields": [ { "fieldPath": "id", "columnName": "id", "affinity": "INTEGER", - "notNull": true + "notNull": false }, { "fieldPath": "userId", @@ -369,7 +369,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c7b1b47572d7ace1b422d0a3887a54e1')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3cabfd9fea2cd5a25c6c1c8103ef65fb')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index b3150ff9b..d15d87ea0 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -42,6 +42,7 @@ import com.nextcloud.talk.controllers.CallNotificationController import com.nextcloud.talk.controllers.LockedController import com.nextcloud.talk.controllers.ServerSelectionController import com.nextcloud.talk.controllers.base.providers.ActionBarProvider +import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView import com.nextcloud.talk.utils.ConductorRemapping import com.nextcloud.talk.utils.SecurityUtils @@ -50,6 +51,9 @@ import com.nextcloud.talk.utils.database.user.UserUtils import io.requery.Persistable import io.requery.android.sqlcipher.SqlCipherDatabaseSource import io.requery.reactivex.ReactiveEntityStore +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -60,10 +64,8 @@ class MainActivity : BaseActivity(), ActionBarProvider { @BindView(R.id.controller_container) lateinit var container: ViewGroup - @Inject - lateinit var userUtils: UserUtils - @Inject - lateinit var dataStore: ReactiveEntityStore + val usersRepository: UsersRepository by inject() + @Inject lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource @@ -89,6 +91,7 @@ class MainActivity : BaseActivity(), ActionBarProvider { hasDb = false } + if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { if (!router!!.hasRootController()) { router!!.setRoot( @@ -100,18 +103,26 @@ class MainActivity : BaseActivity(), ActionBarProvider { onNewIntent(intent) } else if (!router!!.hasRootController()) { if (hasDb) { - if (userUtils.anyUserExists()) { - router!!.setRoot( - RouterTransaction.with(ConversationsListView()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) - } else { - router!!.setRoot( - RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) + GlobalScope.launch { + if (usersRepository.getUsers().count() > 0) { + runOnUiThread { + router!!.setRoot( + RouterTransaction.with(ConversationsListView()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + + } + } else { + runOnUiThread { + router!!.setRoot( + RouterTransaction.with(ServerSelectionController()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } + } + } } else { router!!.setRoot( diff --git a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java deleted file mode 100644 index 4d5ee737e..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 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 . - */ - -package com.nextcloud.talk.controllers; - -import android.content.pm.ActivityInfo; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import autodagger.AutoInjector; -import butterknife.BindView; -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.nextcloud.talk.R; -import com.nextcloud.talk.api.NcApi; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.events.EventStatus; -import com.nextcloud.talk.jobs.CapabilitiesWorker; -import com.nextcloud.talk.jobs.PushRegistrationWorker; -import com.nextcloud.talk.jobs.SignalingSettingsWorker; -import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.models.json.conversations.RoomsOverall; -import com.nextcloud.talk.models.json.generic.Status; -import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; -import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView; -import com.nextcloud.talk.utils.ApiUtils; -import com.nextcloud.talk.utils.ClosedInterfaceImpl; -import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.database.user.UserUtils; -import com.nextcloud.talk.utils.preferences.AppPreferences; -import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder; -import com.uber.autodispose.AutoDispose; -import io.reactivex.CompletableObserver; -import io.reactivex.Observer; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import java.net.CookieManager; -import javax.inject.Inject; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -@AutoInjector(NextcloudTalkApplication.class) -public class AccountVerificationController extends BaseController { - - public static final String TAG = "AccountVerificationController"; - - @Inject - NcApi ncApi; - - @Inject - UserUtils userUtils; - - @Inject - CookieManager cookieManager; - - @Inject - AppPreferences appPreferences; - - @Inject - EventBus eventBus; - - @BindView(R.id.progress_text) - TextView progressText; - - private long internalAccountId = -1; - - private String baseUrl; - private String username; - private String token; - private boolean isAccountImport; - private String originalProtocol; - - public AccountVerificationController(Bundle args) { - super(); - if (args != null) { - baseUrl = args.getString(BundleKeys.INSTANCE.getKEY_BASE_URL()); - username = args.getString(BundleKeys.INSTANCE.getKEY_USERNAME()); - token = args.getString(BundleKeys.INSTANCE.getKEY_TOKEN()); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT())) { - isAccountImport = true; - } - if (args.containsKey(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL())) { - originalProtocol = args.getString(BundleKeys.INSTANCE.getKEY_ORIGINAL_PROTOCOL()); - } - } - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_account_verification, container, false); - } - - @Override - protected void onDetach(@NonNull View view) { - eventBus.unregister(this); - super.onDetach(view); - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - eventBus.register(this); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication() - .getComponentApplication() - .inject(this); - - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } - - if (getActionBar() != null) { - getActionBar().hide(); - } - - if (isAccountImport && !baseUrl.startsWith("http://") && !baseUrl.startsWith("https://") - || (!TextUtils - .isEmpty(originalProtocol) && !baseUrl.startsWith(originalProtocol))) { - determineBaseUrlProtocol(true); - } else { - checkEverything(); - } - } - - private void checkEverything() { - String credentials = ApiUtils.getCredentials(username, token); - cookieManager.getCookieStore().removeAll(); - - findServerTalkApp(credentials); - } - - private void determineBaseUrlProtocol(boolean checkForcedHttps) { - cookieManager.getCookieStore().removeAll(); - - String queryUrl; - - baseUrl = baseUrl.replace("http://", "").replace("https://", ""); - - if (checkForcedHttps) { - queryUrl = "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus(); - } else { - queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus(); - } - - ncApi.getServerStatus(queryUrl) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(Status status) { - if (checkForcedHttps) { - baseUrl = "https://" + baseUrl; - } else { - baseUrl = "http://" + baseUrl; - } - - if (isAccountImport) { - getRouter().replaceTopController( - RouterTransaction.with(new WebViewLoginController(baseUrl, - false, username, "")) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - checkEverything(); - } - } - - @Override - public void onError(Throwable e) { - if (checkForcedHttps) { - determineBaseUrlProtocol(false); - } else { - abortVerification(); - } - } - - @Override - public void onComplete() { - - } - }); - } - - private void findServerTalkApp(String credentials) { - ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) - .subscribeOn(Schedulers.io()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(RoomsOverall roomsOverall) { - fetchProfile(credentials); - } - - @Override - public void onError(Throwable e) { - if (getActivity() != null && getResources() != null) { - getActivity().runOnUiThread( - () -> progressText.setText(String.format(getResources().getString( - R.string.nc_nextcloud_talk_app_not_installed), - getResources().getString(R.string.nc_app_name)))); - } - - ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK); - - abortVerification(); - } - - @Override - public void onComplete() { - - } - }); - } - - private void storeProfile(String displayName, String userId) { - userUtils.createOrUpdateUser(username, token, - baseUrl, displayName, null, true, - userId, null, null, - appPreferences.getTemporaryClientCertAlias(), null) - .subscribeOn(Schedulers.io()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(UserEntity userEntity) { - internalAccountId = userEntity.getId(); - - if (new ClosedInterfaceImpl().isGooglePlayServicesAvailable()) { - registerForPush(); - } else { - getActivity().runOnUiThread( - () -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_push_disabled))); - fetchAndStoreCapabilities(); - } - } - - @Override - public void onError(Throwable e) { - progressText.setText(progressText.getText().toString() + - "\n" + - getResources().getString( - R.string.nc_display_name_not_stored)); - abortVerification(); - } - - @Override - public void onComplete() { - - } - }); - } - - private void fetchProfile(String credentials) { - ncApi.getUserProfile(credentials, - ApiUtils.getUrlForUserProfile(baseUrl)) - .subscribeOn(Schedulers.io()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - } - - @Override - public void onNext(UserProfileOverall userProfileOverall) { - String displayName = null; - if (!TextUtils.isEmpty(userProfileOverall.ocs.data.displayName)) { - displayName = userProfileOverall.ocs.data.displayName; - } else if (!TextUtils.isEmpty(userProfileOverall.ocs.data.displayNameAlt)) { - displayName = userProfileOverall.ocs.data.displayNameAlt; - } - - if (!TextUtils.isEmpty(displayName)) { - storeProfile(displayName, userProfileOverall.ocs.data.userId); - } else { - if (getActivity() != null) { - getActivity().runOnUiThread( - () -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_display_name_not_fetched))); - } - abortVerification(); - } - } - - @Override - public void onError(Throwable e) { - if (getActivity() != null) { - getActivity().runOnUiThread( - () -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_display_name_not_fetched))); - } - abortVerification(); - } - - @Override - public void onComplete() { - - } - }); - } - - private void registerForPush() { - OneTimeWorkRequest pushRegistrationWork = - new OneTimeWorkRequest.Builder(PushRegistrationWorker.class).build(); - WorkManager.getInstance().enqueue(pushRegistrationWork); - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(EventStatus eventStatus) { - if (EventStatus.EventType.PUSH_REGISTRATION == eventStatus.eventType) { - if (internalAccountId == eventStatus.userId - && !eventStatus.allGood - && getActivity() != null) { - getActivity().runOnUiThread( - () -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_push_disabled))); - } - fetchAndStoreCapabilities(); - } else if (EventStatus.EventType.CAPABILITIES_FETCH == eventStatus.eventType) { - if (internalAccountId == eventStatus.userId && !eventStatus.allGood) { - if (getActivity() != null) { - getActivity().runOnUiThread( - () -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_capabilities_failed))); - } - abortVerification(); - } else if (internalAccountId == eventStatus.userId && eventStatus.allGood) { - fetchAndStoreExternalSignalingSettings(); - } - } else if (EventStatus.EventType.SIGNALING_SETTINGS == eventStatus.eventType) { - if (internalAccountId == eventStatus.userId && !eventStatus.allGood) { - if (getActivity() != null) { - getActivity().runOnUiThread( - () -> progressText.setText(progressText.getText().toString() + "\n" + - getResources().getString(R.string.nc_external_server_failed))); - } - } - - proceedWithLogin(); - } - } - - private void fetchAndStoreCapabilities() { - Data userData = new Data.Builder() - .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), internalAccountId) - .build(); - - OneTimeWorkRequest pushNotificationWork = - new OneTimeWorkRequest.Builder(CapabilitiesWorker.class) - .setInputData(userData) - .build(); - WorkManager.getInstance().enqueue(pushNotificationWork); - } - - private void fetchAndStoreExternalSignalingSettings() { - Data userData = new Data.Builder() - .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), internalAccountId) - .build(); - - OneTimeWorkRequest signalingSettings = - new OneTimeWorkRequest.Builder(SignalingSettingsWorker.class) - .setInputData(userData) - .build(); - WorkManager.getInstance().enqueue(signalingSettings); - } - - private void proceedWithLogin() { - cookieManager.getCookieStore().removeAll(); - userUtils.disableAllUsersWithoutId(internalAccountId); - - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - if (userUtils.getUsers().size() == 1) { - getRouter().setRoot(RouterTransaction.with(new - ConversationsListView()) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - if (isAccountImport) { - ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED); - } - getRouter().popToRoot(); - } - }); - } - } - - @Override - protected void onDestroyView(@NonNull View view) { - super.onDestroyView(view); - if (getActivity() != null) { - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); - } - } - - private void abortVerification() { - - if (!isAccountImport) { - if (internalAccountId != -1) { - userUtils.deleteUserWithId(internalAccountId).subscribe(new CompletableObserver() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onComplete() { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - new Handler().postDelayed(() -> getRouter().popToRoot(), 7500); - }); - } - } - - @Override - public void onError(Throwable e) { - - } - }); - } else { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - new Handler().postDelayed(() -> getRouter().popToRoot(), 7500); - }); - } - } - } else { - ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT); - if (getActivity() != null) { - getActivity().runOnUiThread(() -> new Handler().postDelayed(() -> { - if (getRouter().hasRootController()) { - if (getActivity() != null) { - getRouter().popToRoot(); - } - } else { - if (userUtils.anyUserExists()) { - getRouter().setRoot(RouterTransaction.with(new ConversationsListView()) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - getRouter().setRoot(RouterTransaction.with(new ServerSelectionController()) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } - } - }, 7500)); - } - } - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt new file mode 100644 index 000000000..426f28c1a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt @@ -0,0 +1,453 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 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 . + */ + +package com.nextcloud.talk.controllers + +import android.content.pm.ActivityInfo +import android.os.Bundle +import android.os.Handler +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.work.Data +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import autodagger.AutoInjector +import butterknife.BindView +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler +import com.nextcloud.talk.R +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.events.EventStatus +import com.nextcloud.talk.jobs.CapabilitiesWorker +import com.nextcloud.talk.jobs.PushRegistrationWorker +import com.nextcloud.talk.jobs.SignalingSettingsWorker +import com.nextcloud.talk.models.json.conversations.RoomsOverall +import com.nextcloud.talk.models.json.generic.Status +import com.nextcloud.talk.models.json.userprofile.UserProfileOverall +import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository +import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView +import com.nextcloud.talk.newarch.local.dao.UsersDao +import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.ClosedInterfaceImpl +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder +import com.uber.autodispose.AutoDispose +import com.uber.autodispose.ObservableSubscribeProxy +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import org.koin.core.KoinComponent +import org.koin.core.inject +import java.net.CookieManager +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class AccountVerificationController(args: Bundle?) : BaseController(), KoinComponent { + + @JvmField @Inject + internal var ncApi: NcApi? = null + + val cookieManager: CookieManager by inject() + val usersRepository: UsersRepository by inject() + val usersDao: UsersDao by inject() + + @JvmField @BindView(R.id.progress_text) + internal var progressText: TextView? = null + + private var internalAccountId: Long = -1 + + private var baseUrl: String? = null + private var username: String? = null + private var token: String? = null + private var isAccountImport: Boolean = false + private var originalProtocol: String? = null + + init { + if (args != null) { + baseUrl = args.getString(BundleKeys.KEY_BASE_URL) + username = args.getString(BundleKeys.KEY_USERNAME) + token = args.getString(BundleKeys.KEY_TOKEN) + if (args.containsKey(BundleKeys.KEY_IS_ACCOUNT_IMPORT)) { + isAccountImport = true + } + if (args.containsKey(BundleKeys.KEY_ORIGINAL_PROTOCOL)) { + originalProtocol = args.getString(BundleKeys.KEY_ORIGINAL_PROTOCOL) + } + } + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.controller_account_verification, container, false) + } + + override fun onDetach(view: View) { + eventBus.unregister(this) + super.onDetach(view) + } + + override fun onAttach(view: View) { + super.onAttach(view) + eventBus.register(this) + } + + override fun onViewBound(view: View) { + super.onViewBound(view) + NextcloudTalkApplication.sharedApplication!! + .componentApplication + .inject(this) + + if (activity != null) { + activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + + if (actionBar != null) { + actionBar!!.hide() + } + + if (isAccountImport && !baseUrl!!.startsWith("http://") && !baseUrl!!.startsWith("https://") || !TextUtils + .isEmpty(originalProtocol) && !baseUrl!!.startsWith(originalProtocol!!)) { + determineBaseUrlProtocol(true) + } else { + checkEverything() + } + } + + private fun checkEverything() { + val credentials = ApiUtils.getCredentials(username, token) + cookieManager.cookieStore.removeAll() + + findServerTalkApp(credentials) + } + + private fun determineBaseUrlProtocol(checkForcedHttps: Boolean) { + cookieManager.cookieStore.removeAll() + + val queryUrl: String + + baseUrl = baseUrl!!.replace("http://", "").replace("https://", "") + + if (checkForcedHttps) { + queryUrl = "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus() + } else { + queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus() + } + + ncApi!!.getServerStatus(queryUrl) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .`as`>(AutoDispose.autoDisposable(scopeProvider)) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) {} + + override fun onNext(status: Status) { + if (checkForcedHttps) { + baseUrl = "https://" + baseUrl!! + } else { + baseUrl = "http://" + baseUrl!! + } + + if (isAccountImport) { + router.replaceTopController( + RouterTransaction.with(WebViewLoginController(baseUrl, + false, username, "")) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } else { + checkEverything() + } + } + + override fun onError(e: Throwable) { + if (checkForcedHttps) { + determineBaseUrlProtocol(false) + } else { + GlobalScope.launch { + abortVerification() + + } + } + } + + override fun onComplete() { + + } + }) + } + + private fun findServerTalkApp(credentials: String?) { + ncApi!!.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) + .subscribeOn(Schedulers.io()) + .`as`>(AutoDispose.autoDisposable(scopeProvider)) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) {} + + override fun onNext(roomsOverall: RoomsOverall) { + fetchProfile(credentials) + } + + override fun onError(e: Throwable) { + if (activity != null && resources != null) { + activity!!.runOnUiThread { + progressText!!.text = String.format(resources!!.getString( + R.string.nc_nextcloud_talk_app_not_installed), + resources!!.getString(R.string.nc_app_name)) + } + } + + ApplicationWideMessageHolder.getInstance().setMessageType( + ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK) + + GlobalScope.launch { + abortVerification() + } + } + + override fun onComplete() { + + } + }) + } + + private suspend fun storeProfile(displayName: String?, userId: String) { + var user = usersRepository.getUserWithUsernameAndServer(username!!, baseUrl!!) + if (user == null) { + user = UserNgEntity(null, userId, username!!, baseUrl!!, token, displayName) + internalAccountId = usersDao.saveUser(user) + } else { + user.displayName = displayName + usersRepository.updateUser(user) + internalAccountId = user.id!! + } + + if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) { + registerForPush() + } else { + activity!!.runOnUiThread { + progressText!!.text = progressText!!.text.toString() + "\n" + + resources!!.getString(R.string.nc_push_disabled) + } + fetchAndStoreCapabilities() + } + } + + private fun fetchProfile(credentials: String?) { + ncApi!!.getUserProfile(credentials, + ApiUtils.getUrlForUserProfile(baseUrl)) + .subscribeOn(Schedulers.io()) + .`as`>(AutoDispose.autoDisposable(scopeProvider)) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) {} + + override fun onNext(userProfileOverall: UserProfileOverall) { + var displayName: String? = null + if (!TextUtils.isEmpty(userProfileOverall.ocs.data.displayName)) { + displayName = userProfileOverall.ocs.data.displayName + } else if (!TextUtils.isEmpty(userProfileOverall.ocs.data.displayNameAlt)) { + displayName = userProfileOverall.ocs.data.displayNameAlt + } + + if (!TextUtils.isEmpty(displayName)) { + GlobalScope.launch { + storeProfile(displayName, userProfileOverall.ocs.data.userId) + } + } else { + if (activity != null) { + activity!!.runOnUiThread { + progressText!!.text = progressText!!.text.toString() + "\n" + + resources!!.getString(R.string.nc_display_name_not_fetched) + } + } + GlobalScope.launch { + abortVerification() + } + } + } + + override fun onError(e: Throwable) { + if (activity != null) { + activity!!.runOnUiThread { + progressText!!.text = progressText!!.text.toString() + "\n" + + resources!!.getString(R.string.nc_display_name_not_fetched) + } + } + GlobalScope.launch { + abortVerification() + } + } + + override fun onComplete() { + + } + }) + } + + private fun registerForPush() { + val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build() + WorkManager.getInstance().enqueue(pushRegistrationWork) + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + fun onMessageEvent(eventStatus: EventStatus) { + if (EventStatus.EventType.PUSH_REGISTRATION == eventStatus.eventType) { + if (internalAccountId == eventStatus.userId + && !eventStatus.allGood + && activity != null) { + activity!!.runOnUiThread { + progressText!!.text = progressText!!.text.toString() + "\n" + + resources!!.getString(R.string.nc_push_disabled) + } + } + fetchAndStoreCapabilities() + } else if (EventStatus.EventType.CAPABILITIES_FETCH == eventStatus.eventType) { + if (internalAccountId == eventStatus.userId && !eventStatus.allGood) { + if (activity != null) { + activity!!.runOnUiThread { + progressText!!.text = progressText!!.text.toString() + "\n" + + resources!!.getString(R.string.nc_capabilities_failed) + } + } + GlobalScope.launch { + abortVerification() + } + } else if (internalAccountId == eventStatus.userId && eventStatus.allGood) { + fetchAndStoreExternalSignalingSettings() + } + } else if (EventStatus.EventType.SIGNALING_SETTINGS == eventStatus.eventType) { + if (internalAccountId == eventStatus.userId && !eventStatus.allGood) { + if (activity != null) { + activity!!.runOnUiThread { + progressText!!.text = progressText!!.text.toString() + "\n" + + resources!!.getString(R.string.nc_external_server_failed) + } + } + } + + GlobalScope.launch { + proceedWithLogin() + } + } + } + + private fun fetchAndStoreCapabilities() { + val userData = Data.Builder() + .putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalAccountId) + .build() + + val pushNotificationWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java) + .setInputData(userData) + .build() + WorkManager.getInstance().enqueue(pushNotificationWork) + } + + private fun fetchAndStoreExternalSignalingSettings() { + val userData = Data.Builder() + .putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalAccountId) + .build() + + val signalingSettings = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java) + .setInputData(userData) + .build() + WorkManager.getInstance().enqueue(signalingSettings) + } + + private suspend fun proceedWithLogin() { + cookieManager.cookieStore.removeAll() + usersRepository.setUserAsActiveWithId(internalAccountId) + + if (activity != null) { + if (usersRepository.getUsers().count() == 1) { + activity!!.runOnUiThread { + router.setRoot(RouterTransaction.with(ConversationsListView()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } + } else { + if (isAccountImport) { + ApplicationWideMessageHolder.getInstance().messageType = ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED + } + activity!!.runOnUiThread { + router.popToRoot() + } + } + } + } + + override fun onDestroyView(view: View) { + super.onDestroyView(view) + if (activity != null) { + activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR + } + } + + private suspend fun abortVerification() { + + if (!isAccountImport) { + if (internalAccountId != -1L) { + usersRepository.deleteUserWithId(internalAccountId) + activity!!.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, 7500) } + + } else { + if (activity != null) { + activity!!.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, 7500) } + } + } + } else { + ApplicationWideMessageHolder.getInstance().messageType = + ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT + if (activity != null) { + activity!!.runOnUiThread { + Handler().postDelayed({ + if (router.hasRootController()) { + if (activity != null) { + router.popToRoot() + } + } else { + if (usersRepository.getUsers().count() > 0) { + router.setRoot(RouterTransaction.with(ConversationsListView()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } else { + router.setRoot(RouterTransaction.with(ServerSelectionController()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } + } + }, 7500) + } + } + } + } + + companion object { + + const val TAG = "AccountVerificationController" + } +} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 8da5c7bf5..6f8930d45 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -1017,11 +1017,11 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter private fun setupWebsocket() { if (conversationUser != null) { if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId( - conversationUser.id + conversationUser.id!! ) != null ) { magicWebSocketInstance = - WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id) + WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) } else { magicWebSocketInstance = null } @@ -1489,7 +1489,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter null && roomOverall.ocs.data.token != null ) { ConductorRemapping.remapChatController( - router, conversationUser.id, + router, conversationUser.id!!, roomOverall.ocs.data.token!!, bundle, false ) } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt index 210205cd9..97f72e794 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt @@ -332,7 +332,7 @@ class ContactsController : BaseController, ConductorRemapping.remapChatController( router, - currentUser!!.id, + currentUser!!.id!!, roomOverall.ocs.data.token!!, bundle, true ) } @@ -389,7 +389,7 @@ class ContactsController : BaseController, val groupIdsArray = selectedGroupIds.toTypedArray() val data = Data.Builder() - data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, currentUser!!.id) + data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, currentUser!!.id!!) data.putString(BundleKeys.KEY_TOKEN, conversationToken) data.putStringArray(BundleKeys.KEY_SELECTED_USERS, userIdsArray) data.putStringArray(BundleKeys.KEY_SELECTED_GROUPS, groupIdsArray) @@ -893,7 +893,7 @@ class ContactsController : BaseController, ConductorRemapping.remapChatController( router, - currentUser!!.id, + currentUser!!.id!!, roomOverall.ocs.data.token!!, bundle, true ) } else { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index 238c1a90b..525001568 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -192,7 +192,7 @@ class ConversationInfoController(args: Bundle) : BaseController(), if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) { val data = Data.Builder() data.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken) - data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id) + data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!) return data.build() } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.kt b/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.kt index 564ebcf3c..f29724951 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.kt @@ -74,7 +74,7 @@ class AddParticipantsToConversation(context: Context, .subscribeOn(Schedulers.io()) .blockingSubscribe() } - eventBus.post(EventStatus(user.id, EventStatus.EventType.PARTICIPANTS_UPDATE, true)) + eventBus.post(EventStatus(user.id!!, EventStatus.EventType.PARTICIPANTS_UPDATE, true)) return Result.success() } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt index 2ee4d936f..19868b1e8 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt @@ -55,7 +55,7 @@ class CapabilitiesWorker(context: Context, workerParams: WorkerParameters) : Cor internalUserEntity.capabilities = capabilitiesOverall.ocs.data.capabilities runBlocking { val result = usersRepository.updateUser(internalUserEntity) - eventBus!!.post(EventStatus(internalUserEntity.id, + eventBus!!.post(EventStatus(internalUserEntity.id!!, EventStatus.EventType.CAPABILITIES_FETCH, result > 0)) } @@ -87,7 +87,7 @@ class CapabilitiesWorker(context: Context, workerParams: WorkerParameters) : Cor } override fun onError(e: Throwable) { - eventBus.post(EventStatus(internalUserEntity.id, + eventBus.post(EventStatus(internalUserEntity.id!!, EventStatus.EventType.CAPABILITIES_FETCH, false)) } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt index 6c87e6d71..cfacfcbee 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt @@ -59,7 +59,7 @@ class LeaveConversationWorker(context: Context, workerParams: WorkerParameters) if (operationUser != null) { val credentials = operationUser.getCredentials() ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build().create(NcApi::class.java) - val eventStatus = EventStatus(operationUser.id, + val eventStatus = EventStatus(operationUser.id!!, EventStatus.EventType.CONVERSATION_UPDATE, true) ncApi!!.removeSelfFromRoom(credentials, ApiUtils.getUrlForRemoveSelfFromRoom(operationUser.baseUrl, conversationToken)) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index cb981a516..ab60f816e 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -154,7 +154,7 @@ class NotificationWorker( var arbitraryStorageEntity: ArbitraryStorageEntity? arbitraryStorageEntity = arbitraryStorageUtils!!.getStorageSetting( - userEntity.id, + userEntity.id!!, "mute_calls", intent.extras!!.getString(KEY_ROOM_TOKEN) ) @@ -164,7 +164,7 @@ class NotificationWorker( } arbitraryStorageEntity = arbitraryStorageUtils!!.getStorageSetting( - userEntity.id, + userEntity.id!!, "important_conversation", intent.extras!!.getString(KEY_ROOM_TOKEN) ) @@ -366,7 +366,7 @@ class NotificationWorker( val notificationInfo = Bundle() notificationInfo.putLong( KEY_INTERNAL_USER_ID, - signatureVerification!!.userEntity.id + signatureVerification!!.userEntity.id!! ) // could be an ID or a TOKEN diff --git a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt index 75030ec39..ae8c3b4c4 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt @@ -20,33 +20,29 @@ package com.nextcloud.talk.jobs import android.content.Context -import android.util.Log -import androidx.work.* +import androidx.work.CoroutineWorker +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.WorkerParameters import autodagger.AutoInjector -import com.bluelinelabs.logansquare.LoganSquare import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.jobs.WebsocketConnectionsWorker import com.nextcloud.talk.models.ExternalSignalingServer -import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.newarch.local.models.getCredentials import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID -import com.nextcloud.talk.utils.database.user.UserUtils import io.reactivex.Observer import io.reactivex.disposables.Disposable -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.greenrobot.eventbus.EventBus import org.koin.core.KoinComponent import org.koin.core.inject -import java.io.IOException import java.util.* import javax.inject.Inject @@ -63,7 +59,7 @@ class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters) ?.inject(this) val data = inputData val internalUserId = data.getLong(KEY_INTERNAL_USER_ID, -1) - var userEntityList: MutableList = ArrayList() + var userEntityList: MutableList = ArrayList() var userEntity: UserNgEntity? if (internalUserId == -1L || usersRepository.getUserWithId(internalUserId) == null) { userEntityList = usersRepository.getUsers().toMutableList() @@ -84,18 +80,18 @@ class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters) externalSignalingServer = ExternalSignalingServer() externalSignalingServer.externalSignalingServer = signalingSettingsOverall.ocs.settings.externalSignalingServer externalSignalingServer.externalSignalingTicket = signalingSettingsOverall.ocs.settings.externalSignalingTicket - val user = usersRepository.getUserWithId(userEntity.id) + val user = usersRepository.getUserWithId(userEntity.id!!) user.externalSignaling = externalSignalingServer runBlocking { val result = usersRepository.updateUser(user) - eventBus.post(EventStatus(user.id, + eventBus.post(EventStatus(user.id!!, EventStatus.EventType.SIGNALING_SETTINGS, result > 0)) } } override fun onError(e: Throwable) { - eventBus.post(EventStatus(finalUserEntity!!.id, + eventBus.post(EventStatus(finalUserEntity!!.id!!, EventStatus.EventType.SIGNALING_SETTINGS, false)) } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt index affb144ea..783a772c0 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt @@ -53,4 +53,12 @@ class UsersRepositoryImpl(val usersDao: UsersDao): UsersRepository { return usersDao.updateUser(user) } + override suspend fun setUserAsActiveWithId(id: Long) { + usersDao.setUserAsActiveWithId(id) + } + + override suspend fun deleteUserWithId(id: Long) { + usersDao.deleteUserWithId(id) + } + } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt index 8ed08502b..ba2ec08e4 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/UsersRepository.kt @@ -30,4 +30,6 @@ interface UsersRepository { fun getUserWithId(id: Long): UserNgEntity suspend fun getUserWithUsernameAndServer(username: String, server: String): UserNgEntity? suspend fun updateUser(user: UserNgEntity): Int + suspend fun setUserAsActiveWithId(id: Long) + suspend fun deleteUserWithId(id: Long) } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt index a434043a6..41178fe34 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt @@ -37,7 +37,6 @@ class ConversationListViewModelFactory constructor( private val setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase, private val leaveConversationUseCase: LeaveConversationUseCase, private val deleteConversationUseCase: DeleteConversationUseCase, - private val userUtils: UserUtils, private val conversationsRepository: ConversationsRepository, private val usersRepository: UsersRepository ) : ViewModelProvider.Factory { @@ -46,7 +45,7 @@ class ConversationListViewModelFactory constructor( return ConversationsListViewModel( application, conversationsUseCase, setConversationFavoriteValueUseCase, leaveConversationUseCase, deleteConversationUseCase, - userUtils, conversationsRepository, usersRepository + conversationsRepository, usersRepository ) as T } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt index 85011ab6e..2c61caf06 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.text.InputType +import android.util.Log import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -117,14 +118,18 @@ class ConversationsListView : BaseView(), OnQueryTextListener, } settingsItem = menu.findItem(R.id.action_settings) + loadAvatar() + } + + private fun loadAvatar() { val iconSize = settingsItem?.icon?.intrinsicHeight?.toFloat() - ?.let { - DisplayUtils.convertDpToPixel( - it, - activity!! - ) - .toInt() - } + ?.let { + DisplayUtils.convertDpToPixel( + it, + activity!! + ) + .toInt() + } iconSize?.let { val target = object : Target { @@ -141,9 +146,9 @@ class ConversationsListView : BaseView(), OnQueryTextListener, viewModel.currentUserLiveData.value?.let { val avatarRequest = Images().getRequestForUrl( - imageLoader, context, ApiUtils.getUrlForAvatarWithNameAndPixels( - it.baseUrl, - it.userId, iconSize + imageLoader, context, ApiUtils.getUrlForAvatarWithNameAndPixels( + it.baseUrl, + it.userId, iconSize ), it, target, this, CircleCropTransformation() ) @@ -214,6 +219,10 @@ class ConversationsListView : BaseView(), OnQueryTextListener, viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java) viewModel.apply { + currentUserLiveData.observe(this@ConversationsListView, Observer { value -> + loadAvatar() + }) + viewState.observe(this@ConversationsListView, Observer { value -> when (value) { LOADING -> { @@ -363,8 +372,6 @@ class ConversationsListView : BaseView(), OnQueryTextListener, displayName } - - viewModel.loadConversations() } override fun onItemLongClick(position: Int) { @@ -443,7 +450,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener, bundle.putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId) bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation)) ConductorRemapping.remapChatController( - router, viewModel.currentUserLiveData.value!!.id, conversation.token!!, + router, viewModel.currentUserLiveData.value!!.id!!, conversation.token!!, bundle, false ) } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt index a8cb36456..50febdf30 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt @@ -54,7 +54,6 @@ class ConversationsListViewModel constructor( private val setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase, private val leaveConversationUseCase: LeaveConversationUseCase, private val deleteConversationUseCase: DeleteConversationUseCase, - private val userUtils: UserUtils, private val conversationsRepository: ConversationsRepository, usersRepository: UsersRepository ) : BaseViewModel(application) { @@ -64,10 +63,7 @@ class ConversationsListViewModel constructor( val searchQuery = MutableLiveData() val currentUserLiveData: LiveData = usersRepository.getActiveUserLiveData() val conversationsLiveData = Transformations.switchMap(currentUserLiveData) { - if (LOADING != viewState.value) { - viewState.value = LOADING - } - conversationsRepository.getConversationsForUser(it.id) + conversationsRepository.getConversationsForUser(it.id!!) } fun leaveConversation(conversation: Conversation) { @@ -82,7 +78,7 @@ class ConversationsListViewModel constructor( object : UseCaseResponse { override suspend fun onSuccess(result: GenericOverall) { conversationsRepository.deleteConversation( - currentUserLiveData.value!!.id, conversation + currentUserLiveData.value!!.id!!, conversation .conversationId!! ) } @@ -111,7 +107,7 @@ class ConversationsListViewModel constructor( object : UseCaseResponse { override suspend fun onSuccess(result: GenericOverall) { conversationsRepository.deleteConversation( - currentUserLiveData.value!!.id, conversation + currentUserLiveData.value!!.id!!, conversation .conversationId!! ) } @@ -142,7 +138,7 @@ class ConversationsListViewModel constructor( object : UseCaseResponse { override suspend fun onSuccess(result: GenericOverall) { conversationsRepository.setFavoriteValueForConversation( - currentUserLiveData.value!!.id, + currentUserLiveData.value!!.id!!, conversation.conversationId!!, favorite ) } @@ -167,7 +163,7 @@ class ConversationsListViewModel constructor( } conversationsRepository.saveConversationsForUser( - internalUserId, + internalUserId!!, mutableList) messageData = "" } @@ -258,7 +254,7 @@ class ConversationsListViewModel constructor( value: Boolean ) { conversationsRepository.setChangingValueForConversation( - currentUserLiveData.value!!.id, conversation + currentUserLiveData.value!!.id!!, conversation .conversationId!!, value ) } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt index cc8539253..71ed6c885 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt @@ -42,7 +42,7 @@ val ConversationsListModule = module { //viewModel { ConversationsListViewModel(get(), get()) } factory { createConversationListViewModelFactory( - androidApplication(), get(), get(), get(), get + androidApplication(), get(), get(), get (), get(), get(), get() ) } @@ -83,13 +83,12 @@ fun createConversationListViewModelFactory( setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase, leaveConversationUseCase: LeaveConversationUseCase, deleteConversationUseCase: DeleteConversationUseCase, - userUtils: UserUtils, conversationsRepository: ConversationsRepository, usersRepository: UsersRepository ): ConversationListViewModelFactory { return ConversationListViewModelFactory( application, getConversationsUseCase, setConversationFavoriteValueUseCase, leaveConversationUseCase, deleteConversationUseCase, - userUtils, conversationsRepository, usersRepository + conversationsRepository, usersRepository ) } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/converters/UserStatusConverter.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/converters/UserStatusConverter.kt index a5da5aab8..dcafd1f69 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/converters/UserStatusConverter.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/converters/UserStatusConverter.kt @@ -28,16 +28,20 @@ import com.nextcloud.talk.newarch.local.models.other.UserStatus.PENDING_DELETE class UserStatusConverter { @TypeConverter - fun fromUserStatusToInt(userStatus: UserStatus): Int { + fun fromUserStatusToInt(userStatus: UserStatus?): Int { + if (userStatus == null) { + return DORMANT.ordinal + } + return userStatus.ordinal } @TypeConverter - fun fromIntToUserStatus(value: Int): UserStatus { - when (value) { - 0 -> return DORMANT - 1 -> return ACTIVE - else -> return PENDING_DELETE + fun fromIntToUserStatus(value: Int): UserStatus? { + return when (value) { + 0 -> DORMANT + 1 -> ACTIVE + else -> PENDING_DELETE } } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt index e0b127aa2..cb2c16527 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/UsersDao.kt @@ -21,47 +21,68 @@ package com.nextcloud.talk.newarch.local.dao import androidx.lifecycle.LiveData -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Update +import androidx.room.* import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.newarch.local.models.ConversationEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.newarch.local.models.other.UserStatus @Dao abstract class UsersDao { - // get active user - @Query("SELECT * FROM users where status = 1") - abstract fun getActiveUser(): UserNgEntity + // get active user + @Query("SELECT * FROM users where status = 1") + abstract fun getActiveUser(): UserNgEntity - @Query("SELECT * FROM users WHERE status = 1") - abstract fun getActiveUserLiveData(): LiveData + @Query("SELECT * FROM users WHERE status = 1") + abstract fun getActiveUserLiveData(): LiveData - @Query("DELETE FROM users WHERE id = :userId") - abstract fun deleteUserForId(userId: Long) + @Query("DELETE FROM users WHERE id = :id") + abstract suspend fun deleteUserWithId(id: Long) - @Update - abstract suspend fun updateUser(user: UserNgEntity): Int + @Update + abstract suspend fun updateUser(user: UserNgEntity): Int - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun saveUser(user: UserNgEntity) + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract fun saveUser(user: UserNgEntity): Long - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract suspend fun saveUsers(vararg users: UserNgEntity) + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract suspend fun saveUsers(vararg users: UserNgEntity) - // get all users not scheduled for deletion - @Query("SELECT * FROM users where status != 2") - abstract fun getUsers(): List + // get all users not scheduled for deletion + @Query("SELECT * FROM users where status != 2") + abstract fun getUsers(): List - @Query("SELECT * FROM users where id = :id") - abstract fun getUserWithId(id: Long): UserNgEntity + @Query("SELECT * FROM users where id = :id") + abstract fun getUserWithId(id: Long): UserNgEntity - @Query("SELECT * FROM users where status = 2") - abstract fun getUsersScheduledForDeletion(): List + @Query("SELECT * FROM users where status = 2") + abstract fun getUsersScheduledForDeletion(): List - @Query("SELECT * FROM users WHERE username = :username AND base_url = :server") - abstract suspend fun getUserWithUsernameAndServer(username: String, server: String): UserNgEntity? + @Query("SELECT * FROM users WHERE username = :username AND base_url = :server") + abstract suspend fun getUserWithUsernameAndServer(username: String, server: String): UserNgEntity? + @Transaction + open suspend fun setUserAsActiveWithId(id: Long) { + val users = getUsers() + for (user in users) { + if (user.id != id && UserStatus.ACTIVE == user.status) { + user.status = UserStatus.DORMANT + updateUser(user) + } else if (user.id == id && UserStatus.ACTIVE != user.status) { + user.status = UserStatus.ACTIVE + updateUser(user) + } + } + } + + @Transaction + open suspend fun setAnyUserAsActive(): Boolean { + val users = getUsers() + for (user in users) { + user.status = UserStatus.ACTIVE + return true + } + + return false + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt index 5658c901f..08aa554eb 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/models/UserNgEntity.kt @@ -35,7 +35,7 @@ import kotlinx.android.parcel.RawValue @Parcelize @Entity(tableName = "users") data class UserNgEntity( - @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long, + @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long?, @ColumnInfo(name = "user_id") var userId: String, @ColumnInfo(name = "username") var username: String, @ColumnInfo(name = "base_url") var baseUrl: String, diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt index 8e4e82129..0255c3e41 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt @@ -297,7 +297,7 @@ class PushUtils(val usersRepository: UsersRepository) { override fun onError(e: Throwable) { eventBus!!.post( EventStatus( - userEntity.id, + userEntity.id!!, PUSH_REGISTRATION, false ) ) @@ -313,7 +313,7 @@ class PushUtils(val usersRepository: UsersRepository) { override fun onError(e: Throwable) { eventBus!!.post( EventStatus( - userEntity.id, + userEntity.id!!, PUSH_REGISTRATION, false ) @@ -327,7 +327,7 @@ class PushUtils(val usersRepository: UsersRepository) { override fun onError(e: Throwable) { eventBus!!.post( EventStatus( - userEntity.id, + userEntity.id!!, PUSH_REGISTRATION, false ) diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt index 50db69a59..e2070cc85 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt @@ -275,6 +275,6 @@ class DatabaseStorageModule( sharedApplication!! .componentApplication .inject(this) - accountIdentifier = conversationUser.id + accountIdentifier = conversationUser.id!! } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt index 3108db359..9390c316c 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt @@ -253,7 +253,7 @@ class MagicWebSocketInstance internal constructor( HashMap() refreshChatHashMap[KEY_ROOM_TOKEN] = messageHashMap["roomid"] as String? refreshChatHashMap[KEY_INTERNAL_USER_ID] = - java.lang.Long.toString(conversationUser.id) + java.lang.Long.toString(conversationUser.id!!) eventBus.post( WebSocketCommunicationEvent("refreshChat", refreshChatHashMap) ) diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.kt index 994292ad2..ebce21477 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.kt @@ -128,7 +128,7 @@ class WebSocketConnectionHelper: KoinComponent { deleteExternalSignalingInstanceForUserEntity(userId) } magicWebSocketInstance = MagicWebSocketInstance(userEntity, generatedURL, webSocketTicket!!) - magicWebSocketInstanceMap[userEntity.id] = magicWebSocketInstance + magicWebSocketInstanceMap[userEntity.id!!] = magicWebSocketInstance return magicWebSocketInstance } } diff --git a/gradle.properties b/gradle.properties index 7c7ec3f93..f80ddfc11 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,7 +29,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=2048m +org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m android.useAndroidX=true android.enableJetifier=true