diff --git a/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java b/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java deleted file mode 100644 index a50be70f4b0c..000000000000 --- a/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Nextcloud Android client application - * - * Copyright (C) 2017 Tobias Kaminsky - * Copyright (C) 2017 Nextcloud. - * - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.datamodel; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; - -import com.owncloud.android.db.ProviderMeta; -import com.owncloud.android.lib.common.ExternalLink; -import com.owncloud.android.lib.common.ExternalLinkType; -import com.owncloud.android.lib.common.utils.Log_OC; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.NonNull; - -/** - * Database provider for handling the persistence aspects of {@link com.owncloud.android.lib.common.ExternalLink}s. - */ - -public class ExternalLinksProvider { - static private final String TAG = ExternalLinksProvider.class.getSimpleName(); - - private ContentResolver mContentResolver; - - public ExternalLinksProvider(ContentResolver contentResolver) { - if (contentResolver == null) { - throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver"); - } - mContentResolver = contentResolver; - } - - /** - * Stores an external link in database. - * - * @param externalLink object to store - */ - public void storeExternalLink(ExternalLink externalLink) { - Log_OC.v(TAG, "Adding " + externalLink.getName()); - - ContentValues cv = createContentValuesFromExternalLink(externalLink); - - Uri result = mContentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, cv); - - if (result == null) { - Log_OC.e(TAG, "Failed to insert item " + externalLink.getName() + " into external link db."); - } - } - - /** - * Delete all external links from the db - */ - public void deleteAllExternalLinks() { - mContentResolver.delete(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, - null, - null); - } - - /** - * get by type external links. - * - * @return external links, empty if none exists - */ - public List getExternalLink(ExternalLinkType type) { - Cursor cursor = mContentResolver.query( - ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, - null, - "type = ?", - new String[]{type.toString()}, - null - ); - - if (cursor != null) { - List list = new ArrayList<>(); - if (cursor.moveToFirst()) { - do { - ExternalLink externalLink = createExternalLinkFromCursor(cursor); - if (externalLink == null) { - Log_OC.e(TAG, "ExternalLink could not be created from cursor"); - } else { - list.add(externalLink); - } - } while (cursor.moveToNext()); - - } - cursor.close(); - return list; - } else { - Log_OC.e(TAG, "DB error restoring externalLinks."); - } - - return new ArrayList<>(); - } - - /** - * create ContentValues object based on given externalLink. - * - * @param externalLink the external Link - * @return the corresponding ContentValues object - */ - @NonNull - private ContentValues createContentValuesFromExternalLink(ExternalLink externalLink) { - ContentValues cv = new ContentValues(); - cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_ICON_URL, externalLink.getIconUrl()); - cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE, externalLink.getLanguage()); - cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE, externalLink.getType().toString()); - cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_NAME, externalLink.getName()); - cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_URL, externalLink.getUrl()); - cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_REDIRECT, externalLink.getRedirect()); - return cv; - } - - /** - * cursor to externalLink - * - * @param cursor db cursor - * @return externalLink, null if cursor is null - */ - private ExternalLink createExternalLinkFromCursor(Cursor cursor) { - ExternalLink externalLink = null; - if (cursor != null) { - int id = cursor.getInt(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta._ID)); - String iconUrl = cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_ICON_URL)); - String language = cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE)); - ExternalLinkType type = switch (cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE))) { - case "link" -> ExternalLinkType.LINK; - case "settings" -> ExternalLinkType.SETTINGS; - case "quota" -> ExternalLinkType.QUOTA; - default -> ExternalLinkType.UNKNOWN; - }; - String name = cursor.getString(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_NAME)); - String url = cursor.getString(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_URL)); - boolean redirect = cursor.getInt( - cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_REDIRECT)) == 1; - - externalLink = new ExternalLink(id, iconUrl, language, type, name, url, redirect); - } - return externalLink; - } -} diff --git a/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.kt b/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.kt new file mode 100644 index 000000000000..635844c6cfa3 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.kt @@ -0,0 +1,108 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.datamodel + +import android.content.ContentResolver +import android.content.ContentValues +import android.database.Cursor +import com.owncloud.android.db.ProviderMeta +import com.owncloud.android.lib.common.ExternalLink +import com.owncloud.android.lib.common.ExternalLinkType +import com.owncloud.android.lib.common.utils.Log_OC +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class ExternalLinksProvider(private val contentResolver: ContentResolver) { + private val ioScope = CoroutineScope(Dispatchers.IO) + + fun storeExternalLink(externalLink: ExternalLink) { + ioScope.launch { + Log_OC.v(TAG, "Adding " + externalLink.name) + val cv = createContentValuesFromExternalLink(externalLink) + contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, cv) + ?: Log_OC.e(TAG, "Failed to insert ${externalLink.name} into external link db.") + } + } + + fun deleteAllExternalLinks() { + ioScope.launch { + contentResolver.delete( + ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, + null, + null + ) + } + } + + fun getExternalLink(type: ExternalLinkType, onComplete: (List) -> Unit) { + ioScope.launch { + val cursor = contentResolver.query( + ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, + null, + "type = ?", + arrayOf(type.toString()), + null + ) ?: run { + Log_OC.e(TAG, "DB error restoring externalLinks.") + withContext(Dispatchers.Main) { + onComplete(emptyList()) + } + return@launch + } + + val result = cursor.use { c -> + generateSequence { if (c.moveToNext()) c else null } + .map { createExternalLinkFromCursor(it) } + .toList() + } + + return@launch withContext(Dispatchers.Main) { + onComplete(result) + } + } + } + + private fun createContentValuesFromExternalLink(externalLink: ExternalLink): ContentValues = ContentValues().apply { + put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_ICON_URL, externalLink.iconUrl) + put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE, externalLink.language) + put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE, externalLink.type.toString()) + put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_NAME, externalLink.name) + put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_URL, externalLink.url) + put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_REDIRECT, externalLink.redirect) + } + + private fun createExternalLinkFromCursor(cursor: Cursor): ExternalLink { + fun col(name: String) = cursor.getColumnIndexOrThrow(name) + val typeStr = cursor.getString(col(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE)) + + return ExternalLink( + id = cursor.getInt(col(ProviderMeta.ProviderTableMeta._ID)), + iconUrl = cursor.getString(col(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_ICON_URL)), + language = cursor.getString(col(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE)), + type = when (typeStr) { + "link" -> ExternalLinkType.LINK + "settings" -> ExternalLinkType.SETTINGS + "quota" -> ExternalLinkType.QUOTA + else -> ExternalLinkType.UNKNOWN + }, + name = cursor.getString(col(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_NAME)), + url = cursor.getString(col(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_URL)), + redirect = cursor.getInt(col(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_REDIRECT)) == 1 + ) + } + + fun cleanup() { + ioScope.cancel() + } + + companion object { + private val TAG: String = ExternalLinksProvider::class.java.getSimpleName() + } +} 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 f95db44aea4d..8a151e88aa58 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 @@ -129,6 +129,7 @@ import androidx.fragment.app.Fragment; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hct.Hct; +import kotlin.Unit; /** * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback @@ -804,10 +805,19 @@ public void accountClicked(User user) { } private void externalLinkClicked(MenuItem menuItem) { - for (ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) { - if (menuItem.getTitle().toString().equalsIgnoreCase(link.getName())) { + externalLinksProvider.getExternalLink(ExternalLinkType.LINK, externalLinks -> { + for (ExternalLink link : externalLinks) { + final var menuTitle = menuItem.getTitle(); + if (menuTitle == null) { + continue; + } + + if (!menuTitle.toString().equalsIgnoreCase(link.getName())) { + continue; + } + if (link.getRedirect()) { - DisplayUtils.startLinkIntent(this, link.getUrl()); + DisplayUtils.startLinkIntent(DrawerActivity.this, link.getUrl()); } else { Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.getName()); @@ -816,7 +826,8 @@ private void externalLinkClicked(MenuItem menuItem) { startActivity(externalWebViewIntent); } } - } + return Unit.INSTANCE; + }); } /** @@ -940,39 +951,43 @@ private void setQuotaInformation(long usedSpace, long totalSpace, int relative, } private void updateQuotaLink() { - if (mQuotaTextLink != null) { - if (MDMConfig.INSTANCE.externalSiteSupport(this)) { - List quotas = externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA); - - float density = getResources().getDisplayMetrics().density; - final int size = Math.round(24 * density); - - if (!quotas.isEmpty()) { - final ExternalLink firstQuota = quotas.get(0); - mQuotaTextLink.setText(firstQuota.getName()); - mQuotaTextLink.setClickable(true); - mQuotaTextLink.setVisibility(View.VISIBLE); - mQuotaTextLink.setOnClickListener(v -> { - Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.getName()); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.getUrl()); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true); - startActivity(externalWebViewIntent); - }); + if (mQuotaTextLink == null) { + return; + } - Target quotaTarget = createQuotaDrawableTarget(size, mQuotaTextLink); - GlideHelper.INSTANCE.loadIntoTarget(this, - accountManager.getCurrentOwnCloudAccount(), - firstQuota.getIconUrl(), - quotaTarget, - R.drawable.ic_link); - } else { - mQuotaTextLink.setVisibility(View.GONE); - } - } else { + if (!MDMConfig.INSTANCE.externalSiteSupport(this)) { + mQuotaTextLink.setVisibility(View.GONE); + return; + } + + externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA, quotas -> { + float density = getResources().getDisplayMetrics().density; + final int size = Math.round(24 * density); + if (quotas.isEmpty()) { mQuotaTextLink.setVisibility(View.GONE); + return Unit.INSTANCE; } - } + + final ExternalLink firstQuota = quotas.get(0); + mQuotaTextLink.setText(firstQuota.getName()); + mQuotaTextLink.setClickable(true); + mQuotaTextLink.setVisibility(View.VISIBLE); + mQuotaTextLink.setOnClickListener(v -> { + Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.getName()); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.getUrl()); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true); + startActivity(externalWebViewIntent); + }); + + Target quotaTarget = createQuotaDrawableTarget(size, mQuotaTextLink); + GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this, + accountManager.getCurrentOwnCloudAccount(), + firstQuota.getIconUrl(), + quotaTarget, + R.drawable.ic_link); + return Unit.INSTANCE; + }); } private Target createQuotaDrawableTarget(int size, TextView quotaTextLink) { @@ -1077,25 +1092,28 @@ private void updateExternalLinksInDrawer() { drawerNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links); int greyColor = ContextCompat.getColor(this, R.color.drawer_menu_icon); - - for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) { - int id = drawerNavigationView - .getMenu() - .add(R.id.drawer_menu_external_links, - MENU_ITEM_EXTERNAL_LINK + - link.getId(), MENU_ORDER_EXTERNAL_LINKS, - link.getName() - ) - .setCheckable(true) - .getItemId(); - - Target iconTarget = createMenuItemTarget(id, greyColor); - GlideHelper.INSTANCE.loadIntoTarget(this, - accountManager.getCurrentOwnCloudAccount(), - link.getIconUrl(), - iconTarget, - R.drawable.ic_link); - } + externalLinksProvider.getExternalLink(ExternalLinkType.LINK, externalLinks -> { + for (final ExternalLink link : externalLinks) { + int id = drawerNavigationView + .getMenu() + .add(R.id.drawer_menu_external_links, + MENU_ITEM_EXTERNAL_LINK + + link.getId(), + MENU_ORDER_EXTERNAL_LINKS, + link.getName() + ) + .setCheckable(true) + .getItemId(); + + Target iconTarget = createMenuItemTarget(id, greyColor); + GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this, + accountManager.getCurrentOwnCloudAccount(), + link.getIconUrl(), + iconTarget, + R.drawable.ic_link); + } + return Unit.INSTANCE; + }); } private Target createMenuItemTarget(int menuItemId, int tintColor) { @@ -1144,6 +1162,11 @@ protected void onCreate(Bundle savedInstanceState) { ecosystemManager = new EcosystemManager(this); } + @Override + protected void onDestroy() { + externalLinksProvider.cleanup(); + super.onDestroy(); + } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index 0bb5a9243615..5018c9f45e89 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -78,6 +78,7 @@ import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; +import java.util.List; import java.util.Objects; import javax.inject.Inject; @@ -89,6 +90,8 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; import static com.owncloud.android.ui.activity.DrawerActivity.REQ_ALL_FILES_ACCESS; @@ -1156,14 +1159,16 @@ private AppCompatDelegate getDelegate() { } private void loadExternalSettingLinks(PreferenceCategory preferenceCategory) { - if (MDMConfig.INSTANCE.externalSiteSupport(this)) { - ExternalLinksProvider externalLinksProvider = new ExternalLinksProvider(getContentResolver()); - - for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.SETTINGS)) { + if (!MDMConfig.INSTANCE.externalSiteSupport(this)) { + return; + } + ExternalLinksProvider externalLinksProvider = new ExternalLinksProvider(getContentResolver()); + externalLinksProvider.getExternalLink(ExternalLinkType.SETTINGS, externalLinks -> { + for (final ExternalLink link : externalLinks) { // only add if it does not exist, in case activity is reused if (findPreference(String.valueOf(link.getId())) == null) { - Preference p = new Preference(this); + Preference p = new Preference(SettingsActivity.this); p.setTitle(link.getName()); p.setKey(String.valueOf(link.getId())); @@ -1180,7 +1185,9 @@ private void loadExternalSettingLinks(PreferenceCategory preferenceCategory) { preferenceCategory.addPreference(p); } } - } + return Unit.INSTANCE; + }); + externalLinksProvider.cleanup(); } /**