diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3de7f410d..111a0dc01 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -37,6 +37,12 @@ android:label="@string/app_name" android:theme="@style/Theme.App.Starting" android:exported="true"> + + + + + + diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java index 1ebd68fa1..1a235f4c1 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java @@ -26,18 +26,13 @@ import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Resources; -import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; -import android.view.Menu; import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; import androidx.activity.OnBackPressedCallback; import androidx.annotation.ColorInt; @@ -46,7 +41,6 @@ import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.view.ActionMode; import androidx.appcompat.widget.SearchView; -import androidx.constraintlayout.widget.ConstraintLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; @@ -67,6 +61,9 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.android.common.core.utils.ecosystem.AccountReceiverCallback; +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.android.sso.AccountImporter; import com.nextcloud.android.sso.exceptions.AccountImportCancelledException; @@ -76,16 +73,16 @@ import com.nextcloud.android.sso.exceptions.TokenMismatchException; import com.nextcloud.android.sso.exceptions.UnknownErrorException; import com.nextcloud.android.sso.helper.SingleAccountHelper; +import com.owncloud.android.lib.common.utils.Log_OC; + +import org.jetbrains.annotations.NotNull; import java.net.HttpURLConnection; -import java.util.Arrays; import java.util.LinkedList; -import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; -import hct.Hct; import it.niedermann.android.util.ColorUtil; import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.NotesApplication; @@ -121,6 +118,7 @@ import it.niedermann.owncloud.notes.shared.model.NavigationCategory; import it.niedermann.owncloud.notes.shared.model.NoteClickListener; import it.niedermann.owncloud.notes.shared.util.CustomAppGlideModule; +import it.niedermann.owncloud.notes.shared.util.DisplayUtils; import it.niedermann.owncloud.notes.shared.util.NoteUtil; import it.niedermann.owncloud.notes.shared.util.ShareUtil; import it.niedermann.owncloud.notes.util.LinkHelper; @@ -133,6 +131,8 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A protected MainViewModel mainViewModel; + private EcosystemManager ecosystemManager; + private boolean gridView = true; public static final String ADAPTER_KEY_RECENT = "recent"; @@ -173,6 +173,8 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); + ecosystemManager = new EcosystemManager(this); + handleEcosystemIntent(getIntent()); this.coordinatorLayout = binding.activityNotesListView.activityNotesListView; this.swipeRefreshLayout = binding.activityNotesListView.swiperefreshlayout; this.fabCreate = binding.activityNotesListView.fabCreate; @@ -379,12 +381,20 @@ private void setupDrawerAppMenu() { } private void setupDrawerAppMenuListener() { - // Add listeners to the ecosystem items to launch the app or app-store - binding.drawerEcosystemFiles.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_FILES, mainViewModel.getCurrentAccount().getValue().getAccountName(), this)); - binding.drawerEcosystemTalk.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_TALK, mainViewModel.getCurrentAccount().getValue().getAccountName(), this)); + binding.drawerEcosystemFiles.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.FILES, getAccountName())); + binding.drawerEcosystemTalk.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.TALK, getAccountName())); binding.drawerEcosystemMore.setOnClickListener(v -> LinkHelper.INSTANCE.openAppStore("Nextcloud", true, this)); } + private String getAccountName() { + final var currentAccount = mainViewModel.getCurrentAccount().getValue(); + if (currentAccount == null) { + return null; + } + + return currentAccount.getAccountName(); + } + private void themeDrawerAppMenu(int color) { ColorStateList colorStateList = ColorStateList.valueOf(color); binding.drawerEcosystemFilesIcon.setImageTintList(colorStateList); @@ -482,6 +492,26 @@ protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { mainViewModel.restoreInstanceState(); } + private void handleEcosystemIntent(Intent intent) { + ecosystemManager.receiveAccount(intent, new AccountReceiverCallback() { + @Override + public void onAccountReceived(@NotNull String accountName) { + final var account = mainViewModel.getAccountByName(accountName); + if (account != null) { + onAccountChosen(account); + } else { + Log_OC.w(TAG, "account not found"); + DisplayUtils.showSnackMessage(MainActivity.this, R.string.account_not_found); + } + } + + @Override + public void onAccountError(@NotNull String reason) { + Log_OC.w(TAG, "handleEcosystemIntent: " + reason); + } + }); + } + private void setupToolbars() { setSupportActionBar(binding.activityNotesListView.searchToolbar); activityBinding.searchBar.homeToolbar.setOnClickListener((v) -> { @@ -754,6 +784,7 @@ protected void onNewIntent(Intent intent) { activityBinding.searchView.setQuery(intent.getStringExtra(SearchManager.QUERY), true); } super.onNewIntent(intent); + handleEcosystemIntent(intent); } @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java index bae942e5a..771cb42a4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java @@ -126,6 +126,10 @@ public LiveData getCurrentAccount() { return distinctUntilChanged(currentAccount); } + public Account getAccountByName(String accountName) { + return repo.getAccountByName(accountName); + } + public void postCurrentAccount(@NonNull Account account) { state.set(KEY_CURRENT_ACCOUNT, account); BrandingUtil.saveBrandColor(getApplication(), account.getColor()); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/util/LinkHelper.kt b/app/src/main/java/it/niedermann/owncloud/notes/util/LinkHelper.kt index 365f3d708..0c9be9e91 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/util/LinkHelper.kt +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/LinkHelper.kt @@ -10,10 +10,7 @@ package it.niedermann.owncloud.notes.util import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent -import android.net.Uri import androidx.core.net.toUri -import com.owncloud.android.lib.common.utils.Log_OC -import java.util.Locale /** * Helper class for opening Nextcloud apps if present @@ -21,35 +18,8 @@ import java.util.Locale * in case the app is not yet installed on the device. */ object LinkHelper { - const val APP_NEXTCLOUD_FILES = "com.nextcloud.client" - const val APP_NEXTCLOUD_NOTES = "it.niedermann.owncloud.notes" - const val APP_NEXTCLOUD_TALK = "com.nextcloud.talk2" - const val KEY_ACCOUNT: String = "KEY_ACCOUNT" private const val TAG = "LinkHelper" - /** - * Open specified app and, if not installed redirect to corresponding download. - * - * @param packageName of app to be opened - * @param userHash to pass in intent - */ - fun openAppOrStore( - packageName: String, - userHash: String?, - context: Context, - ) { - val intent = context.packageManager.getLaunchIntentForPackage(packageName) - if (intent != null) { - // app installed - open directly - // TODO handle null user? - intent.putExtra(KEY_ACCOUNT, userHash) - 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. @@ -66,7 +36,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" @@ -75,58 +45,4 @@ object LinkHelper { context.startActivity(intent) } } - - // 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 - */ - @Suppress("TooGenericExceptionCaught", "ReturnCount") - fun validateAndGetURL(url: String?): String? { - if (url.isNullOrBlank()) { - Log_OC.w(TAG, "Given url is null or blank") - return null - } - - return try { - val uri = url.toUri() - if (uri.scheme == null) { - return null - } - val validSchemes = listOf(HTTP, HTTPS) - if (uri.scheme in validSchemes) url else null - } catch (e: Exception) { - Log_OC.e(TAG, "Invalid URL: $url -- $e") - null - } - } - // endregion } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 17a1de26c..3dfe25173 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -520,4 +520,5 @@ Sharing Details Share expires on %1$s Dismiss + Account not found! diff --git a/build.gradle b/build.gradle index 7142dd7f4..2100c808a 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { kotlinVersion = '2.3.0' commonsVersion = '2.4.1' androidCommonsVersion = '1.1.1' - nextcloudAndroidCommonLib = "0.31.0" + nextcloudAndroidCommonLib = "3babd42636" singleSignOnVersion = "1.3.4" } repositories { diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 1ad09525e..5be15792c 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -806,6 +806,11 @@ + + + + + @@ -12760,17 +12765,14 @@ - - - - - - - - + + + + + + + + @@ -12939,6 +12941,22 @@ + + + + + + + + + + + + + + + + @@ -12971,6 +12989,14 @@ + + + + + + + + @@ -13139,6 +13165,22 @@ + + + + + + + + + + + + + + + + @@ -13175,6 +13217,14 @@ + + + + + + + + @@ -13343,6 +13393,22 @@ + + + + + + + + + + + + + + + + @@ -13375,6 +13441,14 @@ + + + + + + + +