Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tighter integration - Open in Notes Button #14497

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,337 changes: 1,337 additions & 0 deletions app/schemas/com.nextcloud.client.database.NextcloudDatabase/87.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ import com.owncloud.android.db.ProviderMeta
AutoMigration(from = 82, to = 83),
AutoMigration(from = 83, to = 84),
AutoMigration(from = 84, to = 85, spec = DatabaseMigrationUtil.DeleteColumnSpec::class),
AutoMigration(from = 85, to = 86, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class)
AutoMigration(from = 85, to = 86, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
AutoMigration(from = 86, to = 87, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class)
],
exportSchema = true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,7 @@ data class CapabilityEntity(
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT)
val filesDownloadLimitDefault: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_RECOMMENDATION)
val recommendation: Int?
val recommendation: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH)
val notesFolderPath: String?
)
73 changes: 73 additions & 0 deletions app/src/main/java/com/nextcloud/utils/LinkHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2025 ZetaTom <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.utils

import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import com.nextcloud.client.account.User
import com.owncloud.android.ui.activity.FileDisplayActivity
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"

/**
* 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<User>, 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.
*
* @param string packageName or url-encoded search string
* @param search false -> show app corresponding to packageName; true -> open search for string
*/
fun openAppStore(string: String, search: Boolean = false, context: Context) {
var suffix = (if (search) "search?q=" else "details?id=") + string
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://$suffix"))
try {
context.startActivity(intent)
} catch (activityNotFoundException1: ActivityNotFoundException) {
// all is lost: open google play store web page for app
if (!search) {
suffix = "apps/$suffix"
}
intent.setData(Uri.parse("https://play.google.com/store/$suffix"))
context.startActivity(intent)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2315,6 +2315,8 @@ private ContentValues createContentValues(String accountName, OCCapability capab

contentValues.put(ProviderTableMeta.CAPABILITIES_RECOMMENDATION, capability.getRecommendations().getValue());

contentValues.put(ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH, capability.getNotesFolderPath());

return contentValues;
}

Expand Down Expand Up @@ -2490,7 +2492,10 @@ private OCCapability createCapabilityInstance(Cursor cursor) {
capability.setForbiddenFilenameBaseNamesJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES));
capability.setFilesDownloadLimit(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT));
capability.setFilesDownloadLimitDefault(getInt(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT));

capability.setRecommendations(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_RECOMMENDATION));

capability.setNotesFolderPath(getString(cursor, ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH));
}

return capability;
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/com/owncloud/android/db/ProviderMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*/
public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 86;
public static final int DB_VERSION = 87;

private ProviderMeta() {
// No instance
Expand Down Expand Up @@ -272,6 +272,7 @@ static public class ProviderTableMeta implements BaseColumns {
public static final String CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES = "forbidden_filename_basenames";
public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT = "files_download_limit";
public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT = "files_download_limit_default";
public static final String CAPABILITIES_NOTES_FOLDER_PATH = "notes_folder_path";

//Columns of Uploads table
public static final String UPLOADS_LOCAL_PATH = "local_path";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import com.nextcloud.ui.ChooseAccountDialogFragment;
import com.nextcloud.ui.composeActivity.ComposeActivity;
import com.nextcloud.ui.composeActivity.ComposeDestination;
import com.nextcloud.utils.LinkHelper;
import com.nextcloud.utils.extensions.ViewExtensionsKt;
import com.nextcloud.utils.mdm.MDMConfig;
import com.owncloud.android.MainApp;
Expand Down Expand Up @@ -425,9 +426,9 @@ private void showTopBanner(LinearLayout banner, int primaryColor) {
LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more);
LinearLayout assistantView = banner.findViewById(R.id.drawer_ecosystem_assistant);

notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes"));
talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2"));
moreView.setOnClickListener(v -> openAppStore("Nextcloud", true));
notesView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_NOTES, getUser(), this));
talkView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_TALK, getUser(), this));
moreView.setOnClickListener(v -> LinkHelper.INSTANCE.openAppStore("Nextcloud", true, this));
assistantView.setOnClickListener(v -> {
DrawerActivity.menuItemId = Menu.NONE;
startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title);
Expand Down Expand Up @@ -459,45 +460,6 @@ private void showTopBanner(LinearLayout banner, int primaryColor) {
banner.setVisibility(View.VISIBLE);
}

/**
* Open specified app and, if not installed redirect to corresponding download.
*
* @param packageName of app to be opened
*/
private void openAppOrStore(String packageName) {
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
if (intent != null) {
// app installed - open directly
intent.putExtra(FileDisplayActivity.KEY_ACCOUNT, getUser().get().hashCode());
startActivity(intent);
} else {
// app not found - open market (Google Play Store, F-Droid, etc.)
openAppStore(packageName, false);
}
}

/**
* Open app store page of specified app or search for specified string. Will attempt to open browser when no app
* store is available.
*
* @param string packageName or url-encoded search string
* @param search false -> show app corresponding to packageName; true -> open search for string
*/
private void openAppStore(String string, boolean search) {
String suffix = (search ? "search?q=" : "details?id=") + string;
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://" + suffix));
try {
startActivity(intent);
} catch (android.content.ActivityNotFoundException activityNotFoundException1) {
// all is lost: open google play store web page for app
if (!search) {
suffix = "apps/" + suffix;
}
intent.setData(Uri.parse("https://play.google.com/store/" + suffix));
startActivity(intent);
}
}

private void setDrawerHeaderLogo(Drawable drawable, String serverName) {
ImageView imageHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_logo);
imageHeader.setImageDrawable(drawable);
Expand Down Expand Up @@ -1357,7 +1319,7 @@ protected void handleDeepLink(@NonNull Uri uri) {
findViewById(R.id.fab_main).callOnClick();
break;
case ACTION_APP_UPDATE:
openAppStore(getPackageName(), false);
LinkHelper.INSTANCE.openAppStore(getPackageName(), false, this);
break;
case OPEN_NOTIFICATIONS:
startActivity(NotificationsActivity.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.model.OCFileFilterType;
import com.nextcloud.model.OfflineOperationType;
import com.nextcloud.utils.LinkHelper;
import com.nextcloud.utils.extensions.ViewExtensionsKt;
import com.nextcloud.utils.mdm.MDMConfig;
import com.owncloud.android.MainApp;
Expand All @@ -65,6 +66,7 @@
import com.owncloud.android.lib.resources.shares.OCShare;
import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.shares.ShareeUser;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.lib.resources.tags.Tag;
import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.operations.RemoteOperationFailedException;
Expand All @@ -79,6 +81,7 @@
import com.owncloud.android.utils.FileSortOrder;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.theme.CapabilityUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils;

import java.io.File;
Expand Down Expand Up @@ -116,6 +119,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
private final String userId;
private final Activity activity;
private final AppPreferences preferences;
private final OCCapability capability;
private List<OCFile> mFiles = new ArrayList<>();
private final List<OCFile> mFilesAll = new ArrayList<>();
private final boolean hideItemOptions;
Expand All @@ -126,7 +130,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
private User user;
private final OCFileListFragmentInterface ocFileListFragmentInterface;


private OCFile currentDirectory;
private static final String TAG = OCFileListAdapter.class.getSimpleName();

Expand Down Expand Up @@ -168,6 +171,7 @@ public OCFileListAdapter(
hideItemOptions = argHideItemOptions;
this.gridView = gridView;
mStorageManager = transferServiceGetter.getStorageManager();
this.capability = CapabilityUtils.getCapability(user, activity);

if (activity instanceof FileDisplayActivity) {
((FileDisplayActivity) activity).showSortListGroup(true);
Expand Down Expand Up @@ -434,23 +438,43 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
footerViewHolder.getLoadingProgressBar().setVisibility(
ocFileListFragmentInterface.isLoading() ? View.VISIBLE : View.GONE);
} else if (holder instanceof OCFileListHeaderViewHolder headerViewHolder) {
ListHeaderBinding headerBinding = headerViewHolder.getBinding();
headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked());

String text = currentDirectory.getRichWorkspace();
PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils);
headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked());

ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesRecyclerView, shouldShowRecommendedFiles());
ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesTitle, shouldShowRecommendedFiles());
ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().allFilesTitle, shouldShowRecommendedFiles());
// hide header text if empty (server returns NBSP)
ViewExtensionsKt.setVisibleIf(headerViewHolder.getHeaderText(), text != null && !text.isBlank() && !" ".equals(text));

ViewExtensionsKt.setVisibleIf(headerBinding.recommendedFilesRecyclerView, shouldShowRecommendedFiles());
ViewExtensionsKt.setVisibleIf(headerBinding.recommendedFilesTitle, shouldShowRecommendedFiles());
ViewExtensionsKt.setVisibleIf(headerBinding.allFilesTitle, shouldShowRecommendedFiles());

if (shouldShowRecommendedFiles()) {
final var recommendedFilesRecyclerView = headerViewHolder.getBinding().recommendedFilesRecyclerView;
final var recommendedFilesRecyclerView = headerBinding.recommendedFilesRecyclerView;

final LinearLayoutManager layoutManager = new LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false);
recommendedFilesRecyclerView.setLayoutManager(layoutManager);

final var adapter = new RecommendedFilesAdapter(recommendedFiles, ocFileListDelegate, this, mStorageManager);
recommendedFilesRecyclerView.setAdapter(adapter);
}

ViewExtensionsKt.setVisibleIf(headerBinding.openIn.getRoot(), shouldShowOpenInNotes());

if (shouldShowOpenInNotes()) {
final var listHeaderOpenInBinding = headerBinding.openIn;

listHeaderOpenInBinding.infoText.setText(String.format(activity.getString(R.string.folder_best_viewed_in),
activity.getString(R.string.ecosystem_apps_notes)));

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));
}

} else {
ListViewHolder gridViewHolder = (ListViewHolder) holder;
OCFile file = getItem(position);
Expand Down Expand Up @@ -486,6 +510,12 @@ private boolean shouldShowRecommendedFiles() {
return !recommendedFiles.isEmpty() && currentDirectory.isRootDirectory();
}

private boolean shouldShowOpenInNotes() {
String notesFolderPath = capability.getNotesFolderPath();
String currentPath = currentDirectory.getDecryptedRemotePath();
return notesFolderPath != null && currentPath != null && currentPath.startsWith(notesFolderPath);
}

private void checkVisibilityOfFileFeaturesLayout(ListViewHolder holder) {
int fileFeaturesVisibility = View.GONE;
LinearLayout fileFeaturesLayout = holder.getFileFeaturesLayout();
Expand Down Expand Up @@ -806,6 +836,10 @@ public boolean shouldShowHeader() {
return true;
}

if (shouldShowOpenInNotes()) {
return true;
}

if (currentDirectory.getRichWorkspace() == null) {
return false;
}
Expand Down
34 changes: 21 additions & 13 deletions app/src/main/res/layout/list_header.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
~ SPDX-FileCopyrightText: 2019 Nextcloud GmbH
~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/headerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/standard_padding"
android:paddingStart="@dimen/standard_padding"
android:paddingTop="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
android:showDividers="none">

Expand All @@ -33,27 +33,35 @@

<TextView
android:id="@+id/recommended_files_title"
android:text="@string/recommended_files_title"
android:textSize="@dimen/large_title_text_size"
android:gravity="center|start"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/standard_margin"
android:textColor="@color/text_color" />
android:gravity="center|start"
android:text="@string/recommended_files_title"
android:textColor="@color/text_color"
android:textSize="@dimen/large_title_text_size" />

<androidx.recyclerview.widget.RecyclerView
android:layout_marginBottom="@dimen/standard_margin"
android:id="@+id/recommended_files_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/standard_margin" />

<include
android:id="@+id/open_in"
layout="@layout/list_header_open_in"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible" />

<TextView
android:id="@+id/all_files_title"
android:text="@string/drawer_item_all_files"
android:textSize="@dimen/large_title_text_size"
android:gravity="center|start"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/text_color" />
android:gravity="center|start"
android:text="@string/drawer_item_all_files"
android:textColor="@color/text_color"
android:textSize="@dimen/large_title_text_size" />

</LinearLayout>
Loading
Loading