diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a665df2c229a..66e67a745f93 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -504,8 +504,9 @@ dependencies {
"gplayImplementation"(libs.bundles.gplay)
// endregion
- // region UI
+ // region common
implementation(libs.ui)
+ implementation(libs.common.core)
// endregion
// region Image loading
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6a210d519cb1..b9589dd3854c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -147,6 +147,13 @@
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/Theme.ownCloud.Launcher">
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/nextcloud/utils/LinkHelper.kt b/app/src/main/java/com/nextcloud/utils/LinkHelper.kt
index 294c98d7b748..75ee63098667 100644
--- a/app/src/main/java/com/nextcloud/utils/LinkHelper.kt
+++ b/app/src/main/java/com/nextcloud/utils/LinkHelper.kt
@@ -10,53 +10,17 @@ package com.nextcloud.utils
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
-import android.net.Uri
import androidx.core.net.toUri
-import com.nextcloud.client.account.User
import com.owncloud.android.lib.common.utils.Log_OC
-import com.owncloud.android.ui.activity.FileDisplayActivity
import java.util.Locale
-import java.util.Optional
-import kotlin.jvm.optionals.getOrNull
object LinkHelper {
- const val APP_NEXTCLOUD_NOTES = "it.niedermann.owncloud.notes"
- const val APP_NEXTCLOUD_TALK = "com.nextcloud.talk2"
private const val TAG = "LinkHelper"
fun isHttpOrHttpsLink(link: String?): Boolean = link?.lowercase(Locale.getDefault())?.let {
it.startsWith("http://") || it.startsWith("https://")
} == true
- /**
- * Open specified app and, if not installed redirect to corresponding download.
- *
- * @param packageName of app to be opened
- * @param user to pass in intent
- */
- fun openAppOrStore(packageName: String, user: Optional, context: Context) {
- openAppOrStore(packageName, user.getOrNull(), context)
- }
-
- /**
- * Open specified app and, if not installed redirect to corresponding download.
- *
- * @param packageName of app to be opened
- * @param user to pass in intent
- */
- fun openAppOrStore(packageName: String, user: User?, context: Context) {
- val intent = context.packageManager.getLaunchIntentForPackage(packageName)
- if (intent != null) {
- // app installed - open directly
- // TODO handle null user?
- intent.putExtra(FileDisplayActivity.KEY_ACCOUNT, user.hashCode())
- context.startActivity(intent)
- } else {
- // app not found - open market (Google Play Store, F-Droid, etc.)
- openAppStore(packageName, false, context)
- }
- }
-
/**
* Open app store page of specified app or search for specified string. Will attempt to open browser when no app
* store is available.
@@ -69,7 +33,7 @@ object LinkHelper {
val intent = Intent(Intent.ACTION_VIEW, "market://$suffix".toUri())
try {
context.startActivity(intent)
- } catch (activityNotFoundException1: ActivityNotFoundException) {
+ } catch (_: ActivityNotFoundException) {
// all is lost: open google play store web page for app
if (!search) {
suffix = "apps/$suffix"
@@ -82,32 +46,6 @@ object LinkHelper {
// region Validation
private const val HTTP = "http"
private const val HTTPS = "https"
- private const val FILE = "file"
- private const val CONTENT = "content"
-
- /**
- * Validates if a string can be converted to a valid URI
- */
- @Suppress("TooGenericExceptionCaught", "ReturnCount")
- fun validateAndGetURI(uriString: String?): Uri? {
- if (uriString.isNullOrBlank()) {
- Log_OC.w(TAG, "Given uriString is null or blank")
- return null
- }
-
- return try {
- val uri = uriString.toUri()
- if (uri.scheme == null) {
- return null
- }
-
- val validSchemes = listOf(HTTP, HTTPS, FILE, CONTENT)
- if (uri.scheme in validSchemes) uri else null
- } catch (e: Exception) {
- Log_OC.e(TAG, "Invalid URI string: $uriString -- $e")
- null
- }
- }
/**
* Validates if a URL string is valid
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
index 29849c71f0c5..13d4cdca534a 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
+++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
@@ -49,6 +49,8 @@
import com.google.android.material.button.MaterialButton;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.progressindicator.LinearProgressIndicator;
+import com.nextcloud.android.common.core.utils.ecosystem.EcosystemApp;
+import com.nextcloud.android.common.core.utils.ecosystem.EcosystemManager;
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
import com.nextcloud.client.account.User;
import com.nextcloud.client.di.Injectable;
@@ -205,6 +207,8 @@ public abstract class DrawerActivity extends ToolbarActivity
private BottomNavigationView bottomNavigationView;
+ private EcosystemManager ecosystemManager;
+
@Inject
AppPreferences preferences;
@@ -429,8 +433,13 @@ private void showTopBanner(ConstraintLayout banner) {
LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more);
LinearLayout assistantView = banner.findViewById(R.id.drawer_ecosystem_assistant);
- notesView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_NOTES, getUser(), this));
- talkView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_TALK, getUser(), this));
+ final var optionalUser = getUser();
+ if (optionalUser.isPresent()) {
+ final var accountName = optionalUser.get().getAccountName();
+ notesView.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.NOTES, accountName));
+ talkView.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.TALK, accountName));
+ }
+
moreView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppStore("Nextcloud", true, this));
assistantView.setOnClickListener(v -> {
DrawerActivity.menuItemId = Menu.NONE;
@@ -727,6 +736,10 @@ private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) {
startActivity(intent);
}
+ public EcosystemManager getEcosystemManager() {
+ return ecosystemManager;
+ }
+
/**
* sets the new/current account and restarts. In case the given account equals the actual/current account the call
* will be ignored.
@@ -1136,6 +1149,7 @@ protected void onCreate(Bundle savedInstanceState) {
externalLinksProvider = new ExternalLinksProvider(getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
+ ecosystemManager = new EcosystemManager(this);
}
@Override
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt
index cd383b4241da..cc27ee6c40d7 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt
@@ -52,6 +52,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
+import com.nextcloud.android.common.core.utils.ecosystem.AccountReceiverCallback
import com.nextcloud.appReview.InAppReviewHelper
import com.nextcloud.client.account.User
import com.nextcloud.client.appinfo.AppInfo
@@ -256,6 +257,7 @@ class FileDisplayActivity :
intent?.let {
handleCommonIntents(it)
+ handleEcosystemIntent(it)
}
loadSavedInstanceState(savedInstanceState)
@@ -547,6 +549,7 @@ class FileDisplayActivity :
handleCommonIntents(intent)
handleSpecialIntents(intent)
handleRestartIntent(intent)
+ handleEcosystemIntent(intent)
}
private fun handleSpecialIntents(intent: Intent) {
@@ -3073,6 +3076,28 @@ class FileDisplayActivity :
})
}
+ private fun handleEcosystemIntent(intent: Intent?) {
+ ecosystemManager.receiveAccount(
+ intent,
+ object : AccountReceiverCallback {
+ override fun onAccountReceived(accountName: String) {
+ val account = accountManager.getUser(accountName).orElse(null)
+ ?: run {
+ Log_OC.w(TAG, "user is not present")
+ DisplayUtils.showSnackMessage(this@FileDisplayActivity, R.string.account_not_found)
+ return
+ }
+
+ accountClicked(account)
+ }
+
+ override fun onAccountError(reason: String) {
+ Log_OC.w(TAG, "handleEcosystemIntent: $reason")
+ }
+ }
+ )
+ }
+
// region MetadataSyncJob
private fun startMetadataSyncForRoot() {
backgroundJobManager.startMetadataSyncJob(OCFile.ROOT_PATH)
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
index 08f690ccbe93..603a4f864ec0 100644
--- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
@@ -26,13 +26,13 @@
import com.elyeproj.loaderviewlibrary.LoaderImageView;
import com.google.android.material.chip.Chip;
+import com.nextcloud.android.common.core.utils.ecosystem.EcosystemApp;
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
import com.nextcloud.client.account.User;
import com.nextcloud.client.database.entity.OfflineOperationEntity;
import com.nextcloud.client.jobs.upload.FileUploadHelper;
import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.model.OfflineOperationType;
-import com.nextcloud.utils.LinkHelper;
import com.nextcloud.utils.extensions.OCFileExtensionsKt;
import com.nextcloud.utils.extensions.ViewExtensionsKt;
import com.nextcloud.utils.mdm.MDMConfig;
@@ -55,6 +55,7 @@
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.lib.resources.tags.Tag;
import com.owncloud.android.ui.activity.ComponentsGetter;
+import com.owncloud.android.ui.activity.DrawerActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.adapter.helper.OCFileListAdapterDataProvider;
import com.owncloud.android.ui.adapter.helper.OCFileListAdapterHelper;
@@ -449,7 +450,12 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
listHeaderOpenInBinding.openInButton.setText(String.format(activity.getString(R.string.open_in_app),
activity.getString(R.string.ecosystem_apps_display_notes)));
- listHeaderOpenInBinding.openInButton.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_NOTES, user, activity));
+ if (activity instanceof DrawerActivity drawerActivity) {
+ final var ecosystemManager = drawerActivity.getEcosystemManager();
+ if (ecosystemManager != null) {
+ listHeaderOpenInBinding.openInButton.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.NOTES, user.getAccountName()));
+ }
+ }
}
} else {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ad93c15c00dd..c01d38c6305b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
[versions]
-androidCommonLibraryVersion = "0.31.0"
+androidCommonLibraryVersion = "30a17d42f4"
androidGifDrawableVersion = "1.2.30"
androidImageCropperVersion = "4.7.0"
androidLibraryVersion = "c112fd86c76f429db250e6abca711348e5534c0a"
@@ -232,6 +232,7 @@ prism4j-bundler = { module = "io.noties:prism4j-bundler", version.ref = "prismVe
# Nextcloud libraries
ui = { module = "com.github.nextcloud.android-common:ui", version.ref = "androidCommonLibraryVersion" }
+common-core = { module = "com.github.nextcloud.android-common:core", version.ref = "androidCommonLibraryVersion" }
qrcodescanner = { module = "com.github.nextcloud-deps:qrcodescanner", version.ref = "qrcodescannerVersion" }
# Worker
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 3ece291a172e..475880d21ff4 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -1449,6 +1449,11 @@
+
+
+
+
+
@@ -20728,6 +20733,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -20760,6 +20781,14 @@
+
+
+
+
+
+
+
+
@@ -20908,6 +20937,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -20940,6 +20985,14 @@
+
+
+
+
+
+
+
+
@@ -21084,6 +21137,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -21116,6 +21185,14 @@
+
+
+
+
+
+
+
+