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 @@ + + + + + + + +