From 591d6dc3e87f72dc9c537abd3a3fb637d472ce58 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 3 Nov 2023 17:40:34 +0100 Subject: [PATCH 1/2] remove conductor - replace remaining controllers with activities - remove conductor lib - modify some code related to account management and conductor Signed-off-by: Marcel Hibbe --- CONTRIBUTING.md | 1 - app/build.gradle | 4 +- app/lint.xml | 1 - app/src/main/AndroidManifest.xml | 62 ++-- .../AccountVerificationActivity.kt} | 326 +++++++++--------- .../ServerSelectionActivity.kt} | 253 +++++++------- .../SwitchAccountActivity.kt} | 98 +++--- .../WebViewLoginActivity.kt} | 293 ++++++++-------- .../ActionBarProvider.java | 2 +- .../nextcloud/talk/activities/BaseActivity.kt | 72 +++- .../nextcloud/talk/activities/CallActivity.kt | 2 +- .../nextcloud/talk/activities/MainActivity.kt | 84 +---- .../messages/IncomingPollMessageViewHolder.kt | 2 +- .../OutcomingPollMessageViewHolder.kt | 2 +- .../items/BasicListItemWithImage.kt | 2 +- .../items/BottomSheets.kt} | 2 +- .../items/ListIconDialogAdapter.kt | 2 +- .../CallNotificationActivity.kt | 2 +- .../com/nextcloud/talk/chat/ChatActivity.kt | 6 +- .../talk/contacts/ContactsActivity.kt | 17 +- .../RingtoneSelectionController.kt | 256 -------------- .../talk/controllers/base/BaseController.kt | 309 ----------------- .../util/ControllerViewBindingDelegate.kt | 52 --- .../ConversationInfoActivity.kt | 4 +- .../ConversationsListActivity.kt | 193 +++++++---- .../talk/jobs/AccountRemovalWorker.java | 15 - .../talk/location/GeocodingActivity.kt | 2 +- .../nextcloud/talk/profile/ProfileActivity.kt | 18 +- .../receivers/ShareRecordingToChatReceiver.kt | 2 - .../talk/settings/SettingsActivity.kt | 51 ++- .../ui/bottom/sheet/ProfileBottomSheet.kt | 4 +- .../dialog/ChooseAccountDialogFragment.java | 10 +- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 4 +- .../ApplicationWideMessageHolder.java | 2 +- ....xml => activity_account_verification.xml} | 0 app/src/main/res/layout/activity_chat.xml | 2 +- app/src/main/res/layout/activity_contacts.xml | 14 +- ...ions_rv.xml => activity_conversations.xml} | 0 app/src/main/res/layout/activity_main.xml | 9 - ...tion.xml => activity_server_selection.xml} | 0 ...ric_rv.xml => activity_switch_account.xml} | 31 +- ..._login.xml => activity_web_view_login.xml} | 0 app/src/main/res/layout/call_activity.xml | 4 +- .../res/layout/call_notification_activity.xml | 6 +- app/src/main/res/values-night/colors.xml | 2 +- app/src/main/res/values/colors.xml | 4 +- app/src/main/res/values/setup.xml | 2 +- app/src/main/res/values/strings.xml | 1 + .../nextcloud/talk/utils/BundleKeysTest.kt | 2 +- 49 files changed, 831 insertions(+), 1401 deletions(-) rename app/src/main/java/com/nextcloud/talk/{controllers/AccountVerificationController.kt => account/AccountVerificationActivity.kt} (67%) rename app/src/main/java/com/nextcloud/talk/{controllers/ServerSelectionController.kt => account/ServerSelectionActivity.kt} (65%) rename app/src/main/java/com/nextcloud/talk/{controllers/SwitchAccountController.kt => account/SwitchAccountActivity.kt} (74%) rename app/src/main/java/com/nextcloud/talk/{controllers/WebViewLoginController.kt => account/WebViewLoginActivity.kt} (64%) rename app/src/main/java/com/nextcloud/talk/{controllers/base/providers => activities}/ActionBarProvider.java (93%) rename app/src/main/java/com/nextcloud/talk/{controllers => }/bottomsheet/items/BasicListItemWithImage.kt (95%) rename app/src/main/java/com/nextcloud/talk/{controllers/bottomsheet/items/MagicBottomSheets.kt => bottomsheet/items/BottomSheets.kt} (97%) rename app/src/main/java/com/nextcloud/talk/{controllers => }/bottomsheet/items/ListIconDialogAdapter.kt (98%) delete mode 100644 app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt rename app/src/main/res/layout/{controller_account_verification.xml => activity_account_verification.xml} (100%) rename app/src/main/res/layout/{controller_conversations_rv.xml => activity_conversations.xml} (100%) rename app/src/main/res/layout/{controller_server_selection.xml => activity_server_selection.xml} (100%) rename app/src/main/res/layout/{controller_generic_rv.xml => activity_switch_account.xml} (57%) rename app/src/main/res/layout/{controller_web_view_login.xml => activity_web_view_login.xml} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 584d24df6b..3bf77a53dd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -289,7 +289,6 @@ We are using [Dagger 2](https://dagger.dev/) to inject dependencies into major A * `Activity` * `Fragment` - * `Controller` * `Service` * `BroadcastReceiver` * `ContentProvider` diff --git a/app/build.gradle b/app/build.gradle index 3df5f7fb5e..1167a85527 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,7 @@ * @author Tim Krüger * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2017-2019 Mario Danic - * Copyright (C) 2021 Marcel Hibbe + * Copyright (C) 2021-2023 Marcel Hibbe * Copyright (C) 2022 Tim Krüger * * This program is free software: you can redistribute it and/or modify @@ -210,8 +210,6 @@ dependencies { implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation "io.reactivex.rxjava2:rxjava:2.2.21" - implementation 'com.bluelinelabs:conductor:3.2.0' - implementation "com.squareup.okhttp3:okhttp:${okhttpVersion}" implementation "com.squareup.okhttp3:okhttp-urlconnection:${okhttpVersion}" implementation "com.squareup.okhttp3:logging-interceptor:${okhttpVersion}" diff --git a/app/lint.xml b/app/lint.xml index 9015fa6cb0..c571055aa4 100644 --- a/app/lint.xml +++ b/app/lint.xml @@ -32,6 +32,5 @@ - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 89f0d25efa..f0823c943c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ ~ @author Mario Danic ~ @author Marcel Hibbe ~ Copyright (C) 2017-2019 Mario Danic - ~ Copyright (C) 2021-2022 Marcel Hibbe + ~ Copyright (C) 2021-2023 Marcel Hibbe ~ ~ 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 @@ -131,6 +131,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - diff --git a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt similarity index 67% rename from app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt rename to app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt index d6ecd5a812..4230a6511c 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt +++ b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt @@ -3,6 +3,8 @@ * * @author Mario Danic * @author Andy Scherzinger + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe * Copyright (C) 2022 Andy Scherzinger * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) * @@ -19,7 +21,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.nextcloud.talk.controllers +package com.nextcloud.talk.account import android.annotation.SuppressLint import android.content.Intent @@ -28,25 +30,24 @@ import android.os.Bundle import android.os.Handler import android.text.TextUtils import android.util.Log -import android.view.View +import android.widget.Toast import androidx.work.Data import androidx.work.OneTimeWorkRequest +import androidx.work.WorkInfo import androidx.work.WorkManager import autodagger.AutoInjector -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.logansquare.LoganSquare import com.google.android.material.snackbar.Snackbar import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -import com.nextcloud.talk.controllers.base.BaseController -import com.nextcloud.talk.controllers.util.viewBinding import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.databinding.ControllerAccountVerificationBinding +import com.nextcloud.talk.databinding.ActivityAccountVerificationBinding import com.nextcloud.talk.events.EventStatus +import com.nextcloud.talk.jobs.AccountRemovalWorker import com.nextcloud.talk.jobs.CapabilitiesWorker import com.nextcloud.talk.jobs.PushRegistrationWorker import com.nextcloud.talk.jobs.SignalingSettingsWorker @@ -63,6 +64,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_BASE_URL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_ACCOUNT_IMPORT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ORIGINAL_PROTOCOL +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_PASSWORD import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USERNAME import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder @@ -71,20 +73,15 @@ import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers -import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.net.CookieManager import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class AccountVerificationController(args: Bundle? = null) : BaseController( - R.layout.controller_account_verification, - args -) { - private val binding: ControllerAccountVerificationBinding? by viewBinding( - ControllerAccountVerificationBinding::bind - ) +class AccountVerificationActivity : BaseActivity() { + + private lateinit var binding: ActivityAccountVerificationBinding @Inject lateinit var ncApi: NcApi @@ -95,9 +92,6 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( @Inject lateinit var cookieManager: CookieManager - @Inject - lateinit var eventBus: EventBus - private var internalAccountId: Long = -1 private val disposables: MutableList = ArrayList() private var baseUrl: String? = null @@ -106,43 +100,53 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( private var isAccountImport = false private var originalProtocol: String? = null - override fun onAttach(view: View) { - super.onAttach(view) - eventBus.register(this) - } + @SuppressLint("SourceLockedOrientationActivity") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + sharedApplication!!.componentApplication.inject(this) + binding = ActivityAccountVerificationBinding.inflate(layoutInflater) + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + setContentView(binding.root) + actionBar?.hide() + setupPrimaryColors() - override fun onDetach(view: View) { - super.onDetach(view) - eventBus.unregister(this) + handleIntent() } - override fun onViewBound(view: View) { - super.onViewBound(view) - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + private fun handleIntent() { + val extras = intent.extras!! + baseUrl = extras.getString(KEY_BASE_URL) + username = extras.getString(KEY_USERNAME) + token = extras.getString(KEY_TOKEN) + if (extras.containsKey(KEY_IS_ACCOUNT_IMPORT)) { + isAccountImport = true + } + if (extras.containsKey(KEY_ORIGINAL_PROTOCOL)) { + originalProtocol = extras.getString(KEY_ORIGINAL_PROTOCOL) + } + } - actionBar?.hide() + override fun onResume() { + super.onResume() if ( isAccountImport && !UriUtils.hasHttpProtocolPrefixed(baseUrl!!) || - isSameProtocol(baseUrl!!, originalProtocol!!) + isNotSameProtocol(baseUrl!!, originalProtocol) ) { determineBaseUrlProtocol(true) } else { - checkEverything() + findServerTalkApp() } } - private fun isSameProtocol(baseUrl: String, originalProtocol: String): Boolean { + private fun isNotSameProtocol(baseUrl: String, originalProtocol: String?): Boolean { + if (originalProtocol == null) { + return true + } return !TextUtils.isEmpty(originalProtocol) && !baseUrl.startsWith(originalProtocol) } - private fun checkEverything() { - val credentials = ApiUtils.getCredentials(username, token) - cookieManager.cookieStore.removeAll() - findServerTalkApp(credentials) - } - private fun determineBaseUrlProtocol(checkForcedHttps: Boolean) { cookieManager.cookieStore.removeAll() baseUrl = baseUrl!!.replace("http://", "").replace("https://", "") @@ -166,20 +170,16 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( "http://$baseUrl" } if (isAccountImport) { - router.replaceTopController( - RouterTransaction.with( - WebViewLoginController( - baseUrl, - false, - username, - "" - ) - ) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) + val bundle = Bundle() + bundle.putString(KEY_BASE_URL, baseUrl) + bundle.putString(KEY_USERNAME, username) + bundle.putString(KEY_PASSWORD, "") + + val intent = Intent(context, WebViewLoginActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) } else { - checkEverything() + findServerTalkApp() } } @@ -197,7 +197,10 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( }) } - private fun findServerTalkApp(credentials: String) { + private fun findServerTalkApp() { + val credentials = ApiUtils.getCredentials(username, token) + cookieManager.cookieStore.removeAll() + ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) .subscribeOn(Schedulers.io()) .subscribe(object : Observer { @@ -214,27 +217,24 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( if (hasTalk) { fetchProfile(credentials, capabilitiesOverall) } else { - if (activity != null && resources != null) { - activity!!.runOnUiThread { - binding?.progressText?.setText( - String.format( - resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed), - resources!!.getString(R.string.nc_app_product_name) - ) + if (resources != null) { + runOnUiThread { + binding.progressText.text = String.format( + resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed), + resources!!.getString(R.string.nc_app_product_name) ) } } - ApplicationWideMessageHolder.getInstance().setMessageType( + ApplicationWideMessageHolder.getInstance().messageType = ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK - ) abortVerification() } } override fun onError(e: Throwable) { - if (activity != null && resources != null) { - activity!!.runOnUiThread { - binding?.progressText?.text = String.format( + if (resources != null) { + runOnUiThread { + binding.progressText.text = String.format( resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed), resources!!.getString(R.string.nc_app_product_name) ) @@ -263,7 +263,7 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( displayName = displayName, pushConfigurationState = null, capabilities = LoganSquare.serialize(capabilities), - certificateAlias = appPreferences!!.temporaryClientCertAlias, + certificateAlias = appPreferences.temporaryClientCertAlias, externalSignalingServer = null ) ) @@ -279,9 +279,9 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) { registerForPush() } else { - activity!!.runOnUiThread { - binding?.progressText?.text = - """ ${binding?.progressText?.text} + runOnUiThread { + binding.progressText.text = + """ ${binding.progressText.text} ${resources!!.getString(R.string.nc_push_disabled)} """.trimIndent() } @@ -291,7 +291,7 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( @SuppressLint("SetTextI18n") override fun onError(e: Throwable) { - binding?.progressText?.text = """ ${binding?.progressText?.text}""".trimIndent() + + binding.progressText.text = """ ${binding.progressText.text}""".trimIndent() + resources!!.getString(R.string.nc_display_name_not_stored) abortVerification() } @@ -328,14 +328,12 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( capabilities.ocs!!.data!!.capabilities!! ) } else { - if (activity != null) { - activity!!.runOnUiThread { - binding?.progressText?.text = - """ - ${binding?.progressText?.text} - ${resources!!.getString(R.string.nc_display_name_not_fetched)} - """.trimIndent() - } + runOnUiThread { + binding.progressText.text = + """ + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_display_name_not_fetched)} + """.trimIndent() } abortVerification() } @@ -343,14 +341,12 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( @SuppressLint("SetTextI18n") override fun onError(e: Throwable) { - if (activity != null) { - activity!!.runOnUiThread { - binding?.progressText?.text = - """ - ${binding?.progressText?.text} - ${resources!!.getString(R.string.nc_display_name_not_fetched)} - """.trimIndent() - } + runOnUiThread { + binding.progressText.text = + """ + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_display_name_not_fetched)} + """.trimIndent() } abortVerification() } @@ -364,7 +360,7 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( private fun registerForPush() { val data = Data.Builder() - .putString(PushRegistrationWorker.ORIGIN, "AccountVerificationController#registerForPush") + .putString(PushRegistrationWorker.ORIGIN, "AccountVerificationActivity#registerForPush") .build() val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java) @@ -377,11 +373,11 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( @Subscribe(threadMode = ThreadMode.BACKGROUND) fun onMessageEvent(eventStatus: EventStatus) { if (eventStatus.eventType == EventStatus.EventType.PUSH_REGISTRATION) { - if (internalAccountId == eventStatus.userId && !eventStatus.isAllGood && activity != null) { - activity!!.runOnUiThread { - binding?.progressText?.text = + if (internalAccountId == eventStatus.userId && !eventStatus.isAllGood) { + runOnUiThread { + binding.progressText.text = """ - ${binding?.progressText?.text} + ${binding.progressText.text} ${resources!!.getString(R.string.nc_push_disabled)} """.trimIndent() } @@ -389,14 +385,12 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( fetchAndStoreCapabilities() } else if (eventStatus.eventType == EventStatus.EventType.CAPABILITIES_FETCH) { if (internalAccountId == eventStatus.userId && !eventStatus.isAllGood) { - if (activity != null) { - activity!!.runOnUiThread { - binding?.progressText?.text = - """ - ${binding?.progressText?.text} - ${resources!!.getString(R.string.nc_capabilities_failed)} - """.trimIndent() - } + runOnUiThread { + binding.progressText.text = + """ + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_capabilities_failed)} + """.trimIndent() } abortVerification() } else if (internalAccountId == eventStatus.userId && eventStatus.isAllGood) { @@ -404,14 +398,12 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( } } else if (eventStatus.eventType == EventStatus.EventType.SIGNALING_SETTINGS) { if (internalAccountId == eventStatus.userId && !eventStatus.isAllGood) { - if (activity != null) { - activity!!.runOnUiThread { - binding?.progressText?.text = - """ - ${binding?.progressText?.text} - ${resources!!.getString(R.string.nc_external_server_failed)} - """.trimIndent() - } + runOnUiThread { + binding.progressText.text = + """ + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_external_server_failed)} + """.trimIndent() } } proceedWithLogin() @@ -457,24 +449,22 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( Log.d(TAG, "userToSetAsActive: " + userToSetAsActive.username) if (userManager.setUserAsActive(userToSetAsActive).blockingGet()) { - if (activity != null) { - activity!!.runOnUiThread { - if (userManager.users.blockingGet().size == 1) { - val intent = Intent(context, ConversationsListActivity::class.java) - startActivity(intent) - } else { - if (isAccountImport) { - ApplicationWideMessageHolder.getInstance().messageType = - ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED - } - val intent = Intent(context, ConversationsListActivity::class.java) - startActivity(intent) + runOnUiThread { + if (userManager.users.blockingGet().size == 1) { + val intent = Intent(context, ConversationsListActivity::class.java) + startActivity(intent) + } else { + if (isAccountImport) { + ApplicationWideMessageHolder.getInstance().messageType = + ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED } + val intent = Intent(context, ConversationsListActivity::class.java) + startActivity(intent) } } } else { Log.e(TAG, "failed to set active user") - Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() } } } @@ -487,70 +477,70 @@ class AccountVerificationController(args: Bundle? = null) : BaseController( } } - override fun onDestroyView(view: View) { - super.onDestroyView(view) - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR - } - public override fun onDestroy() { dispose() super.onDestroy() } private fun abortVerification() { - if (!isAccountImport) { + if (isAccountImport) { + ApplicationWideMessageHolder.getInstance().messageType = ApplicationWideMessageHolder.MessageType + .FAILED_TO_IMPORT_ACCOUNT + runOnUiThread { + Handler().postDelayed({ + val intent = Intent(this, ServerSelectionActivity::class.java) + startActivity(intent) + }, DELAY_IN_MILLIS) + } + } else { if (internalAccountId != -1L) { - val count = userManager.deleteUser(internalAccountId) - if (count > 0) { - activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) } + runOnUiThread { + deleteUserAndStartServerSelection(internalAccountId) } } else { - activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) } + runOnUiThread { + Handler().postDelayed({ + val intent = Intent(this, ServerSelectionActivity::class.java) + startActivity(intent) + }, DELAY_IN_MILLIS) + } } - } else { - ApplicationWideMessageHolder.getInstance().setMessageType( - ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT - ) - activity?.runOnUiThread { - Handler().postDelayed({ - if (router.hasRootController()) { - if (activity != null) { - router.popToRoot() - } - } else { - if (userManager.users.blockingGet().isNotEmpty()) { - val intent = Intent(context, ConversationsListActivity::class.java) - startActivity(intent) - } else { - router.setRoot( - RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) - } + } + } + + @SuppressLint("CheckResult") + private fun deleteUserAndStartServerSelection(userId: Long) { + userManager.scheduleUserForDeletionWithId(userId).blockingGet() + val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() + WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork) + + WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id) + .observeForever { workInfo: WorkInfo -> + + when (workInfo.state) { + WorkInfo.State.SUCCEEDED -> { + val intent = Intent(this, ServerSelectionActivity::class.java) + startActivity(intent) } - }, DELAY_IN_MILLIS) + + WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> { + Toast.makeText( + context, + context.resources.getString(R.string.nc_common_error_sorry), + Toast.LENGTH_LONG + ).show() + Log.e(TAG, "something went wrong when deleting user with id $userId") + val intent = Intent(this, ServerSelectionActivity::class.java) + startActivity(intent) + } + + else -> {} + } } - } } companion object { - const val TAG = "AccountVerification" + private val TAG = AccountVerificationActivity::class.java.simpleName const val DELAY_IN_MILLIS: Long = 7500 } - - init { - sharedApplication!!.componentApplication.inject(this) - if (args != null) { - baseUrl = args.getString(KEY_BASE_URL) - username = args.getString(KEY_USERNAME) - token = args.getString(KEY_TOKEN) - if (args.containsKey(KEY_IS_ACCOUNT_IMPORT)) { - isAccountImport = true - } - if (args.containsKey(KEY_ORIGINAL_PROTOCOL)) { - originalProtocol = args.getString(KEY_ORIGINAL_PROTOCOL) - } - } - } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt b/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt similarity index 65% rename from app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt rename to app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt index a05899b52a..fb7d2de7a8 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt +++ b/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt @@ -3,6 +3,8 @@ * * @author Andy Scherzinger * @author Mario Danic + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2017 Mario Danic * @@ -19,7 +21,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.nextcloud.talk.controllers +package com.nextcloud.talk.account import android.accounts.Account import android.annotation.SuppressLint @@ -34,24 +36,21 @@ import android.view.KeyEvent import android.view.View import android.view.inputmethod.EditorInfo import android.widget.TextView -import androidx.core.content.res.ResourcesCompat +import androidx.activity.OnBackPressedCallback import autodagger.AutoInjector -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -import com.nextcloud.talk.controllers.base.BaseController -import com.nextcloud.talk.controllers.util.viewBinding -import com.nextcloud.talk.databinding.ControllerServerSelectionBinding +import com.nextcloud.talk.databinding.ActivityServerSelectionBinding import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall import com.nextcloud.talk.models.json.generic.Status import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.AccountUtils import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.UriUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ADDITIONAL_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_ACCOUNT_IMPORT import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder @@ -61,12 +60,12 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import java.security.cert.CertificateException import javax.inject.Inject +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication @AutoInjector(NextcloudTalkApplication::class) -class ServerSelectionController : - BaseController(R.layout.controller_server_selection) { +class ServerSelectionActivity : BaseActivity() { - private val binding: ControllerServerSelectionBinding? by viewBinding(ControllerServerSelectionBinding::bind) + private lateinit var binding: ActivityServerSelectionBinding @Inject lateinit var ncApi: NcApi @@ -76,44 +75,40 @@ class ServerSelectionController : private var statusQueryDisposable: Disposable? = null - fun onCertClick() { - if (activity != null) { - KeyChain.choosePrivateKeyAlias( - activity!!, - { alias: String? -> - if (alias != null) { - appPreferences!!.temporaryClientCertAlias = alias - } else { - appPreferences!!.removeTemporaryClientCertAlias() - } - setCertTextView() - }, - arrayOf("RSA", "EC"), - null, - null, - -1, - null - ) + private val onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (intent.hasExtra(ADD_ADDITIONAL_ACCOUNT) && intent.getBooleanExtra(ADD_ADDITIONAL_ACCOUNT, false)) { + finish() + } else { + finishAffinity() + } } } - override fun onViewBound(view: View) { - super.onViewBound(view) + @SuppressLint("SourceLockedOrientationActivity") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) sharedApplication!!.componentApplication.inject(this) - if (activity != null) { - activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } - + binding = ActivityServerSelectionBinding.inflate(layoutInflater) + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + setContentView(binding.root) actionBar?.hide() + setupPrimaryColors() + + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + } - binding?.hostUrlInputHelperText?.text = String.format( + override fun onResume() { + super.onResume() + + binding.hostUrlInputHelperText.text = String.format( resources!!.getString(R.string.nc_server_helper_text), resources!!.getString(R.string.nc_server_product_name) ) - binding?.serverEntryTextInputLayout?.setEndIconOnClickListener { checkServerAndProceed() } + binding.serverEntryTextInputLayout.setEndIconOnClickListener { checkServerAndProceed() } if (resources!!.getBoolean(R.bool.hide_auth_cert)) { - binding?.certTextView?.visibility = View.GONE + binding.certTextView.visibility = View.GONE } val loggedInUsers = userManager.users.blockingGet() @@ -124,21 +119,54 @@ class ServerSelectionController : } else if (isAbleToShowProviderLink() && loggedInUsers.isEmpty()) { showVisitProvidersInfo() } else { - binding?.importOrChooseProviderText?.visibility = View.INVISIBLE + binding.importOrChooseProviderText.visibility = View.INVISIBLE } - binding?.serverEntryTextInputEditText?.requestFocus() + binding.serverEntryTextInputEditText.requestFocus() if (!TextUtils.isEmpty(resources!!.getString(R.string.weblogin_url))) { - binding?.serverEntryTextInputEditText?.setText(resources!!.getString(R.string.weblogin_url)) + binding.serverEntryTextInputEditText.setText(resources!!.getString(R.string.weblogin_url)) checkServerAndProceed() } - binding?.serverEntryTextInputEditText?.setOnEditorActionListener { _: TextView?, i: Int, _: KeyEvent? -> + binding.serverEntryTextInputEditText.setOnEditorActionListener { _: TextView?, i: Int, _: KeyEvent? -> if (i == EditorInfo.IME_ACTION_DONE) { checkServerAndProceed() } false } - binding?.certTextView?.setOnClickListener { onCertClick() } + binding.certTextView.setOnClickListener { onCertClick() } + + if (ApplicationWideMessageHolder.getInstance().messageType != null) { + if (ApplicationWideMessageHolder.getInstance().messageType + == ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK + ) { + setErrorText(resources!!.getString(R.string.nc_settings_no_talk_installed)) + } else if (ApplicationWideMessageHolder.getInstance().messageType + == ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT + ) { + setErrorText(resources!!.getString(R.string.nc_server_failed_to_import_account)) + } + ApplicationWideMessageHolder.getInstance().messageType = null + } + setCertTextView() + } + + fun onCertClick() { + KeyChain.choosePrivateKeyAlias( + this, + { alias: String? -> + if (alias != null) { + appPreferences.temporaryClientCertAlias = alias + } else { + appPreferences.removeTemporaryClientCertAlias() + } + setCertTextView() + }, + arrayOf("RSA", "EC"), + null, + null, + -1, + null + ) } private fun isAbleToShowProviderLink(): Boolean { @@ -152,41 +180,37 @@ class ServerSelectionController : ) ) { if (availableAccounts.size > 1) { - binding?.importOrChooseProviderText?.text = String.format( + binding.importOrChooseProviderText.text = String.format( resources!!.getString(R.string.nc_server_import_accounts), AccountUtils.getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from)) ) } else { - binding?.importOrChooseProviderText?.text = String.format( + binding.importOrChooseProviderText.text = String.format( resources!!.getString(R.string.nc_server_import_account), AccountUtils.getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from)) ) } } else { if (availableAccounts.size > 1) { - binding?.importOrChooseProviderText?.text = + binding.importOrChooseProviderText.text = resources!!.getString(R.string.nc_server_import_accounts_plain) } else { - binding?.importOrChooseProviderText?.text = + binding.importOrChooseProviderText.text = resources!!.getString(R.string.nc_server_import_account_plain) } } - binding?.importOrChooseProviderText?.setOnClickListener { + binding.importOrChooseProviderText.setOnClickListener { val bundle = Bundle() bundle.putBoolean(KEY_IS_ACCOUNT_IMPORT, true) - router.pushController( - RouterTransaction.with( - SwitchAccountController(bundle) - ) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) + val intent = Intent(context, SwitchAccountActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) } } private fun showVisitProvidersInfo() { - binding?.importOrChooseProviderText?.setText(R.string.nc_get_from_provider) - binding?.importOrChooseProviderText?.setOnClickListener { + binding.importOrChooseProviderText.setText(R.string.nc_get_from_provider) + binding.importOrChooseProviderText.setOnClickListener { val browserIntent = Intent( Intent.ACTION_VIEW, Uri.parse( @@ -206,11 +230,11 @@ class ServerSelectionController : @Suppress("Detekt.TooGenericExceptionCaught") private fun checkServerAndProceed() { dispose() - var url: String = binding?.serverEntryTextInputEditText?.text.toString().trim { it <= ' ' } + var url: String = binding.serverEntryTextInputEditText.text.toString().trim { it <= ' ' } showserverEntryProgressBar() - if (binding?.importOrChooseProviderText?.visibility != View.INVISIBLE) { - binding?.importOrChooseProviderText?.visibility = View.INVISIBLE - binding?.certTextView?.visibility = View.INVISIBLE + if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) { + binding.importOrChooseProviderText.visibility = View.INVISIBLE + binding.certTextView.visibility = View.INVISIBLE } if (url.endsWith("/")) { url = url.substring(0, url.length - 1) @@ -278,17 +302,17 @@ class ServerSelectionController : hideserverEntryProgressBar() } - if (binding?.importOrChooseProviderText?.visibility != View.INVISIBLE) { - binding?.importOrChooseProviderText?.visibility = View.VISIBLE - binding?.certTextView?.visibility = View.VISIBLE + if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) { + binding.importOrChooseProviderText.visibility = View.VISIBLE + binding.certTextView.visibility = View.VISIBLE } dispose() } }) { hideserverEntryProgressBar() - if (binding?.importOrChooseProviderText?.visibility != View.INVISIBLE) { - binding?.importOrChooseProviderText?.visibility = View.VISIBLE - binding?.certTextView?.visibility = View.VISIBLE + if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) { + binding.importOrChooseProviderText.visibility = View.VISIBLE + binding.certTextView.visibility = View.VISIBLE } dispose() } @@ -311,29 +335,25 @@ class ServerSelectionController : capabilities.spreedCapability?.features?.isNotEmpty() == true if (hasTalk) { - activity?.runOnUiThread { + runOnUiThread { if (CapabilitiesUtilNew.isServerEOL(capabilities)) { if (resources != null) { - activity!!.runOnUiThread { + runOnUiThread { setErrorText(resources!!.getString(R.string.nc_settings_server_eol)) } } } else { - router.pushController( - RouterTransaction.with( - WebViewLoginController( - queryUrl.replace("/status.php", ""), - false - ) - ) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_BASE_URL, queryUrl.replace("/status.php", "")) + + val intent = Intent(context, WebViewLoginActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) } } } else { - if (activity != null && resources != null) { - activity!!.runOnUiThread { + if (resources != null) { + runOnUiThread { setErrorText(resources!!.getString(R.string.nc_server_unsupported)) } } @@ -342,8 +362,8 @@ class ServerSelectionController : override fun onError(e: Throwable) { Log.e(TAG, "Error while checking capabilities", e) - if (activity != null && resources != null) { - activity!!.runOnUiThread { + if (resources != null) { + runOnUiThread { setErrorText(resources!!.getString(R.string.nc_common_error_sorry)) } } @@ -360,70 +380,29 @@ class ServerSelectionController : } private fun setErrorText(text: String) { - binding?.errorWrapper?.visibility = View.VISIBLE - binding?.errorText?.text = text + binding.errorWrapper.visibility = View.VISIBLE + binding.errorText.text = text hideserverEntryProgressBar() } private fun showserverEntryProgressBar() { - binding?.errorWrapper?.visibility = View.INVISIBLE - binding?.serverEntryProgressBar?.visibility = View.VISIBLE + binding.errorWrapper.visibility = View.INVISIBLE + binding.serverEntryProgressBar.visibility = View.VISIBLE } private fun hideserverEntryProgressBar() { - binding?.serverEntryProgressBar?.visibility = View.INVISIBLE - } - - override fun onAttach(view: View) { - super.onAttach(view) - if (ApplicationWideMessageHolder.getInstance().messageType != null) { - if (ApplicationWideMessageHolder.getInstance().messageType - == ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION - ) { - setErrorText(resources!!.getString(R.string.nc_account_scheduled_for_deletion)) - ApplicationWideMessageHolder.getInstance().messageType = null - } else if (ApplicationWideMessageHolder.getInstance().messageType - == ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK - ) { - setErrorText(resources!!.getString(R.string.nc_settings_no_talk_installed)) - } else if (ApplicationWideMessageHolder.getInstance().messageType - == ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT - ) { - setErrorText(resources!!.getString(R.string.nc_server_failed_to_import_account)) - } - ApplicationWideMessageHolder.getInstance().messageType = null - } - if (activity != null && resources != null) { - DisplayUtils.applyColorToStatusBar( - activity, - ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) - ) - DisplayUtils.applyColorToNavigationBar( - activity!!.window, - ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) - ) - } - setCertTextView() + binding.serverEntryProgressBar.visibility = View.INVISIBLE } @SuppressLint("LongLogTag") private fun setCertTextView() { - if (activity != null) { - activity!!.runOnUiThread { - if (!TextUtils.isEmpty(appPreferences!!.temporaryClientCertAlias)) { - binding?.certTextView?.setText(R.string.nc_change_cert_auth) - } else { - binding?.certTextView?.setText(R.string.nc_configure_cert_auth) - } - hideserverEntryProgressBar() + runOnUiThread { + if (!TextUtils.isEmpty(appPreferences.temporaryClientCertAlias)) { + binding.certTextView.setText(R.string.nc_change_cert_auth) + } else { + binding.certTextView.setText(R.string.nc_configure_cert_auth) } - } - } - - override fun onDestroyView(view: View) { - super.onDestroyView(view) - if (activity != null) { - activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR + hideserverEntryProgressBar() } } @@ -443,7 +422,7 @@ class ServerSelectionController : get() = AppBarLayoutType.EMPTY companion object { - const val TAG = "ServerSelectionController" + private val TAG = ServerSelectionActivity::class.java.simpleName const val MIN_SERVER_MAJOR_VERSION = 13 } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt b/app/src/main/java/com/nextcloud/talk/account/SwitchAccountActivity.kt similarity index 74% rename from app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt rename to app/src/main/java/com/nextcloud/talk/account/SwitchAccountActivity.kt index 36bf20a067..c19c3223fd 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt +++ b/app/src/main/java/com/nextcloud/talk/account/SwitchAccountActivity.kt @@ -3,6 +3,8 @@ * * @author Mario Danic * @author Andy Scherzinger + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe * Copyright (C) 2022 Andy Scherzinger * Copyright (C) 2017 Mario Danic * @@ -22,25 +24,23 @@ * Parts related to account import were either copied from or inspired by the great work done by David Luhmer at: * https://github.com/nextcloud/ownCloud-Account-Importer */ -package com.nextcloud.talk.controllers +package com.nextcloud.talk.account import android.accounts.Account +import android.annotation.SuppressLint +import android.content.Intent +import android.content.pm.ActivityInfo +import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.view.MenuItem -import android.view.View import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.adapters.items.AdvancedUserItem import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -import com.nextcloud.talk.controllers.base.BaseController -import com.nextcloud.talk.controllers.util.viewBinding import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.databinding.ControllerGenericRvBinding +import com.nextcloud.talk.databinding.ActivitySwitchAccountBinding import com.nextcloud.talk.models.ImportAccount import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.users.UserManager @@ -56,14 +56,11 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import org.osmdroid.config.Configuration import java.net.CookieManager import javax.inject.Inject +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication @AutoInjector(NextcloudTalkApplication::class) -class SwitchAccountController(args: Bundle? = null) : - BaseController( - R.layout.controller_generic_rv, - args - ) { - private val binding: ControllerGenericRvBinding? by viewBinding(ControllerGenericRvBinding::bind) +class SwitchAccountActivity : BaseActivity() { + private lateinit var binding: ActivitySwitchAccountBinding @Inject lateinit var userManager: UserManager @@ -89,41 +86,52 @@ class SwitchAccountController(args: Bundle? = null) : if (userManager.setUserAsActive(user).blockingGet()) { cookieManager.cookieStore.removeAll() - if (activity != null) { - activity!!.runOnUiThread { router.popCurrentController() } - } + finish() } } true } - init { - setHasOptionsMenu(true) + @SuppressLint("SourceLockedOrientationActivity") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) sharedApplication!!.componentApplication.inject(this) + binding = ActivitySwitchAccountBinding.inflate(layoutInflater) + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + setContentView(binding.root) + setupActionBar() + setupPrimaryColors() + Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context)) - if (args?.containsKey(KEY_IS_ACCOUNT_IMPORT) == true) { - isAccountImport = true - } + + handleIntent() } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - router.popCurrentController() - true + private fun handleIntent() { + intent.extras?.let { + if (it.containsKey(KEY_IS_ACCOUNT_IMPORT)) { + isAccountImport = true } - else -> super.onOptionsItemSelected(item) } } - override fun onViewBound(view: View) { - super.onViewBound(view) - binding?.swipeRefreshLayout?.isEnabled = false + private fun setupActionBar() { + setSupportActionBar(binding.toolbar) + binding.toolbar.setNavigationOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(R.color.transparent, null))) + supportActionBar?.title = resources!!.getString(R.string.nc_select_an_account) + } - actionBar?.show() + @Suppress("Detekt.NestedBlockDepth") + override fun onResume() { + super.onResume() if (adapter == null) { - adapter = FlexibleAdapter(userItems, activity, false) + adapter = FlexibleAdapter(userItems, this, false) var participant: Participant if (!isAccountImport) { @@ -166,11 +174,10 @@ class SwitchAccountController(args: Bundle? = null) : } private fun prepareViews() { - val layoutManager: LinearLayoutManager = SmoothScrollLinearLayoutManager(activity) - binding?.recyclerView?.layoutManager = layoutManager - binding?.recyclerView?.setHasFixedSize(true) - binding?.recyclerView?.adapter = adapter - binding?.swipeRefreshLayout?.isEnabled = false + val layoutManager: LinearLayoutManager = SmoothScrollLinearLayoutManager(this) + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.setHasFixedSize(true) + binding.recyclerView.adapter = adapter } private fun reauthorizeFromImport(account: Account?) { @@ -180,14 +187,9 @@ class SwitchAccountController(args: Bundle? = null) : bundle.putString(KEY_USERNAME, importAccount.getUsername()) bundle.putString(KEY_TOKEN, importAccount.getToken()) bundle.putBoolean(KEY_IS_ACCOUNT_IMPORT, true) - router.pushController( - RouterTransaction.with(AccountVerificationController(bundle)) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) - } - override val title: String - get() = - resources!!.getString(R.string.nc_select_an_account) + val intent = Intent(context, AccountVerificationActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt b/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt similarity index 64% rename from app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt rename to app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt index 7bddc38c35..fb98745730 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.kt +++ b/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt @@ -3,6 +3,8 @@ * * @author Mario Danic * @author Andy Scherzinger + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe * Copyright (C) 2022 Andy Scherzinger * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) * @@ -19,9 +21,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.nextcloud.talk.controllers +package com.nextcloud.talk.account import android.annotation.SuppressLint +import android.content.Intent import android.content.pm.ActivityInfo import android.graphics.Bitmap import android.net.http.SslError @@ -40,34 +43,30 @@ import android.webkit.WebResourceResponse import android.webkit.WebSettings import android.webkit.WebView import android.webkit.WebViewClient -import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.res.ResourcesCompat -import androidx.work.Data +import androidx.activity.OnBackPressedCallback import androidx.work.OneTimeWorkRequest +import androidx.work.WorkInfo import androidx.work.WorkManager import autodagger.AutoInjector -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler +import com.google.android.material.snackbar.Snackbar import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity +import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -import com.nextcloud.talk.controllers.base.BaseController -import com.nextcloud.talk.controllers.util.viewBinding -import com.nextcloud.talk.databinding.ControllerWebViewLoginBinding +import com.nextcloud.talk.databinding.ActivityWebViewLoginBinding import com.nextcloud.talk.events.CertificateEvent -import com.nextcloud.talk.jobs.PushRegistrationWorker +import com.nextcloud.talk.jobs.AccountRemovalWorker import com.nextcloud.talk.models.LoginData import com.nextcloud.talk.users.UserManager -import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_BASE_URL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ORIGINAL_PROTOCOL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USERNAME -import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder import com.nextcloud.talk.utils.ssl.TrustManager import de.cotech.hw.fido.WebViewFidoBridge import io.reactivex.disposables.Disposable -import org.greenrobot.eventbus.EventBus import java.lang.reflect.Field import java.net.CookieManager import java.net.URLDecoder @@ -78,11 +77,9 @@ import java.util.Locale import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class WebViewLoginController(args: Bundle? = null) : BaseController( - R.layout.controller_web_view_login, - args -) { - private val binding: ControllerWebViewLoginBinding? by viewBinding(ControllerWebViewLoginBinding::bind) +class WebViewLoginActivity : BaseActivity() { + + private lateinit var binding: ActivityWebViewLoginBinding @Inject lateinit var userManager: UserManager @@ -90,34 +87,26 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( @Inject lateinit var trustManager: TrustManager - @Inject - lateinit var eventBus: EventBus - @Inject lateinit var cookieManager: CookieManager private var assembledPrefix: String? = null private var userQueryDisposable: Disposable? = null private var baseUrl: String? = null - private var isPasswordUpdate = false + private var reauthorizeAccount = false private var username: String? = null private var password: String? = null private var loginStep = 0 private var automatedLoginAttempted = false private var webViewFidoBridge: WebViewFidoBridge? = null - constructor(baseUrl: String?, isPasswordUpdate: Boolean) : this() { - this.baseUrl = baseUrl - this.isPasswordUpdate = isPasswordUpdate - } - - constructor(baseUrl: String?, isPasswordUpdate: Boolean, username: String?, password: String?) : this() { - this.baseUrl = baseUrl - this.isPasswordUpdate = isPasswordUpdate - this.username = username - this.password = password + private val onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(intent) + } } - private val webLoginUserAgent: String get() = ( Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + @@ -129,33 +118,57 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( ")" ) - @SuppressLint("SetJavaScriptEnabled") - override fun onViewBound(view: View) { - super.onViewBound(view) - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - + @SuppressLint("SourceLockedOrientationActivity") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + sharedApplication!!.componentApplication.inject(this) + binding = ActivityWebViewLoginBinding.inflate(layoutInflater) + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + setContentView(binding.root) actionBar?.hide() + setupPrimaryColors() + + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + handleIntent() + setupWebView() + } + + private fun handleIntent() { + val extras = intent.extras!! + baseUrl = extras.getString(KEY_BASE_URL) + username = extras.getString(KEY_USERNAME) + if (extras.containsKey(BundleKeys.KEY_REAUTHORIZE_ACCOUNT)) { + reauthorizeAccount = extras.getBoolean(BundleKeys.KEY_REAUTHORIZE_ACCOUNT) + } + + if (extras.containsKey(BundleKeys.KEY_PASSWORD)) { + password = extras.getString(BundleKeys.KEY_PASSWORD) + } + } + + @SuppressLint("SetJavaScriptEnabled") + private fun setupWebView() { assembledPrefix = resources!!.getString(R.string.nc_talk_login_scheme) + PROTOCOL_SUFFIX + "login/" - binding?.webview?.settings?.allowFileAccess = false - binding?.webview?.settings?.allowFileAccessFromFileURLs = false - binding?.webview?.settings?.javaScriptEnabled = true - binding?.webview?.settings?.javaScriptCanOpenWindowsAutomatically = false - binding?.webview?.settings?.domStorageEnabled = true - binding?.webview?.settings?.setUserAgentString(webLoginUserAgent) - binding?.webview?.settings?.saveFormData = false - binding?.webview?.settings?.savePassword = false - binding?.webview?.settings?.setRenderPriority(WebSettings.RenderPriority.HIGH) - binding?.webview?.clearCache(true) - binding?.webview?.clearFormData() - binding?.webview?.clearHistory() + binding.webview.settings.allowFileAccess = false + binding.webview.settings.allowFileAccessFromFileURLs = false + binding.webview.settings.javaScriptEnabled = true + binding.webview.settings.javaScriptCanOpenWindowsAutomatically = false + binding.webview.settings.domStorageEnabled = true + binding.webview.settings.userAgentString = webLoginUserAgent + binding.webview.settings.saveFormData = false + binding.webview.settings.savePassword = false + binding.webview.settings.setRenderPriority(WebSettings.RenderPriority.HIGH) + binding.webview.clearCache(true) + binding.webview.clearFormData() + binding.webview.clearHistory() WebView.clearClientCertPreferences(null) - webViewFidoBridge = WebViewFidoBridge.createInstanceForWebView(activity as AppCompatActivity?, binding?.webview) - CookieSyncManager.createInstance(activity) + webViewFidoBridge = WebViewFidoBridge.createInstanceForWebView(this, binding.webview) + CookieSyncManager.createInstance(this) android.webkit.CookieManager.getInstance().removeAllCookies(null) val headers: MutableMap = HashMap() - headers.put("OCS-APIRequest", "true") - binding?.webview?.webViewClient = object : WebViewClient() { + headers["OCS-APIRequest"] = "true" + binding.webview.webViewClient = object : WebViewClient() { private var basePageLoaded = false override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { webViewFidoBridge?.delegateShouldInterceptRequest(view, request) @@ -180,24 +193,24 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( override fun onPageFinished(view: WebView, url: String) { loginStep++ if (!basePageLoaded) { - binding?.progressBar?.visibility = View.GONE - binding?.webview?.visibility = View.VISIBLE + binding.progressBar.visibility = View.GONE + binding.webview.visibility = View.VISIBLE basePageLoaded = true } if (!TextUtils.isEmpty(username)) { if (loginStep == 1) { - binding?.webview?.loadUrl( + binding.webview.loadUrl( "javascript: {document.getElementsByClassName('login')[0].click(); };" ) } else if (!automatedLoginAttempted) { automatedLoginAttempted = true if (TextUtils.isEmpty(password)) { - binding?.webview?.loadUrl( + binding.webview.loadUrl( "javascript:var justStore = document.getElementById('user').value = '$username';" ) } else { - binding?.webview?.loadUrl( + binding.webview.loadUrl( "javascript: {" + "document.getElementById('user').value = '" + username + "';" + "document.getElementById('password').value = '" + password + "';" + @@ -213,8 +226,8 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( override fun onReceivedClientCertRequest(view: WebView, request: ClientCertRequest) { val user = userManager.currentUser.blockingGet() var alias: String? = null - if (!isPasswordUpdate) { - alias = appPreferences!!.temporaryClientCertAlias + if (!reauthorizeAccount) { + alias = appPreferences.temporaryClientCertAlias } if (TextUtils.isEmpty(alias) && user != null) { alias = user.clientCertificate @@ -223,9 +236,9 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( val finalAlias = alias Thread { try { - val privateKey = KeyChain.getPrivateKey(activity!!, finalAlias!!) + val privateKey = KeyChain.getPrivateKey(applicationContext, finalAlias!!) val certificates = KeyChain.getCertificateChain( - activity!!, + applicationContext, finalAlias ) if (privateKey != null && certificates != null) { @@ -241,16 +254,16 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( }.start() } else { KeyChain.choosePrivateKeyAlias( - activity!!, + this@WebViewLoginActivity, { chosenAlias: String? -> if (chosenAlias != null) { appPreferences!!.temporaryClientCertAlias = chosenAlias Thread { var privateKey: PrivateKey? = null try { - privateKey = KeyChain.getPrivateKey(activity!!, chosenAlias) + privateKey = KeyChain.getPrivateKey(applicationContext, chosenAlias) val certificates = KeyChain.getCertificateChain( - activity!!, + applicationContext, chosenAlias ) if (privateKey != null && certificates != null) { @@ -304,7 +317,7 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( super.onReceivedError(view, errorCode, description, failingUrl) } } - binding?.webview?.loadUrl("$baseUrl/index.php/login/flow", headers) + binding.webview.loadUrl("$baseUrl/index.php/login/flow", headers) } private fun dispose() { @@ -318,80 +331,77 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( val loginData = parseLoginData(assembledPrefix, dataString) if (loginData != null) { dispose() - val currentUser = userManager.currentUser.blockingGet() - var messageType: ApplicationWideMessageHolder.MessageType? = null - if (!isPasswordUpdate && - userManager.checkIfUserExists(loginData.username!!, baseUrl!!).blockingGet() - ) { - messageType = ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED - } + cookieManager.cookieStore.removeAll() + if (userManager.checkIfUserIsScheduledForDeletion(loginData.username!!, baseUrl!!).blockingGet()) { - ApplicationWideMessageHolder.getInstance().messageType = - ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION - if (!isPasswordUpdate) { - router.popToRoot() + Log.e(TAG, "Tried to add already existing user who is scheduled for deletion.") + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + // however the user is not yet deleted, just start AccountRemovalWorker again to make sure to delete it. + startAccountRemovalWorkerAndRestartApp() + } else if (userManager.checkIfUserExists(loginData.username!!, baseUrl!!).blockingGet()) { + if (reauthorizeAccount) { + updateUserAndRestartApp(loginData) } else { - router.popCurrentController() - } - } - val finalMessageType = messageType - cookieManager.cookieStore.removeAll() - if (!isPasswordUpdate && finalMessageType == null) { - val bundle = Bundle() - bundle.putString(KEY_USERNAME, loginData.username) - bundle.putString(KEY_TOKEN, loginData.token) - bundle.putString(KEY_BASE_URL, loginData.serverUrl) - var protocol = "" - if (baseUrl!!.startsWith("http://")) { - protocol = "http://" - } else if (baseUrl!!.startsWith("https://")) { - protocol = "https://" - } - if (!TextUtils.isEmpty(protocol)) { - bundle.putString(KEY_ORIGINAL_PROTOCOL, protocol) + Log.w(TAG, "It was tried to add an account that account already exists. Skipped user creation.") + restartApp() } - router.pushController( - RouterTransaction.with(AccountVerificationController(bundle)) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) } else { - if (isPasswordUpdate) { - if (currentUser != null) { - currentUser.clientCertificate = appPreferences!!.temporaryClientCertAlias - currentUser.token = loginData.token - val rowsUpdated = userManager.updateOrCreateUser(currentUser).blockingGet() - Log.d(TAG, "User rows updated: $rowsUpdated") - - if (finalMessageType != null) { - ApplicationWideMessageHolder.getInstance().messageType = finalMessageType - } + startAccountVerification(loginData) + } + } + } - val data = Data.Builder().putString( - PushRegistrationWorker.ORIGIN, - "WebViewLoginController#parseAndLoginFromWebView" - ).build() + private fun startAccountVerification(loginData: LoginData) { + val bundle = Bundle() + bundle.putString(KEY_USERNAME, loginData.username) + bundle.putString(KEY_TOKEN, loginData.token) + bundle.putString(KEY_BASE_URL, loginData.serverUrl) + var protocol = "" + if (baseUrl!!.startsWith("http://")) { + protocol = "http://" + } else if (baseUrl!!.startsWith("https://")) { + protocol = "https://" + } + if (!TextUtils.isEmpty(protocol)) { + bundle.putString(KEY_ORIGINAL_PROTOCOL, protocol) + } + val intent = Intent(context, AccountVerificationActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) + } - val pushRegistrationWork = OneTimeWorkRequest.Builder( - PushRegistrationWorker::class.java - ) - .setInputData(data) - .build() + private fun restartApp() { + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(intent) + } - WorkManager.getInstance().enqueue(pushRegistrationWork) - router.popCurrentController() - } - } else { - if (finalMessageType != null) { - // FIXME when the user registers a new account that was setup before (aka - // ApplicationWideMessageHolder.MessageType.ACCOUNT_UPDATED_NOT_ADDED) - // The token is not updated in the database and therefore the account not visible/usable - ApplicationWideMessageHolder.getInstance().messageType = finalMessageType + private fun updateUserAndRestartApp(loginData: LoginData) { + val currentUser = userManager.currentUser.blockingGet() + if (currentUser != null) { + currentUser.clientCertificate = appPreferences.temporaryClientCertAlias + currentUser.token = loginData.token + val rowsUpdated = userManager.updateOrCreateUser(currentUser).blockingGet() + Log.d(TAG, "User rows updated: $rowsUpdated") + restartApp() + } + } + + private fun startAccountRemovalWorkerAndRestartApp() { + val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() + WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork) + + WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id) + .observeForever { workInfo: WorkInfo -> + + when (workInfo.state) { + WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> { + restartApp() } - router.popToRoot() + + else -> {} } } - } } private fun parseLoginData(prefix: String?, dataString: String): LoginData? { @@ -432,30 +442,11 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( } } - override fun onAttach(view: View) { - super.onAttach(view) - if (activity != null && resources != null) { - DisplayUtils.applyColorToStatusBar( - activity, - ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) - ) - DisplayUtils.applyColorToNavigationBar( - activity!!.window, - ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) - ) - } - } - public override fun onDestroy() { super.onDestroy() dispose() } - override fun onDestroyView(view: View) { - super.onDestroyView(view) - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR - } - init { sharedApplication!!.componentApplication.inject(this) } @@ -464,7 +455,7 @@ class WebViewLoginController(args: Bundle? = null) : BaseController( get() = AppBarLayoutType.EMPTY companion object { - const val TAG = "WebViewLoginController" + private val TAG = WebViewLoginActivity::class.java.simpleName private const val PROTOCOL_SUFFIX = "://" private const val LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":" private const val PARAMETER_COUNT = 3 diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/providers/ActionBarProvider.java b/app/src/main/java/com/nextcloud/talk/activities/ActionBarProvider.java similarity index 93% rename from app/src/main/java/com/nextcloud/talk/controllers/base/providers/ActionBarProvider.java rename to app/src/main/java/com/nextcloud/talk/activities/ActionBarProvider.java index ceb393e673..76d7ce1fde 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/providers/ActionBarProvider.java +++ b/app/src/main/java/com/nextcloud/talk/activities/ActionBarProvider.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.nextcloud.talk.controllers.base.providers; +package com.nextcloud.talk.activities; import androidx.appcompat.app.ActionBar; diff --git a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt index 05792f941e..9d147d8adf 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt @@ -24,17 +24,26 @@ package com.nextcloud.talk.activities import android.annotation.SuppressLint import android.content.Context +import android.os.Build import android.os.Bundle import android.util.Log import android.view.View +import android.view.ViewGroup import android.view.WindowManager +import android.view.inputmethod.EditorInfo import android.webkit.SslErrorHandler +import android.widget.EditText +import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import autodagger.AutoInjector import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.talk.R +import com.nextcloud.talk.account.AccountVerificationActivity +import com.nextcloud.talk.account.ServerSelectionActivity +import com.nextcloud.talk.account.SwitchAccountActivity +import com.nextcloud.talk.account.WebViewLoginActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.events.CertificateEvent import com.nextcloud.talk.ui.theme.ViewThemeUtils @@ -77,6 +86,8 @@ open class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) super.onCreate(savedInstanceState) + + cleanTempCertPreference() } public override fun onStart() { @@ -87,6 +98,11 @@ open class BaseActivity : AppCompatActivity() { public override fun onResume() { super.onResume() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) { + val viewGroup = (findViewById(android.R.id.content) as ViewGroup).getChildAt(0) as ViewGroup + disableKeyboardPersonalisedLearning(viewGroup) + } + if (appPreferences.isScreenSecured) { window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) } else { @@ -104,6 +120,19 @@ open class BaseActivity : AppCompatActivity() { colorizeNavigationBar() } + fun setupPrimaryColors() { + if (resources != null) { + DisplayUtils.applyColorToStatusBar( + this, + ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) + ) + DisplayUtils.applyColorToNavigationBar( + window, + ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) + ) + } + } + open fun colorizeStatusBar() { if (resources != null) { if (appBarLayoutType == AppBarLayoutType.SEARCH_BAR) { @@ -123,7 +152,23 @@ open class BaseActivity : AppCompatActivity() { } } - fun showCertificateDialog( + @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) + } + } + } + + @Suppress("Detekt.NestedBlockDepth") + private fun showCertificateDialog( cert: X509Certificate, trustManager: TrustManager, sslErrorHandler: SslErrorHandler? @@ -160,15 +205,17 @@ open class BaseActivity : AppCompatActivity() { validUntil ) - val dialogBuilder = MaterialAlertDialogBuilder(this) - .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_security_white_24dp)) - .setTitle(R.string.nc_certificate_dialog_title) + val dialogBuilder = MaterialAlertDialogBuilder(this).setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_security_white_24dp + ) + ).setTitle(R.string.nc_certificate_dialog_title) .setMessage(dialogText) .setPositiveButton(R.string.nc_yes) { _, _ -> trustManager.addCertInTrustStore(cert) sslErrorHandler?.proceed() - } - .setNegativeButton(R.string.nc_no) { _, _ -> + }.setNegativeButton(R.string.nc_no) { _, _ -> sslErrorHandler?.cancel() } @@ -185,12 +232,23 @@ open class BaseActivity : AppCompatActivity() { } } + private fun cleanTempCertPreference() { + val temporaryClassNames: MutableList = ArrayList() + temporaryClassNames.add(ServerSelectionActivity::class.java.name) + temporaryClassNames.add(AccountVerificationActivity::class.java.name) + temporaryClassNames.add(WebViewLoginActivity::class.java.name) + temporaryClassNames.add(SwitchAccountActivity::class.java.name) + if (!temporaryClassNames.contains(javaClass.name)) { + appPreferences.removeTemporaryClientCertAlias() + } + } + @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: CertificateEvent) { showCertificateDialog(event.x509Certificate, event.magicTrustManager, event.sslErrorHandler) } companion object { - private val TAG = "BaseActivity" + private val TAG = BaseActivity::class.java.simpleName } } diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt index d39ad36f96..8a6c92466d 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt @@ -3096,7 +3096,7 @@ class CallActivity : CallBaseActivity() { } override fun suppressFitsSystemWindows() { - binding!!.controllerCallLayout.fitsSystemWindows = false + binding!!.callLayout.fitsSystemWindows = false } override fun onConfigurationChanged(newConfig: Configuration) { 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 4d7462e54d..5c107f9171 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -37,21 +37,14 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner import autodagger.AutoInjector -import com.bluelinelabs.conductor.Conductor -import com.bluelinelabs.conductor.Router -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler -import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler import com.google.android.material.snackbar.Snackbar -import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.R +import com.nextcloud.talk.account.ServerSelectionActivity +import com.nextcloud.talk.account.WebViewLoginActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.callnotification.CallNotificationActivity import com.nextcloud.talk.chat.ChatActivity -import com.nextcloud.talk.controllers.ServerSelectionController -import com.nextcloud.talk.controllers.WebViewLoginController -import com.nextcloud.talk.controllers.base.providers.ActionBarProvider import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivityMainBinding @@ -61,7 +54,6 @@ import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.SecurityUtils import com.nextcloud.talk.utils.bundle.BundleKeys -import com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import io.reactivex.Observer import io.reactivex.SingleObserver @@ -80,13 +72,9 @@ class MainActivity : BaseActivity(), ActionBarProvider { @Inject lateinit var userManager: UserManager - private var router: Router? = null - private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (!router!!.handleBack()) { - finish() - } + finish() } } @@ -111,8 +99,6 @@ class MainActivity : BaseActivity(), ActionBarProvider { setSupportActionBar(binding.toolbar) - router = Conductor.attachRouter(this, binding.controllerContainer, savedInstanceState) - handleIntent(intent) onBackPressedDispatcher.addCallback(this, onBackPressedCallback) @@ -128,28 +114,24 @@ class MainActivity : BaseActivity(), ActionBarProvider { } } - private fun launchLoginScreen() { - if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) { - router!!.pushController( - RouterTransaction.with( - WebViewLoginController(resources.getString(R.string.weblogin_url), false) - ) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) + private fun launchServerSelection() { + if (isBrandingUrlSet()) { + val intent = Intent(context, WebViewLoginActivity::class.java) + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_BASE_URL, resources.getString(R.string.weblogin_url)) + intent.putExtras(bundle) + startActivity(intent) } else { - router!!.setRoot( - RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) + val intent = Intent(context, ServerSelectionActivity::class.java) + startActivity(intent) } } + private fun isBrandingUrlSet() = !TextUtils.isEmpty(resources.getString(R.string.weblogin_url)) + override fun onStart() { Log.d(TAG, "onStart: Activity: " + System.identityHashCode(this).toString()) super.onStart() - logRouterBackStack(router!!) } override fun onResume() { @@ -178,14 +160,6 @@ class MainActivity : BaseActivity(), ActionBarProvider { startActivity(intent) } - fun addAccount() { - router!!.pushController( - RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(VerticalChangeHandler()) - .popChangeHandler(VerticalChangeHandler()) - ) - } - private fun handleActionFromContact(intent: Intent) { if (intent.action == Intent.ACTION_VIEW && intent.data != null) { val cursor = contentResolver.query(intent.data!!, null, null, null, null) @@ -209,7 +183,7 @@ class MainActivity : BaseActivity(), ActionBarProvider { startConversation(user) } else { Snackbar.make( - binding.controllerContainer, + binding.root, R.string.nc_phone_book_integration_account_not_found, Snackbar.LENGTH_LONG ).show() @@ -283,28 +257,18 @@ class MainActivity : BaseActivity(), ActionBarProvider { } if (user != null && userManager.setUserAsActive(user).blockingGet()) { - // this should be avoided (it's still from conductor architecture). activities should be opened directly. if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { - if (!router!!.hasRootController()) { - openConversationList() - } val callNotificationIntent = Intent(this, CallNotificationActivity::class.java) intent.extras?.let { callNotificationIntent.putExtras(it) } startActivity(callNotificationIntent) } else { - logRouterBackStack(router!!) - val chatIntent = Intent(context, ChatActivity::class.java) chatIntent.putExtras(intent.extras!!) startActivity(chatIntent) - - logRouterBackStack(router!!) } } - } else if (intent.hasExtra(ADD_ACCOUNT) && intent.getBooleanExtra(ADD_ACCOUNT, false)) { - addAccount() - } else if (!router!!.hasRootController()) { + } else { if (!appPreferences.isDbRoomMigrated) { appPreferences.isDbRoomMigrated = true } @@ -321,7 +285,7 @@ class MainActivity : BaseActivity(), ActionBarProvider { } } else { runOnUiThread { - launchLoginScreen() + launchServerSelection() } } } @@ -333,19 +297,7 @@ class MainActivity : BaseActivity(), ActionBarProvider { } } - private fun logRouterBackStack(router: Router) { - if (BuildConfig.DEBUG) { - val backstack = router.backstack - var routerTransaction: RouterTransaction? - Log.d(TAG, " backstack size: " + router.backstackSize) - for (i in 0 until router.backstackSize) { - routerTransaction = backstack[i] - Log.d(TAG, " controller: " + routerTransaction.controller) - } - } - } - companion object { - private const val TAG = "MainActivity" + private val TAG = MainActivity::class.java.simpleName } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 15364af132..1486756469 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -231,6 +231,6 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : } companion object { - private val TAG = NextcloudTalkApplication::class.java.simpleName + private val TAG = IncomingPollMessageViewHolder::class.java.simpleName } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index f850c82de2..dcc0ca7c77 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -209,6 +209,6 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : } companion object { - private val TAG = NextcloudTalkApplication::class.java.simpleName + private val TAG = OutcomingPollMessageViewHolder::class.java.simpleName } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BasicListItemWithImage.kt similarity index 95% rename from app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt rename to app/src/main/java/com/nextcloud/talk/bottomsheet/items/BasicListItemWithImage.kt index 80399135b5..247494f0ba 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt +++ b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BasicListItemWithImage.kt @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.controllers.bottomsheet.items +package com.nextcloud.talk.bottomsheet.items import android.widget.ImageView import androidx.annotation.DrawableRes diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BottomSheets.kt similarity index 97% rename from app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt rename to app/src/main/java/com/nextcloud/talk/bottomsheet/items/BottomSheets.kt index ede849cddb..fa11981de0 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt +++ b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BottomSheets.kt @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.controllers.bottomsheet.items +package com.nextcloud.talk.bottomsheet.items import androidx.annotation.CheckResult import androidx.recyclerview.widget.LinearLayoutManager diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt similarity index 98% rename from app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt rename to app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt index 075cae600f..ccae4ac4f1 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.controllers.bottomsheet.items +package com.nextcloud.talk.bottomsheet.items import android.view.View import android.view.ViewGroup diff --git a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt index eb5da933ac..5d88fdc649 100644 --- a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt @@ -317,7 +317,7 @@ class CallNotificationActivity : CallBaseActivity() { } override fun suppressFitsSystemWindows() { - binding!!.controllerCallNotificationLayout.fitsSystemWindows = false + binding!!.callNotificationLayout.fitsSystemWindows = false } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 32223d561c..1f5ee43649 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -1605,7 +1605,7 @@ class ChatActivity : participantPermissions.hasChatPermission() && !isReadOnlyConversation() ) { - val messageSwipeController = MessageSwipeCallback( + val messageSwipeCallback = MessageSwipeCallback( this, object : MessageSwipeActions { override fun showReplyUI(position: Int) { @@ -1617,7 +1617,7 @@ class ChatActivity : } ) - val itemTouchHelper = ItemTouchHelper(messageSwipeController) + val itemTouchHelper = ItemTouchHelper(messageSwipeCallback) itemTouchHelper.attachToRecyclerView(binding.messagesListView) } } @@ -2561,7 +2561,7 @@ class ChatActivity : super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Log.d(ConversationsListActivity.TAG, "upload starting after permissions were granted") + Log.d(TAG, "upload starting after permissions were granted") if (filesToUpload.isNotEmpty()) { uploadFiles(filesToUpload) } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt index a318d20d79..c32f748ff6 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt @@ -488,17 +488,13 @@ class ContactsActivity : } else { adapter?.filterItems() } - - binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false } override fun onError(e: Throwable) { - binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false dispose(contactsQueryDisposable) } override fun onComplete() { - binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false dispose(contactsQueryDisposable) alreadyFetching = false disengageProgressBar() @@ -656,12 +652,9 @@ class ContactsActivity : private fun prepareViews() { layoutManager = SmoothScrollLinearLayoutManager(this) - binding.controllerGenericRv.recyclerView.layoutManager = layoutManager - binding.controllerGenericRv.recyclerView.setHasFixedSize(true) - binding.controllerGenericRv.recyclerView.adapter = adapter - binding.controllerGenericRv.swipeRefreshLayout.setOnRefreshListener { fetchData() } - - binding.controllerGenericRv.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it.swipeRefreshLayout) } + binding.contactsRv.layoutManager = layoutManager + binding.contactsRv.setHasFixedSize(true) + binding.contactsRv.adapter = adapter binding.listOpenConversationsImage.background?.setColorFilter( ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null), @@ -677,7 +670,7 @@ class ContactsActivity : private fun disengageProgressBar() { if (!alreadyFetching) { binding.loadingContent.visibility = View.GONE - binding.controllerGenericRv.root.visibility = View.VISIBLE + binding.root.visibility = View.VISIBLE if (isNewConversationView) { binding.callHeaderLayout.visibility = View.VISIBLE } @@ -713,8 +706,6 @@ class ContactsActivity : adapter?.updateDataSet(contactItems as List?) } - binding.controllerGenericRv?.swipeRefreshLayout?.isEnabled = !adapter!!.hasFilter() - return true } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt deleted file mode 100644 index 2c5e81193a..0000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * @author Andy Scherzinger - * Copyright (C) 2022 Andy Scherzinger - * Copyright (C) 2017-2018 Mario Danic - * - * 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.annotation.SuppressLint -import android.media.MediaPlayer -import android.media.RingtoneManager -import android.net.Uri -import android.os.Bundle -import android.os.Handler -import android.text.TextUtils -import android.util.Log -import android.view.MenuItem -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import autodagger.AutoInjector -import com.bluelinelabs.logansquare.LoganSquare -import com.nextcloud.talk.R -import com.nextcloud.talk.adapters.items.NotificationSoundItem -import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -import com.nextcloud.talk.controllers.base.BaseController -import com.nextcloud.talk.controllers.util.viewBinding -import com.nextcloud.talk.databinding.ControllerGenericRvBinding -import com.nextcloud.talk.models.RingtoneSettings -import com.nextcloud.talk.utils.NotificationUtils -import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ARE_CALL_SOUNDS -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.SelectableAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import java.io.IOException - -@AutoInjector(NextcloudTalkApplication::class) -class RingtoneSelectionController(args: Bundle) : - BaseController( - R.layout.controller_generic_rv, - args - ), - FlexibleAdapter.OnItemClickListener { - private val binding: ControllerGenericRvBinding? by viewBinding(ControllerGenericRvBinding::bind) - - private var adapter: FlexibleAdapter<*>? = null - private var adapterDataObserver: RecyclerView.AdapterDataObserver? = null - private val abstractFlexibleItemList: MutableList> = ArrayList() - private val callNotificationSounds: Boolean - private var mediaPlayer: MediaPlayer? = null - private var cancelMediaPlayerHandler: Handler? = null - - override fun onViewBound(view: View) { - super.onViewBound(view) - if (adapter == null) { - adapter = FlexibleAdapter(abstractFlexibleItemList, activity, false) - adapter!!.setNotifyChangeOfUnfilteredItems(true).mode = SelectableAdapter.Mode.SINGLE - adapter!!.addListener(this) - cancelMediaPlayerHandler = Handler() - } - adapter!!.addListener(this) - prepareViews() - fetchNotificationSounds() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == android.R.id.home) { - router.popCurrentController() - } else { - super.onOptionsItemSelected(item) - } - } - - private fun prepareViews() { - val layoutManager: RecyclerView.LayoutManager = SmoothScrollLinearLayoutManager(activity) - binding?.recyclerView?.layoutManager = layoutManager - binding?.recyclerView?.setHasFixedSize(true) - binding?.recyclerView?.adapter = adapter - adapterDataObserver = object : RecyclerView.AdapterDataObserver() { - override fun onChanged() { - super.onChanged() - findSelectedSound() - } - } - adapter!!.registerAdapterDataObserver(adapterDataObserver!!) - binding?.swipeRefreshLayout?.isEnabled = false - } - - @SuppressLint("LongLogTag") - private fun findSelectedSound() { - var foundDefault = false - var preferencesString: String? = null - val callsEnabledButNoRingtone = callNotificationSounds && - TextUtils.isEmpty(appPreferences.callRingtoneUri.also { preferencesString = it }) - val noCallsAndNoMessageTone = !callNotificationSounds && - TextUtils.isEmpty(appPreferences.messageRingtoneUri.also { preferencesString = it }) - if (callsEnabledButNoRingtone || noCallsAndNoMessageTone) { - adapter!!.toggleSelection(1) - foundDefault = true - } - if (!TextUtils.isEmpty(preferencesString) && !foundDefault) { - try { - val ringtoneSettings: RingtoneSettings = - LoganSquare.parse(preferencesString, RingtoneSettings::class.java) - if (ringtoneSettings.ringtoneUri == null) { - adapter!!.toggleSelection(0) - } else if (ringtoneSettings.ringtoneUri!!.toString() == ringtoneString) { - adapter!!.toggleSelection(1) - } else { - var notificationSoundItem: NotificationSoundItem? - for (i in 2 until adapter!!.itemCount) { - notificationSoundItem = adapter!!.getItem(i) as NotificationSoundItem? - if ( - notificationSoundItem!!.notificationSoundUri == ringtoneSettings.ringtoneUri!!.toString() - ) { - adapter!!.toggleSelection(i) - break - } - } - } - } catch (e: IOException) { - Log.e(TAG, "Failed to parse ringtone settings") - } - } - adapter!!.unregisterAdapterDataObserver(adapterDataObserver!!) - adapterDataObserver = null - } - - private val ringtoneString: String - get() = if (callNotificationSounds) { - NotificationUtils.DEFAULT_CALL_RINGTONE_URI - } else { - NotificationUtils.DEFAULT_MESSAGE_RINGTONE_URI - } - - private fun fetchNotificationSounds() { - abstractFlexibleItemList.add( - NotificationSoundItem( - resources!!.getString(R.string.nc_settings_no_ringtone), - null - ) - ) - abstractFlexibleItemList.add( - NotificationSoundItem( - resources!!.getString(R.string.nc_settings_default_ringtone), - ringtoneString - ) - ) - if (activity != null) { - val manager = RingtoneManager(activity) - if (callNotificationSounds) { - manager.setType(RingtoneManager.TYPE_RINGTONE) - } else { - manager.setType(RingtoneManager.TYPE_NOTIFICATION) - } - val cursor = manager.cursor - var notificationSoundItem: NotificationSoundItem - while (cursor.moveToNext()) { - val notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX) - val notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX) - val completeNotificationUri = notificationUri + "/" + cursor.getString(RingtoneManager.ID_COLUMN_INDEX) - notificationSoundItem = NotificationSoundItem(notificationTitle, completeNotificationUri) - abstractFlexibleItemList.add(notificationSoundItem) - } - } - adapter!!.updateDataSet(abstractFlexibleItemList as List?, false) - } - - override fun onItemClick(view: View, position: Int): Boolean { - val notificationSoundItem = adapter!!.getItem(position) as NotificationSoundItem? - var ringtoneUri: Uri? = null - if (!TextUtils.isEmpty(notificationSoundItem!!.notificationSoundUri)) { - ringtoneUri = Uri.parse(notificationSoundItem.notificationSoundUri) - endMediaPlayer() - mediaPlayer = MediaPlayer.create(activity, ringtoneUri) - cancelMediaPlayerHandler = Handler() - cancelMediaPlayerHandler!!.postDelayed( - { endMediaPlayer() }, - (mediaPlayer!!.duration + DURATION_EXTENSION).toLong() - ) - mediaPlayer!!.start() - } - if (adapter!!.selectedPositions.size == 0 || adapter!!.selectedPositions[0] != position) { - val ringtoneSettings = RingtoneSettings() - ringtoneSettings.ringtoneName = notificationSoundItem.notificationSoundName - ringtoneSettings.ringtoneUri = ringtoneUri - if (callNotificationSounds) { - try { - appPreferences!!.callRingtoneUri = LoganSquare.serialize(ringtoneSettings) - adapter!!.toggleSelection(position) - adapter!!.notifyDataSetChanged() - } catch (e: IOException) { - Log.e(TAG, "Failed to store selected ringtone for calls") - } - } else { - try { - appPreferences!!.messageRingtoneUri = LoganSquare.serialize(ringtoneSettings) - adapter!!.toggleSelection(position) - adapter!!.notifyDataSetChanged() - } catch (e: IOException) { - Log.e(TAG, "Failed to store selected ringtone for calls") - } - } - } - return true - } - - private fun endMediaPlayer() { - if (cancelMediaPlayerHandler != null) { - cancelMediaPlayerHandler!!.removeCallbacksAndMessages(null) - } - if (mediaPlayer != null) { - if (mediaPlayer!!.isPlaying) { - mediaPlayer!!.stop() - } - mediaPlayer!!.release() - mediaPlayer = null - } - } - - public override fun onDestroy() { - endMediaPlayer() - super.onDestroy() - } - - companion object { - private const val TAG = "RingtoneSelection" - private const val DURATION_EXTENSION = 25 - } - - init { - setHasOptionsMenu(true) - sharedApplication!!.componentApplication.inject(this) - callNotificationSounds = args.getBoolean(KEY_ARE_CALL_SOUNDS, false) - } - - override val title: String - get() = - resources!!.getString(R.string.nc_settings_notification_sounds) -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt deleted file mode 100644 index 18737bc5ab..0000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Andy Scherzinger - * @author BlueLine Labs, Inc. - * @author Mario Danic - * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) - * Copyright (C) 2021 BlueLine Labs, Inc. - * Copyright (C) 2020 Mario Danic (mario@lovelyhq.com) - * - * 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.os.Bundle -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.LayoutRes -import androidx.annotation.RequiresApi -import androidx.appcompat.app.ActionBar -import autodagger.AutoInjector -import com.bluelinelabs.conductor.Controller -import com.bluelinelabs.conductor.ControllerChangeHandler -import com.bluelinelabs.conductor.ControllerChangeType -import com.nextcloud.talk.R -import com.nextcloud.talk.activities.MainActivity -import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -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.ui.theme.ViewThemeUtils -import com.nextcloud.talk.utils.preferences.AppPreferences -import javax.inject.Inject -import kotlin.jvm.internal.Intrinsics - -// TODO: check what needs to be migrated from this class to BaseActivity etc when conductor is removed -@AutoInjector(NextcloudTalkApplication::class) -abstract class BaseController(@LayoutRes var layoutRes: Int, args: Bundle? = null) : Controller(args) { - enum class AppBarLayoutType { - TOOLBAR, SEARCH_BAR, EMPTY - } - - @Inject - lateinit var appPreferences: AppPreferences - - @Inject - lateinit var context: Context - - @Inject - lateinit var viewThemeUtils: ViewThemeUtils - - protected open val title: String? - get() = null - - @Suppress("Detekt.TooGenericExceptionCaught") - protected val actionBar: ActionBar? - get() { - var actionBarProvider: ActionBarProvider? = null - if (this.activity is ActionBarProvider) { - try { - actionBarProvider = this.activity as ActionBarProvider? - } catch (e: Exception) { - Log.d(TAG, "Failed to fetch the action bar provider", e) - } - } - return actionBarProvider?.supportActionBar - } - - init { - @Suppress("LeakingThis") - sharedApplication!!.componentApplication.inject(this) - addLifecycleListener(object : LifecycleListener() { - override fun postCreateView(controller: Controller, view: View) { - onViewBound(view) - actionBar?.let { setTitle() } - } - }) - cleanTempCertPreference() - } - - fun isAlive(): Boolean { - return !isDestroyed && !isBeingDestroyed - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup, - savedViewState: Bundle? - ): View { - return inflater.inflate(layoutRes, container, false) - } - - protected open fun onViewBound(view: View) { - var activity: MainActivity? = null - - // if (getActivity() != null && getActivity() is MainActivity) { - // activity = getActivity() as MainActivity? - // viewThemeUtils.material.themeCardView(activity!!.binding.searchToolbar) - // viewThemeUtils.material.themeToolbar(activity.binding.toolbar) - // viewThemeUtils.material.themeSearchBarText(activity.binding.searchText) - // } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) { - disableKeyboardPersonalisedLearning((view as ViewGroup)) - if (activity != null) { - disableKeyboardPersonalisedLearning(activity.binding.appBar) - } - } - } - - override fun onAttach(view: View) { - // showSearchOrToolbar() - setTitle() - if (actionBar != null) { - actionBar!!.setDisplayHomeAsUpEnabled(parentController != null || router.backstackSize >= 1) - } - super.onAttach(view) - } - - // open fun showSearchOrToolbar() { - // if (isValidActivity(activity)) { - // val showSearchBar = appBarLayoutType == AppBarLayoutType.SEARCH_BAR - // val activity = activity as MainActivity - // - // if (appBarLayoutType == AppBarLayoutType.EMPTY) { - // hideBars(activity.binding) - // } else { - // if (showSearchBar) { - // showSearchBar(activity.binding) - // } else { - // showToolbar(activity.binding) - // } - // colorizeStatusBar(showSearchBar, activity, resources) - // } - // - // colorizeNavigationBar(activity, resources) - // } - // } - // - // private fun isValidActivity(activity: Activity?): Boolean { - // return activity != null && activity is MainActivity - // } - // - // private fun showSearchBar(binding: ActivityMainBinding) { - // val layoutParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams - // binding.searchToolbar.visibility = View.VISIBLE - // binding.searchText.hint = searchHint - // binding.toolbar.visibility = View.GONE - // // layoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout - // // .LayoutParams.SCROLL_FLAG_SNAP | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); - // layoutParams.scrollFlags = 0 - // binding.appBar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - // binding.appBar.context, - // R.animator.appbar_elevation_off - // ) - // binding.searchToolbar.layoutParams = layoutParams - // } - // - // private fun showToolbar(binding: ActivityMainBinding) { - // val layoutParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams - // binding.searchToolbar.visibility = View.GONE - // binding.toolbar.visibility = View.VISIBLE - // viewThemeUtils.material.colorToolbarOverflowIcon(binding.toolbar) - // layoutParams.scrollFlags = 0 - // binding.appBar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - // binding.appBar.context, - // R.animator.appbar_elevation_on - // ) - // binding.searchToolbar.layoutParams = layoutParams - // } - // - // private fun hideBars(binding: ActivityMainBinding) { - // binding.toolbar.visibility = View.GONE - // binding.searchToolbar.visibility = View.GONE - // } - // - // fun hideSearchBar() { - // val activity = activity as MainActivity? - // val layoutParams = activity!!.binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams - // activity.binding.searchToolbar.visibility = View.GONE - // activity.binding.toolbar.visibility = View.VISIBLE - // layoutParams.scrollFlags = 0 - // activity.binding.appBar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - // activity.binding.appBar.context, - // R.animator.appbar_elevation_on - // ) - // } - // - // private fun colorizeStatusBar(showSearchBar: Boolean, activity: Activity?, resources: Resources?) { - // if (activity != null && resources != null) { - // if (showSearchBar) { - // view?.let { viewThemeUtils.platform.resetStatusBar(activity) } - // } else { - // view?.let { viewThemeUtils.platform.themeStatusBar(activity, it) } - // } - // } - // } - // - // private fun colorizeNavigationBar(activity: Activity?, resources: Resources?) { - // if (activity != null && resources != null) { - // DisplayUtils.applyColorToNavigationBar( - // activity.window, - // ResourcesCompat.getColor(resources, R.color.bg_default, null) - // ) - // } - // } - - override fun onDetach(view: View) { - super.onDetach(view) - val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(view.windowToken, 0) - } - - protected fun setTitle() { - if (isTitleSetable()) { - run { - calculateValidParentController() - } - actionBar!!.title = title - } - } - - private fun calculateValidParentController() { - var parentController = parentController - while (parentController != null) { - parentController = parentController.parentController - } - } - - private fun isTitleSetable(): Boolean { - return title != null && actionBar != null - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - router.popCurrentController() - return true - } - return super.onOptionsItemSelected(item) - } - - override fun onChangeStarted(changeHandler: ControllerChangeHandler, changeType: ControllerChangeType) { - super.onChangeStarted(changeHandler, changeType) - if (changeType.isEnter && actionBar != null) { - configureMenu(actionBar!!) - } - } - - fun configureMenu(toolbar: ActionBar) { - Intrinsics.checkNotNullParameter(toolbar, "toolbar") - } - - // TODO: check if this must be migrated when using activities instead of conductor - private fun cleanTempCertPreference() { - val temporaryClassNames: MutableList = ArrayList() - 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() - } - } - - @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) - } - } - } - - open val appBarLayoutType: AppBarLayoutType - get() = AppBarLayoutType.TOOLBAR - val searchHint: String - get() = context.getString(R.string.appbar_search_in, context.getString(R.string.nc_app_product_name)) - - companion object { - private val TAG = BaseController::class.java.simpleName - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt b/app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt deleted file mode 100644 index 70d6247f1a..0000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/util/ControllerViewBindingDelegate.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.util - -import android.view.View -import androidx.lifecycle.LifecycleObserver -import androidx.viewbinding.ViewBinding -import com.bluelinelabs.conductor.Controller -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -fun Controller.viewBinding(bindingFactory: (View) -> T) = - ControllerViewBindingDelegate(this, bindingFactory) - -class ControllerViewBindingDelegate( - controller: Controller, - private val viewBinder: (View) -> T -) : ReadOnlyProperty, LifecycleObserver { - - private var binding: T? = null - - init { - controller.addLifecycleListener(object : Controller.LifecycleListener() { - override fun postDestroyView(controller: Controller) { - binding = null - } - }) - } - - override fun getValue(thisRef: Controller, property: KProperty<*>): T? { - if (binding == null) { - binding = thisRef.view?.let { viewBinder(it) } - } - return binding - } -} diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index 42c5ef67e0..8bf1be73a6 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -58,8 +58,8 @@ import com.nextcloud.talk.adapters.items.ParticipantItem import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.contacts.ContactsActivity -import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage -import com.nextcloud.talk.controllers.bottomsheet.items.listItemsWithImage +import com.nextcloud.talk.bottomsheet.items.BasicListItemWithImage +import com.nextcloud.talk.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.conversationinfoedit.ConversationInfoEditActivity import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivityConversationInfoBinding diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt index 0aa943d055..d1392ba43a 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -50,6 +50,7 @@ import android.view.MotionEvent import android.view.View import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager +import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView @@ -71,8 +72,11 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.talk.R +import com.nextcloud.talk.account.ServerSelectionActivity +import com.nextcloud.talk.account.WebViewLoginActivity import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.activities.CallActivity +import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.adapters.items.ConversationItem import com.nextcloud.talk.adapters.items.GenericTextHeaderItem import com.nextcloud.talk.adapters.items.LoadMoreResultsItem @@ -84,7 +88,7 @@ import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.databinding.ControllerConversationsRvBinding +import com.nextcloud.talk.databinding.ActivityConversationsBinding import com.nextcloud.talk.events.ConversationsListFetchDataEvent import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.jobs.AccountRemovalWorker @@ -109,6 +113,7 @@ import com.nextcloud.talk.utils.Mimetype import com.nextcloud.talk.utils.ParticipantPermissions import com.nextcloud.talk.utils.UserIdUtils import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ADDITIONAL_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_HIDE_SOURCE_ROOM import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_FLAG import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_TEXT @@ -146,7 +151,7 @@ class ConversationsListActivity : FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener { - private lateinit var binding: ControllerConversationsRvBinding + private lateinit var binding: ActivityConversationsBinding @Inject lateinit var userManager: UserManager @@ -202,7 +207,6 @@ class ConversationsListActivity : private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - // TODO: replace this when conductor is removed. For now it avoids to load the MainActiviy which has no UI. finishAffinity() } } @@ -211,7 +215,7 @@ class ConversationsListActivity : super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - binding = ControllerConversationsRvBinding.inflate(layoutInflater) + binding = ActivityConversationsBinding.inflate(layoutInflater) setupActionBar() setContentView(binding.root) setupSystemColors() @@ -740,11 +744,20 @@ class ConversationsListActivity : } } + if (resources!!.getBoolean(R.bool.multiaccount_support)) { + dialogBuilder.setNeutralButton(R.string.nc_account_chooser_add_account) { _, _ -> + val intent = Intent(this, ServerSelectionActivity::class.java) + intent.putExtra(ADD_ADDITIONAL_ACCOUNT, true) + startActivity(intent) + } + } + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder) val dialog = dialogBuilder.show() viewThemeUtils.platform.colorTextButtons( dialog.getButton(AlertDialog.BUTTON_POSITIVE), - dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + dialog.getButton(AlertDialog.BUTTON_NEGATIVE), + dialog.getButton(AlertDialog.BUTTON_NEUTRAL) ) } } @@ -819,10 +832,10 @@ class ConversationsListActivity : @SuppressLint("ClickableViewAccessibility") private fun prepareViews() { layoutManager = SmoothScrollLinearLayoutManager(this) - binding?.recyclerView?.layoutManager = layoutManager - binding?.recyclerView?.setHasFixedSize(true) - binding?.recyclerView?.adapter = adapter - binding?.recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() { + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.setHasFixedSize(true) + binding.recyclerView.adapter = adapter + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) if (newState == RecyclerView.SCROLL_STATE_IDLE) { @@ -1131,7 +1144,7 @@ class ConversationsListActivity : selectedConversation!!.displayName ) } - binding?.floatingActionButton?.let { + binding.floatingActionButton.let { val dialogBuilder = MaterialAlertDialogBuilder(it.context) .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.upload)) .setTitle(confirmationQuestion) @@ -1358,30 +1371,17 @@ class ConversationsListActivity : .setTitle(R.string.nc_dialog_invalid_password) .setMessage(R.string.nc_dialog_reauth_or_delete) .setCancelable(false) - .setPositiveButton(R.string.nc_delete) { _, _ -> - val otherUserExists = userManager - .scheduleUserForDeletionWithId(currentUser!!.id!!) - .blockingGet() - val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() - WorkManager.getInstance().enqueue(accountRemovalWork) - if (otherUserExists) { - finish() - startActivity(intent) - } else if (!otherUserExists) { - Log.d(TAG, "No other users found. AccountRemovalWorker will restart the app.") - } + .setPositiveButton(R.string.nc_settings_remove_account) { _, _ -> + deleteUserAndRestartApp() + } + .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ -> + val intent = Intent(context, WebViewLoginActivity::class.java) + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_BASE_URL, currentUser!!.baseUrl) + bundle.putBoolean(BundleKeys.KEY_REAUTHORIZE_ACCOUNT, true) + intent.putExtras(bundle) + startActivity(intent) } - - // TODO: show negative button again when conductor is removed - // .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ -> - // // router.pushController( - // // RouterTransaction.with( - // // WebViewLoginController(currentUser!!.baseUrl, true) - // // ) - // // .pushChangeHandler(VerticalChangeHandler()) - // // .popChangeHandler(VerticalChangeHandler()) - // // ) - // } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder) val dialog = dialogBuilder.show() @@ -1392,6 +1392,50 @@ class ConversationsListActivity : } } + @SuppressLint("CheckResult") + private fun deleteUserAndRestartApp() { + userManager.scheduleUserForDeletionWithId(currentUser!!.id!!).blockingGet() + val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() + WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork) + + WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id) + .observeForever { workInfo: WorkInfo -> + + when (workInfo.state) { + WorkInfo.State.SUCCEEDED -> { + val text = String.format( + context.resources.getString(R.string.nc_deleted_user), + currentUser!!.displayName + ) + Toast.makeText( + context, + text, + Toast.LENGTH_LONG + ).show() + restartApp() + } + + WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> { + Toast.makeText( + context, + context.resources.getString(R.string.nc_common_error_sorry), + Toast.LENGTH_LONG + ).show() + Log.e(TAG, "something went wrong when deleting user with id " + currentUser!!.userId) + restartApp() + } + + else -> {} + } + } + } + + private fun restartApp() { + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(intent) + } + private fun showOutdatedClientDialog() { binding.floatingActionButton.let { val dialogBuilder = MaterialAlertDialogBuilder(it.context) @@ -1423,11 +1467,20 @@ class ConversationsListActivity : } } + if (resources!!.getBoolean(R.bool.multiaccount_support)) { + dialogBuilder.setNeutralButton(R.string.nc_account_chooser_add_account) { _, _ -> + val intent = Intent(this, ServerSelectionActivity::class.java) + intent.putExtra(ADD_ADDITIONAL_ACCOUNT, true) + startActivity(intent) + } + } + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder) val dialog = dialogBuilder.show() viewThemeUtils.platform.colorTextButtons( dialog.getButton(AlertDialog.BUTTON_POSITIVE), - dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + dialog.getButton(AlertDialog.BUTTON_NEGATIVE), + dialog.getButton(AlertDialog.BUTTON_NEUTRAL) ) } } @@ -1445,23 +1498,31 @@ class ConversationsListActivity : .setTitle(R.string.nc_dialog_maintenance_mode) .setMessage(R.string.nc_dialog_maintenance_mode_description) .setCancelable(false) + .setNegativeButton(R.string.nc_settings_remove_account) { _, _ -> + deleteUserAndRestartApp() + } if (resources!!.getBoolean(R.bool.multiaccount_support) && userManager.users.blockingGet().size > 1) { dialogBuilder.setPositiveButton(R.string.nc_switch_account) { _, _ -> val newFragment: DialogFragment = ChooseAccountDialogFragment.newInstance() newFragment.show(supportFragmentManager, ChooseAccountDialogFragment.TAG) } - } else { - dialogBuilder.setPositiveButton(R.string.nc_close_app) { _, _ -> - finishAffinity() - finish() + } + + if (resources!!.getBoolean(R.bool.multiaccount_support)) { + dialogBuilder.setNeutralButton(R.string.nc_account_chooser_add_account) { _, _ -> + val intent = Intent(this, ServerSelectionActivity::class.java) + intent.putExtra(ADD_ADDITIONAL_ACCOUNT, true) + startActivity(intent) } } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder) val dialog = dialogBuilder.show() viewThemeUtils.platform.colorTextButtons( - dialog.getButton(AlertDialog.BUTTON_POSITIVE) + dialog.getButton(AlertDialog.BUTTON_POSITIVE), + dialog.getButton(AlertDialog.BUTTON_NEGATIVE), + dialog.getButton(AlertDialog.BUTTON_NEUTRAL) ) } } else { @@ -1470,55 +1531,41 @@ class ConversationsListActivity : } private fun showServerEOLDialog() { - binding?.floatingActionButton?.let { + binding.floatingActionButton.let { val dialogBuilder = MaterialAlertDialogBuilder(it.context) .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_warning_white)) .setTitle(R.string.nc_settings_server_eol_title) .setMessage(R.string.nc_settings_server_eol) .setCancelable(false) .setPositiveButton(R.string.nc_settings_remove_account) { _, _ -> - val otherUserExists = userManager - .scheduleUserForDeletionWithId(currentUser!!.id!!) - .blockingGet() - val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() - WorkManager.getInstance().enqueue(accountRemovalWork) - if (otherUserExists) { - finish() - startActivity(intent) - } else if (!otherUserExists) { - restartApp(this) - } + deleteUserAndRestartApp() } - .setNegativeButton(R.string.nc_cancel) { _, _ -> - if (userManager.users.blockingGet().isNotEmpty()) { - // TODO show SwitchAccount screen again when conductor is removed instead to close app - // router.pushController(RouterTransaction.with(SwitchAccountController())) - finishAffinity() - finish() - } else { - finishAffinity() - finish() - } + + if (resources!!.getBoolean(R.bool.multiaccount_support) && userManager.users.blockingGet().size > 1) { + dialogBuilder.setNegativeButton(R.string.nc_switch_account) { _, _ -> + val newFragment: DialogFragment = ChooseAccountDialogFragment.newInstance() + newFragment.show(supportFragmentManager, ChooseAccountDialogFragment.TAG) } + } + + if (resources!!.getBoolean(R.bool.multiaccount_support)) { + dialogBuilder.setNeutralButton(R.string.nc_account_chooser_add_account) { _, _ -> + val intent = Intent(this, ServerSelectionActivity::class.java) + intent.putExtra(ADD_ADDITIONAL_ACCOUNT, true) + startActivity(intent) + } + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder) val dialog = dialogBuilder.show() viewThemeUtils.platform.colorTextButtons( dialog.getButton(AlertDialog.BUTTON_POSITIVE), - dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + dialog.getButton(AlertDialog.BUTTON_NEGATIVE), + dialog.getButton(AlertDialog.BUTTON_NEUTRAL) ) } } - fun restartApp(context: Context) { - val packageManager = context.packageManager - val intent = packageManager.getLaunchIntentForPackage(context.packageName) - val componentName = intent!!.component - val mainIntent = Intent.makeRestartActivityTask(componentName) - context.startActivity(mainIntent) - Runtime.getRuntime().exit(0) - } - private fun deleteConversation(conversation: Conversation) { val data = Data.Builder() data.putLong( @@ -1613,10 +1660,10 @@ class ConversationsListActivity : } companion object { - const val TAG = "ConvListController" + private val TAG = ConversationsListActivity::class.java.simpleName const val UNREAD_BUBBLE_DELAY = 2500 const val BOTTOM_SHEET_DELAY: Long = 2500 - private const val KEY_SEARCH_QUERY = "ContactsController.searchQuery" + private const val KEY_SEARCH_QUERY = "ConversationsListActivity.searchQuery" const val SEARCH_DEBOUNCE_INTERVAL_MS = 300 const val SEARCH_MIN_CHARS = 2 const val HTTP_UNAUTHORIZED = 401 diff --git a/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java index 51428fe5df..18b739503a 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java @@ -23,10 +23,7 @@ package com.nextcloud.talk.jobs; import android.app.NotificationManager; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Build; import android.util.Log; @@ -212,17 +209,5 @@ private void deleteUser(User user) { Log.e(TAG, "error while trying to delete user", e); } } - if (userManager.getUsers().blockingGet().isEmpty()) { - restartApp(getApplicationContext()); - } - } - - public static void restartApp(Context context) { - PackageManager packageManager = context.getPackageManager(); - Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName()); - ComponentName componentName = intent.getComponent(); - Intent mainIntent = Intent.makeRestartActivityTask(componentName); - context.startActivity(mainIntent); - Runtime.getRuntime().exit(0); } } diff --git a/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt b/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt index 123b8ee10b..1ecc4769fe 100644 --- a/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt @@ -121,7 +121,7 @@ class GeocodingActivity : if (viewModel.getQuery().isNotEmpty() && adapter.itemCount == 0) { viewModel.searchLocation() } else { - Log.e(TAG, "search string that was passed to GeocodingController was null or empty") + Log.e(TAG, "search string that was passed to GeocodingActivity was null or empty") } adapter.setOnItemClickListener(object : GeocodingAdapter.OnItemClickListener { override fun onItemClick(position: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt index c44c50cff4..8b7f16478e 100644 --- a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt @@ -608,7 +608,7 @@ class ProfileActivity : BaseActivity() { class UserInfoAdapter( displayList: List?, private val viewThemeUtils: ViewThemeUtils, - private val controller: ProfileActivity + private val profileActivity: ProfileActivity ) : RecyclerView.Adapter() { var displayList: List? var filteredDisplayList: MutableList = LinkedList() @@ -643,7 +643,7 @@ class ProfileActivity : BaseActivity() { } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item: UserInfoDetailsItem = if (controller.edit) { + val item: UserInfoDetailsItem = if (profileActivity.edit) { displayList!![position] } else { filteredDisplayList[position] @@ -656,11 +656,11 @@ class ProfileActivity : BaseActivity() { holder.binding.icon.contentDescription = item.hint viewThemeUtils.platform.colorImageView(holder.binding.icon, ColorRole.PRIMARY) - if (!TextUtils.isEmpty(item.text) || controller.edit) { + if (!TextUtils.isEmpty(item.text) || profileActivity.edit) { holder.binding.userInfoDetailContainer.visibility = View.VISIBLE - controller.viewThemeUtils.material.colorTextInputLayout(holder.binding.userInfoInputLayout) - if (controller.edit && - controller.editableFields.contains(item.field.toString().lowercase()) + profileActivity.viewThemeUtils.material.colorTextInputLayout(holder.binding.userInfoInputLayout) + if (profileActivity.edit && + profileActivity.editableFields.contains(item.field.toString().lowercase()) ) { holder.binding.userInfoEditTextEdit.isEnabled = true holder.binding.userInfoEditTextEdit.isFocusableInTouchMode = true @@ -700,7 +700,7 @@ class ProfileActivity : BaseActivity() { } override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - if (controller.edit) { + if (profileActivity.edit) { displayList!![holder.adapterPosition].text = holder.binding.userInfoEditTextEdit.text.toString() } else { filteredDisplayList[holder.adapterPosition].text = @@ -739,7 +739,7 @@ class ProfileActivity : BaseActivity() { } override fun getItemCount(): Int { - return if (controller.edit) { + return if (profileActivity.edit) { displayList!!.size } else { filteredDisplayList.size @@ -762,7 +762,7 @@ class ProfileActivity : BaseActivity() { } companion object { - private const val TAG: String = "ProfileController" + private val TAG = ProfileActivity::class.java.simpleName private const val DEFAULT_CACHE_SIZE: Int = 20 private const val DEFAULT_RETRIES: Long = 3 private const val HIGH_EMPHASIS_ALPHA: Float = 0.87f diff --git a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt index 11f6c5bf39..aebd9230a7 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt @@ -91,8 +91,6 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { // However, as we are in a broadcast receiver, this needs a TaskStackBuilder // combined with addNextIntentWithParentStack. For further reading, see // https://developer.android.com/develop/ui/views/notifications/navigation#DirectEntry - // As we are using the conductor framework it might be hard the combine this or to keep an overview. - // For this reason there is only a Snackbar for now until we got rid of conductor. Snackbar.make( View(context), diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt index 1341731984..d69d48aa02 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -27,6 +27,7 @@ package com.nextcloud.talk.settings import android.animation.Animator import android.animation.AnimatorListenerAdapter +import android.annotation.SuppressLint import android.app.KeyguardManager import android.content.Context import android.content.DialogInterface @@ -51,6 +52,7 @@ import android.view.View import android.view.WindowManager import android.widget.EditText import android.widget.LinearLayout +import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.view.ContextThemeWrapper import androidx.core.content.ContextCompat @@ -66,6 +68,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity +import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.setAppTheme @@ -465,17 +468,47 @@ class SettingsActivity : BaseActivity() { } } + @SuppressLint("CheckResult") private fun removeCurrentAccount() { - val otherUserExists = userManager.scheduleUserForDeletionWithId(currentUser!!.id!!).blockingGet() + userManager.scheduleUserForDeletionWithId(currentUser!!.id!!).blockingGet() val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork) - if (otherUserExists) { - // TODO: find better solution once Conductor is removed - finish() - startActivity(intent) - } else if (!otherUserExists) { - Log.d(TAG, "No other users found. AccountRemovalWorker will restart the app.") - } + + WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id) + .observeForever { workInfo: WorkInfo -> + + when (workInfo.state) { + WorkInfo.State.SUCCEEDED -> { + val text = String.format( + context.resources.getString(R.string.nc_deleted_user), + currentUser!!.displayName + ) + Toast.makeText( + context, + text, + Toast.LENGTH_LONG + ).show() + restartApp() + } + WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> { + Toast.makeText( + context, + context.resources.getString(R.string.nc_common_error_sorry), + Toast.LENGTH_LONG + ).show() + Log.e(TAG, "something went wrong when deleting user with id " + currentUser!!.userId) + restartApp() + } + + else -> {} + } + } + } + + private fun restartApp() { + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(intent) } private fun getRingtoneName(context: Context, ringtoneUri: Uri?): String { @@ -1205,7 +1238,7 @@ class SettingsActivity : BaseActivity() { } companion object { - private const val TAG = "SettingsController" + private val TAG = SettingsActivity::class.java.simpleName private const val DURATION: Long = 2500 private const val START_DELAY: Long = 5000 private const val DISABLED_ALPHA: Float = 0.38f diff --git a/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt index 82be2318ce..305c6b1683 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt @@ -31,8 +31,8 @@ import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.nextcloud.talk.R import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.chat.ChatActivity -import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage -import com.nextcloud.talk.controllers.bottomsheet.items.listItemsWithImage +import com.nextcloud.talk.bottomsheet.items.BasicListItemWithImage +import com.nextcloud.talk.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.hovercard.HoverCardAction diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index 1e2c37cf61..4dfb482bba 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -35,7 +35,7 @@ import android.view.ViewGroup; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.nextcloud.talk.activities.MainActivity; +import com.nextcloud.talk.account.ServerSelectionActivity; import com.nextcloud.talk.adapters.items.AdvancedUserItem; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; @@ -71,7 +71,7 @@ import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import static com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ACCOUNT; +import static com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ADDITIONAL_ACCOUNT; @AutoInjector(NextcloudTalkApplication.class) public class ChooseAccountDialogFragment extends DialogFragment { @@ -185,11 +185,9 @@ private void setupListeners(User user) { // Creating listeners for quick-actions binding.currentAccount.getRoot().setOnClickListener(v -> dismiss()); - binding.addAccount.setOnClickListener(v -> { - // TODO: change this when conductor is removed - Intent intent = new Intent(getContext(), MainActivity.class); - intent.putExtra(ADD_ACCOUNT, true); + Intent intent = new Intent(getContext(), ServerSelectionActivity.class); + intent.putExtra(ADD_ADDITIONAL_ACCOUNT, true); startActivity(intent); dismiss(); }); diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index d5b9cae6e5..4e2331e317 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -84,6 +84,8 @@ object BundleKeys { const val KEY_DISMISS_RECORDING_URL = "KEY_DISMISS_RECORDING_URL" const val KEY_SHARE_RECORDING_TO_CHAT_URL = "KEY_SHARE_RECORDING_TO_CHAT_URL" const val KEY_GEOCODING_RESULT = "KEY_GEOCODING_RESULT" - const val ADD_ACCOUNT = "ADD_ACCOUNT" // temp workaround until conductor is removed + const val ADD_ADDITIONAL_ACCOUNT = "ADD_ADDITIONAL_ACCOUNT" const val SAVED_TRANSLATED_MESSAGE = "SAVED_TRANSLATED_MESSAGE" + const val KEY_REAUTHORIZE_ACCOUNT = "KEY_REAUTHORIZE_ACCOUNT" + const val KEY_PASSWORD = "KEY_PASSWORD" } diff --git a/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java b/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java index 60a5899d56..44ed423fed 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java +++ b/app/src/main/java/com/nextcloud/talk/utils/singletons/ApplicationWideMessageHolder.java @@ -39,7 +39,7 @@ public void setMessageType(@Nullable MessageType messageType) { } public enum MessageType { - WRONG_ACCOUNT, ACCOUNT_UPDATED_NOT_ADDED, ACCOUNT_SCHEDULED_FOR_DELETION, SERVER_WITHOUT_TALK, + WRONG_ACCOUNT, ACCOUNT_UPDATED_NOT_ADDED, SERVER_WITHOUT_TALK, FAILED_TO_IMPORT_ACCOUNT, ACCOUNT_WAS_IMPORTED, CALL_PASSWORD_WRONG } diff --git a/app/src/main/res/layout/controller_account_verification.xml b/app/src/main/res/layout/activity_account_verification.xml similarity index 100% rename from app/src/main/res/layout/controller_account_verification.xml rename to app/src/main/res/layout/activity_account_verification.xml diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml index 0d885922b6..f2b9361ca1 100644 --- a/app/src/main/res/layout/activity_chat.xml +++ b/app/src/main/res/layout/activity_chat.xml @@ -234,7 +234,7 @@ android:id="@+id/separator_1" android:layout_width="match_parent" android:layout_height="1dp" - android:background="@color/controller_chat_separator" /> + android:background="@color/chat_separator" /> - + + + + diff --git a/app/src/main/res/layout/controller_conversations_rv.xml b/app/src/main/res/layout/activity_conversations.xml similarity index 100% rename from app/src/main/res/layout/controller_conversations_rv.xml rename to app/src/main/res/layout/activity_conversations.xml diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 97c8575c02..5d44b0c30d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -40,8 +40,6 @@ android:windowContentOverlay="@null" app:elevation="0dp"> - - - - diff --git a/app/src/main/res/layout/controller_server_selection.xml b/app/src/main/res/layout/activity_server_selection.xml similarity index 100% rename from app/src/main/res/layout/controller_server_selection.xml rename to app/src/main/res/layout/activity_server_selection.xml diff --git a/app/src/main/res/layout/controller_generic_rv.xml b/app/src/main/res/layout/activity_switch_account.xml similarity index 57% rename from app/src/main/res/layout/controller_generic_rv.xml rename to app/src/main/res/layout/activity_switch_account.xml index 3178906d12..c917b9971a 100644 --- a/app/src/main/res/layout/controller_generic_rv.xml +++ b/app/src/main/res/layout/activity_switch_account.xml @@ -18,25 +18,34 @@ ~ along with this program. If not, see . --> - + android:layout_height="match_parent" + android:orientation="vertical" + android:animateLayoutChanges="true"> - + android:layout_height="wrap_content"> - + android:layout_height="?attr/actionBarSize" + android:background="@color/appbar" + android:theme="?attr/actionBarPopupTheme" + app:layout_scrollFlags="enterAlwaysCollapsed|noScroll" + app:navigationIconTint="@color/fontAppbar" + app:popupTheme="@style/appActionBarPopupMenu" + app:titleTextColor="@color/fontAppbar" + tools:title="@string/nc_select_an_account"> - + - + - + diff --git a/app/src/main/res/layout/controller_web_view_login.xml b/app/src/main/res/layout/activity_web_view_login.xml similarity index 100% rename from app/src/main/res/layout/controller_web_view_login.xml rename to app/src/main/res/layout/activity_web_view_login.xml diff --git a/app/src/main/res/layout/call_activity.xml b/app/src/main/res/layout/call_activity.xml index ec162f7055..2e39c606ff 100644 --- a/app/src/main/res/layout/call_activity.xml +++ b/app/src/main/res/layout/call_activity.xml @@ -28,7 +28,7 @@ @@ -83,7 +83,7 @@ android:layout_marginTop="16dp" android:text="@string/nc_call_unknown" android:textAlignment="center" - android:textColor="@color/controller_call_incomingCallTextView" + android:textColor="@color/call_incomingCallTextView" android:textSize="16sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -109,7 +109,7 @@ android:layout_below="@+id/conversationNameTextView" android:text="@string/nc_call_incoming" android:textAlignment="center" - android:textColor="@color/controller_call_incomingCallTextView" + android:textColor="@color/call_incomingCallTextView" android:textSize="16sp" /> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 7a1e8a6b85..3d4a0e342c 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -53,7 +53,7 @@ #373737 #D8D8D8 - #484848 + #484848 #2C2C2C diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5d7ec0cf37..982641554a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -61,10 +61,10 @@ #D32F2F #FF9800 #006400 - #E8E8E8 + #E8E8E8 #757575 #D5D5D5 - #E9FFFFFF + #E9FFFFFF #111111 #767676 #DBDBDB diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml index 8bf763543b..7d3e5bd5a3 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -35,7 +35,7 @@ false - false + true true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8913b73573..f60b4e5bda 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -574,6 +574,7 @@ How to translate with transifex: %1$s (%2$d) Invalid password Do you want to reauthorize or delete this account? + User %1$s was removed App is outdated The app is too old and no longer supported by this server. Please update. diff --git a/app/src/test/java/com/nextcloud/talk/utils/BundleKeysTest.kt b/app/src/test/java/com/nextcloud/talk/utils/BundleKeysTest.kt index 72b42a9f16..8bf9adda9d 100644 --- a/app/src/test/java/com/nextcloud/talk/utils/BundleKeysTest.kt +++ b/app/src/test/java/com/nextcloud/talk/utils/BundleKeysTest.kt @@ -97,7 +97,7 @@ class BundleKeysTest { assertEquals("KEY_DISMISS_RECORDING_URL", BundleKeys.KEY_DISMISS_RECORDING_URL) assertEquals("KEY_SHARE_RECORDING_TO_CHAT_URL", BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL) assertEquals("KEY_GEOCODING_RESULT", BundleKeys.KEY_GEOCODING_RESULT) - assertEquals("ADD_ACCOUNT", BundleKeys.ADD_ACCOUNT) + assertEquals("ADD_ADDITIONAL_ACCOUNT", BundleKeys.ADD_ADDITIONAL_ACCOUNT) assertEquals("SAVED_TRANSLATED_MESSAGE", BundleKeys.SAVED_TRANSLATED_MESSAGE) } } From 0634dbdda1a7ed7bca74ebef663a6396d5e4f960 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 23 Nov 2023 09:39:34 +0000 Subject: [PATCH 2/2] Analysis: update lint results to reflect reduced error/warning count Signed-off-by: github-actions --- scripts/analysis/lint-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index a0c1e5f3ef..bf24c5db3f 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 89 warnings + Lint Report: 85 warnings