Initial import of f7cloud-mobile native Android app.
Kotlin/Compose client for F7cloud (mail, files, talk, calendar, contacts, tasks, support). Current version: 0.5.113 (build 121).
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<application>
|
||||
<activity
|
||||
android:name="ru.f7cloud.talk.activities.MainActivity"
|
||||
tools:node="remove" />
|
||||
</application>
|
||||
</manifest>
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* F7cloud embed — sync F7cloud AuthSession into talk-android User DB.
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package ru.f7cloud.talk.f7cloud
|
||||
|
||||
import android.util.Log
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import ru.f7cloud.talk.api.NcApi
|
||||
import ru.f7cloud.talk.application.F7cloudTalkApplication
|
||||
import ru.f7cloud.talk.jobs.SignalingSettingsWorker
|
||||
import ru.f7cloud.talk.users.UserManager
|
||||
import ru.f7cloud.talk.utils.ApiUtils
|
||||
import ru.f7cloud.talk.utils.bundle.BundleKeys
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(F7cloudTalkApplication::class)
|
||||
class F7TalkUserSync {
|
||||
@Inject lateinit var userManager: UserManager
|
||||
@Inject lateinit var ncApi: NcApi
|
||||
|
||||
fun ensureUser(
|
||||
serverUrl: String,
|
||||
username: String,
|
||||
appPassword: String,
|
||||
davUserId: String?,
|
||||
): Long? {
|
||||
val baseUrl = serverUrl.trimEnd('/')
|
||||
val credentials = ApiUtils.getCredentials(username, appPassword)
|
||||
if (credentials.isNullOrBlank()) {
|
||||
Log.w(TAG, "Missing credentials for $username")
|
||||
return null
|
||||
}
|
||||
|
||||
val existingId = findExistingUserId(username, baseUrl)
|
||||
if (existingId != null && hasCapabilities(existingId)) {
|
||||
refreshTokenIfNeeded(existingId, appPassword)
|
||||
userManager.setUserAsActive(userManager.getUserWithId(existingId).blockingGet()).blockingGet()
|
||||
enqueueSignalingSettings(existingId)
|
||||
Log.d(TAG, "User already synced: $username@$baseUrl")
|
||||
return existingId
|
||||
}
|
||||
|
||||
val capabilities = ncApi
|
||||
.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl))
|
||||
.blockingFirst()
|
||||
val profile = ncApi
|
||||
.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(baseUrl))
|
||||
.blockingFirst()
|
||||
|
||||
val profileData = profile.ocs?.data
|
||||
val capsData = capabilities.ocs?.data
|
||||
if (capsData?.capabilities == null || capsData.serverVersion == null) {
|
||||
Log.w(TAG, "Capabilities response incomplete for $baseUrl")
|
||||
return null
|
||||
}
|
||||
|
||||
val displayName = profileData?.displayName?.takeIf { it.isNotBlank() }
|
||||
?: profileData?.displayNameAlt?.takeIf { it.isNotBlank() }
|
||||
?: username
|
||||
val userId = profileData?.userId?.takeIf { it.isNotBlank() }
|
||||
?: davUserId?.takeIf { it.isNotBlank() }
|
||||
?: username
|
||||
|
||||
val user = userManager.storeProfile(
|
||||
username,
|
||||
UserManager.UserAttributes(
|
||||
id = existingId,
|
||||
serverUrl = baseUrl,
|
||||
currentUser = true,
|
||||
userId = userId,
|
||||
token = appPassword,
|
||||
displayName = displayName,
|
||||
pushConfigurationState = null,
|
||||
capabilities = LoganSquare.serialize(capsData.capabilities),
|
||||
serverVersion = LoganSquare.serialize(capsData.serverVersion),
|
||||
certificateAlias = null,
|
||||
externalSignalingServer = null,
|
||||
),
|
||||
).blockingGet()
|
||||
|
||||
val internalId = user.id
|
||||
if (internalId != null) {
|
||||
enqueueSignalingSettings(internalId)
|
||||
}
|
||||
Log.d(TAG, "Synced talk user id=$internalId for $username@$baseUrl")
|
||||
return internalId
|
||||
}
|
||||
|
||||
private fun enqueueSignalingSettings(internalUserId: Long) {
|
||||
val app = F7cloudTalkApplication.sharedApplication ?: return
|
||||
val data = Data.Builder()
|
||||
.putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalUserId)
|
||||
.build()
|
||||
val work = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java)
|
||||
.setInputData(data)
|
||||
.build()
|
||||
WorkManager.getInstance(app).enqueue(work)
|
||||
Log.d(TAG, "Enqueued SignalingSettingsWorker for user $internalUserId (HPB)")
|
||||
}
|
||||
|
||||
private fun findExistingUserId(username: String, baseUrl: String): Long? =
|
||||
userManager.users.blockingGet()
|
||||
.firstOrNull { it.username == username && it.baseUrl == baseUrl && !it.scheduledForDeletion }
|
||||
?.id
|
||||
|
||||
private fun hasCapabilities(userId: Long): Boolean {
|
||||
val user = userManager.getUserWithId(userId).blockingGet()
|
||||
return user.capabilities?.spreedCapability != null
|
||||
}
|
||||
|
||||
private fun refreshTokenIfNeeded(userId: Long, token: String) {
|
||||
val user = userManager.getUserWithId(userId).blockingGet()
|
||||
if (user.token != token) {
|
||||
user.token = token
|
||||
userManager.saveUser(user).blockingGet()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "F7TalkUserSync"
|
||||
|
||||
@Volatile
|
||||
private var instance: F7TalkUserSync? = null
|
||||
|
||||
fun get(): F7TalkUserSync {
|
||||
val app = F7cloudTalkApplication.sharedApplication
|
||||
?: error("F7cloudTalkApplication not initialized")
|
||||
return instance ?: synchronized(this) {
|
||||
instance ?: F7TalkUserSync().also {
|
||||
app.componentApplication.inject(it)
|
||||
instance = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- F7cloud embed: branding (signaling/HPB comes from server capabilities). -->
|
||||
<resources>
|
||||
<string name="nc_app_name">F7cloud Talk</string>
|
||||
<string name="nc_app_product_name">F7cloud Talk</string>
|
||||
<string name="nc_server_product_name">F7cloud</string>
|
||||
<string name="nc_talk_login_scheme" translatable="false">f7</string>
|
||||
<string name="nc_import_accounts_from" translatable="false"></string>
|
||||
<string name="nc_import_account_type" translatable="false">f7cloud</string>
|
||||
<string name="osm_geocoder_contact" translatable="false">android@f7cloud.ru</string>
|
||||
<string name="nc_privacy_url" translatable="false">https://f7cloud.ru/</string>
|
||||
<bool name="hide_provider">true</bool>
|
||||
<bool name="multiaccount_support">false</bool>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user