Skip to content
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@
tools:ignore="UnusedAttribute"
tools:replace="label, icon, theme, name, allowBackup">

<meta-data
android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/app_restrictions" />

<meta-data
android:name="android.max_aspect"
android:value="10" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ import com.blikoon.qrcodescanner.QrCodeActivity
import com.github.dhaval2404.imagepicker.util.PermissionUtil
import com.google.android.material.snackbar.Snackbar
import com.nextcloud.talk.R
import com.nextcloud.talk.account.data.LoginRepository
import com.nextcloud.talk.account.data.model.LoginCompletion
import com.nextcloud.talk.activities.BaseActivity
import com.nextcloud.talk.account.AccountVerificationActivity
import com.nextcloud.talk.appconfig.AppConfigManager
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
Expand All @@ -44,6 +48,7 @@ import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.UriUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import java.net.HttpURLConnection
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.singletons.ApplicationWideMessageHolder
Expand All @@ -69,6 +74,12 @@ class ServerSelectionActivity : BaseActivity() {
@Inject
lateinit var networkMonitor: NetworkMonitor

@Inject
lateinit var appConfigManager: AppConfigManager

@Inject
lateinit var loginRepository: LoginRepository

private var statusQueryDisposable: Disposable? = null

private val onBackPressedCallback = object : OnBackPressedCallback(true) {
Expand All @@ -92,6 +103,21 @@ class ServerSelectionActivity : BaseActivity() {
initSystemBars()

onBackPressedDispatcher.addCallback(this, onBackPressedCallback)

if (savedInstanceState == null) {
val managedBaseUrl = appConfigManager.getServerBaseUrl()
when {
!managedBaseUrl.isNullOrBlank() -> {
binding.serverEntryTextInputEditText.setText(managedBaseUrl)
checkServerAndProceed()
}

!TextUtils.isEmpty(resources!!.getString(R.string.weblogin_url)) -> {
binding.serverEntryTextInputEditText.setText(resources!!.getString(R.string.weblogin_url))
checkServerAndProceed()
}
}
}
}

override fun onResume() {
Expand Down Expand Up @@ -127,10 +153,7 @@ class ServerSelectionActivity : BaseActivity() {
}

binding.serverEntryTextInputEditText.requestFocus()
if (!TextUtils.isEmpty(resources!!.getString(R.string.weblogin_url))) {
binding.serverEntryTextInputEditText.setText(resources!!.getString(R.string.weblogin_url))
checkServerAndProceed()
}

binding.serverEntryTextInputEditText.setOnEditorActionListener { _: TextView?, i: Int, _: KeyEvent? ->
if (i == EditorInfo.IME_ACTION_DONE) {
checkServerAndProceed()
Expand Down Expand Up @@ -349,8 +372,27 @@ class ServerSelectionActivity : BaseActivity() {
}
}
} else {
val baseUrl = queryUrl.replace("/status.php", "")
val managedUsername = appConfigManager.getUsername()
val managedPassword = appConfigManager.getAppPassword()
if (!managedUsername.isNullOrBlank() && !managedPassword.isNullOrBlank()) {
val loginCompletion = LoginCompletion(
HttpURLConnection.HTTP_OK,
baseUrl,
managedUsername,
managedPassword
)
val managedBundle = loginRepository.parseAndLogin(loginCompletion)
if (managedBundle != null) {
val intent = Intent(context, AccountVerificationActivity::class.java)
intent.putExtras(managedBundle)
startActivity(intent)
return@runOnUiThread
}
}

val bundle = Bundle()
bundle.putString(BundleKeys.KEY_BASE_URL, queryUrl.replace("/status.php", ""))
bundle.putString(BundleKeys.KEY_BASE_URL, baseUrl)

val intent = Intent(context, BrowserLoginActivity::class.java)
intent.putExtras(bundle)
Expand Down
37 changes: 37 additions & 0 deletions app/src/main/java/com/nextcloud/talk/appconfig/AppConfigManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.appconfig

import android.content.RestrictionsManager
import android.content.Context
import androidx.annotation.VisibleForTesting
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class AppConfigManager @Inject constructor(private val context: Context) {

fun getServerBaseUrl(): String? = getString(KEY_SERVER_BASE_URL)?.takeIf { it.isNotBlank() }

fun getUsername(): String? = getString(KEY_USERNAME)?.takeIf { it.isNotBlank() }

fun getAppPassword(): String? = getString(KEY_APP_PASSWORD)?.takeIf { it.isNotBlank() }

@VisibleForTesting
internal fun getString(key: String): String? {
val restrictionsManager = context.getSystemService(Context.RESTRICTIONS_SERVICE) as? RestrictionsManager
?: return null
val restrictions = restrictionsManager.applicationRestrictions
return restrictions?.getString(key)
}

companion object {
const val KEY_SERVER_BASE_URL = "nextcloud_url"
const val KEY_USERNAME = "nextcloud_username"
const val KEY_APP_PASSWORD = "nextcloud_password"
}
}
8 changes: 8 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -996,4 +996,12 @@ How to translate with transifex:
<string name="turn_on_background_blur">Turn on background blur</string>
<string name="turn_off_background_blur">Turn off background blur</string>
<string name="nc_no_account_found">Account not found</string>

<!-- Managed configurations -->
<string name="nc_restriction_server_url_title">Server URL</string>
<string name="nc_restriction_server_url_description">Nextcloud server address that should be used by default.</string>
<string name="nc_restriction_username_title">Username</string>
<string name="nc_restriction_username_description">Username that should be used by default.</string>
<string name="nc_restriction_app_password_title">App password</string>
<string name="nc_restriction_app_password_description">App-specific password or token for the user.</string>
</resources>
29 changes: 29 additions & 0 deletions app/src/main/res/xml/app_restrictions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk - Android Client
~
~ SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
~ SPDX-License-Identifier: GPL-3.0-or-later
-->
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
<restriction
android:defaultValue=""
android:description="@string/nc_restriction_server_url_description"
android:key="nextcloud_url"
android:restrictionType="string"
android:title="@string/nc_restriction_server_url_title" />

<restriction
android:defaultValue=""
android:description="@string/nc_restriction_username_description"
android:key="nextcloud_username"
android:restrictionType="string"
android:title="@string/nc_restriction_username_title" />

<restriction
android:defaultValue=""
android:description="@string/nc_restriction_app_password_description"
android:key="nextcloud_password"
android:restrictionType="string"
android:title="@string/nc_restriction_app_password_title" />
</restrictions>
Loading