checkedFiles = new HashSet<>();
checkedFiles.add(file);
- return onFileActionChosen(item.getItemId(), checkedFiles);
+ return onFileActionChosen(item, checkedFiles);
});
popup.show();
}
@@ -506,6 +500,11 @@ public void newPresentation() {
.show(requireActivity().getSupportFragmentManager(), DIALOG_CREATE_DOCUMENT);
}
+ @Override
+ public void onHeaderClicked() {
+ ((FileDisplayActivity) mContainerActivity).startRichWorkspacePreview(getCurrentFile());
+ }
+
/**
* Handler for multiple selection mode.
*
@@ -635,7 +634,7 @@ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Set checkedFiles = mAdapter.getCheckedItems();
- return onFileActionChosen(item.getItemId(), checkedFiles);
+ return onFileActionChosen(item, checkedFiles);
}
/**
@@ -713,7 +712,7 @@ public void onSaveInstanceState(@NonNull Bundle outState) {
}
@Override
- public void onPrepareOptionsMenu(Menu menu) {
+ public void onPrepareOptionsMenu(@NonNull Menu menu) {
Menu mMenu = menu;
if (mOriginalMenuItems.size() == 0) {
@@ -935,7 +934,7 @@ public void onItemClicked(OCFile file) {
}
} else if (file.isDown() && MimeTypeUtil.isVCard(file)) {
((FileDisplayActivity) mContainerActivity).startContactListFragment(file);
- } else if (PreviewTextFragment.canBePreviewed(file)) {
+ } else if (PreviewTextFileFragment.canBePreviewed(file)) {
((FileDisplayActivity) mContainerActivity).startTextPreview(file, false);
} else if (file.isDown()) {
if (PreviewMediaFragment.canBePreviewed(file)) {
@@ -945,6 +944,7 @@ public void onItemClicked(OCFile file) {
mContainerActivity.getFileOperationsHelper().openFile(file);
}
} else {
+ // file not downloaded, check for streaming, remote editing
User account = accountManager.getUser();
OCCapability capability = mContainerActivity.getStorageManager()
.getCapability(account.getAccountName());
@@ -953,8 +953,13 @@ public void onItemClicked(OCFile file) {
.isMediaStreamingSupported()) {
// stream media preview on >= NC14
((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true, true, true);
+ } else if (FileMenuFilter.isEditorAvailable(requireContext().getContentResolver(),
+ account.toPlatformAccount(),
+ file.getMimeType()) &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(file, getContext());
} else if (capability.getRichDocumentsMimeTypeList().contains(file.getMimeType()) &&
- android.os.Build.VERSION.SDK_INT >= RichDocumentsWebView.MINIMUM_API &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
capability.getRichDocumentsDirectEditing().isTrue()) {
mContainerActivity.getFileOperationsHelper().openFileAsRichDocument(file, getContext());
} else {
@@ -993,11 +998,11 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
/**
* Start the appropriate action(s) on the currently selected files given menu selected by the user.
*
- * @param menuId Identifier of the action menu selected by the user
+ * @param item MenuItem selected by the user
* @param checkedFiles List of files selected by the user on which the action should be performed
* @return 'true' if the menu selection started any action, 'false' otherwise.
*/
- public boolean onFileActionChosen(int menuId, Set checkedFiles) {
+ public boolean onFileActionChosen(MenuItem item, Set checkedFiles) {
if (checkedFiles.isEmpty()) {
return false;
}
@@ -1005,7 +1010,7 @@ public boolean onFileActionChosen(int menuId, Set checkedFiles) {
if (checkedFiles.size() == SINGLE_SELECTION) {
/// action only possible on a single file
OCFile singleFile = checkedFiles.iterator().next();
- switch (menuId) {
+ switch (item.getItemId()) {
case R.id.action_send_share_file: {
mContainerActivity.getFileOperationsHelper().sendShareFile(singleFile);
return true;
@@ -1018,9 +1023,27 @@ public boolean onFileActionChosen(int menuId, Set checkedFiles) {
mContainerActivity.getFileOperationsHelper().streamMediaFile(singleFile);
return true;
}
- case R.id.action_open_file_as_richdocument: {
- mContainerActivity.getFileOperationsHelper().openFileAsRichDocument(singleFile, getContext());
- return true;
+ case R.id.action_edit: {
+ Account account = ((FileActivity) mContainerActivity).getUserAccountManager()
+ .getUser().toPlatformAccount();
+
+ // should not be necessary, as menu item is filtered, but better play safe
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (FileMenuFilter.isEditorAvailable(requireContext().getContentResolver(),
+ account,
+ singleFile.getMimeType())) {
+ mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(singleFile,
+ getContext());
+ } else {
+ mContainerActivity.getFileOperationsHelper().openFileAsRichDocument(singleFile,
+ getContext());
+ }
+
+ return true;
+ } else {
+ DisplayUtils.showSnackMessage(getView(), "Not supported on older than Android 5");
+ return false;
+ }
}
case R.id.action_rename_file: {
RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(singleFile);
@@ -1050,7 +1073,7 @@ public boolean onFileActionChosen(int menuId, Set checkedFiles) {
}
/// actions possible on a batch of files
- switch (menuId) {
+ switch (item.getItemId()) {
case R.id.action_remove_file: {
RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(new ArrayList<>(checkedFiles), mActiveActionMode);
dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
index 387125c67c72..ef720682c05d 100755
--- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
+++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
@@ -67,8 +67,9 @@
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.ui.activity.ExternalSiteWebView;
import com.owncloud.android.ui.activity.FileActivity;
-import com.owncloud.android.ui.activity.RichDocumentsWebView;
+import com.owncloud.android.ui.activity.RichDocumentsEditorWebView;
import com.owncloud.android.ui.activity.ShareActivity;
+import com.owncloud.android.ui.activity.TextEditorWebView;
import com.owncloud.android.ui.dialog.SendShareDialog;
import com.owncloud.android.ui.events.EncryptionEvent;
import com.owncloud.android.ui.events.FavoriteEvent;
@@ -98,6 +99,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.core.content.FileProvider;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
@@ -278,7 +280,7 @@ public void openFile(OCFile file) {
Account account = fileActivity.getAccount();
OCCapability capability = fileActivity.getStorageManager().getCapability(account.name);
if (capability.getRichDocumentsMimeTypeList().contains(file.getMimeType()) &&
- android.os.Build.VERSION.SDK_INT >= RichDocumentsWebView.MINIMUM_API &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
capability.getRichDocumentsDirectEditing().isTrue()) {
openFileAsRichDocument(file, fileActivity);
return;
@@ -341,14 +343,33 @@ public void run() {
}
}
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void openFileAsRichDocument(OCFile file, Context context) {
- Intent collaboraWebViewIntent = new Intent(context, RichDocumentsWebView.class);
+ Intent collaboraWebViewIntent = new Intent(context, RichDocumentsEditorWebView.class);
collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Collabora");
collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_FILE, file);
collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false);
context.startActivity(collaboraWebViewIntent);
}
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public void openFileWithTextEditor(OCFile file, Context context) {
+ Intent textEditorIntent = new Intent(context, TextEditorWebView.class);
+ textEditorIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text");
+ textEditorIntent.putExtra(ExternalSiteWebView.EXTRA_FILE, file);
+ textEditorIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false);
+ context.startActivity(textEditorIntent);
+ }
+
+ public void openRichWorkspaceWithTextEditor(OCFile file, String url, Context context) {
+ Intent textEditorIntent = new Intent(context, TextEditorWebView.class);
+ textEditorIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text");
+ textEditorIntent.putExtra(ExternalSiteWebView.EXTRA_URL, url);
+ textEditorIntent.putExtra(ExternalSiteWebView.EXTRA_FILE, file);
+ textEditorIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false);
+ context.startActivity(textEditorIntent);
+ }
+
@NonNull
private Intent createOpenFileIntent(OCFile file) {
String storagePath = file.getStoragePath();
diff --git a/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java b/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java
index e4fe44d4875d..5b2fd546d57f 100644
--- a/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java
+++ b/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java
@@ -46,4 +46,6 @@ public interface OCFileListFragmentInterface {
boolean onLongItemClicked(OCFile file);
boolean isLoading();
+
+ void onHeaderClicked();
}
diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java
new file mode 100644
index 000000000000..cbed5ebef449
--- /dev/null
+++ b/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java
@@ -0,0 +1,390 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2019 Tobias Kaminsky
+ * Copyright (C) 2019 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.owncloud.android.ui.preview;
+
+import android.accounts.Account;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import com.nextcloud.client.account.UserAccountManager;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.FileMenuFilter;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
+import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.MimeTypeUtil;
+
+import org.jetbrains.annotations.NotNull;
+import org.mozilla.universalchardet.ReaderFactory;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.lang.ref.WeakReference;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Scanner;
+
+import javax.inject.Inject;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+
+public class PreviewTextFileFragment extends PreviewTextFragment {
+ private static final String EXTRA_FILE = "FILE";
+ private static final String EXTRA_ACCOUNT = "ACCOUNT";
+ private static final String TAG = PreviewTextFileFragment.class.getSimpleName();
+
+ private TextLoadAsyncTask textLoadAsyncTask;
+ private Account account;
+
+ @Inject UserAccountManager accountManager;
+
+ /**
+ * Creates an empty fragment for previews.
+ *
+ * MUST BE KEPT: the system uses it when tries to re-instantiate a fragment automatically (for instance, when the
+ * device is turned a aside).
+ *
+ * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction
+ */
+ public PreviewTextFileFragment() {
+ super();
+ account = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+
+ OCFile file = getFile();
+
+ Bundle args = getArguments();
+
+ if (file == null) {
+ file = args.getParcelable(FileDisplayActivity.EXTRA_FILE);
+ }
+
+ if (account == null) {
+ account = args.getParcelable(FileDisplayActivity.EXTRA_ACCOUNT);
+ }
+
+ if (args.containsKey(FileDisplayActivity.EXTRA_SEARCH_QUERY)) {
+ mSearchQuery = args.getString(FileDisplayActivity.EXTRA_SEARCH_QUERY);
+ }
+ mSearchOpen = args.getBoolean(FileDisplayActivity.EXTRA_SEARCH, false);
+
+ if (savedInstanceState == null) {
+ if (file == null) {
+ throw new IllegalStateException("Instanced with a NULL OCFile");
+ }
+ if (account == null) {
+ throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+ }
+ } else {
+ file = savedInstanceState.getParcelable(EXTRA_FILE);
+ account = savedInstanceState.getParcelable(EXTRA_ACCOUNT);
+ }
+
+ mHandler = new Handler();
+ setFile(file);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putParcelable(PreviewTextFileFragment.EXTRA_FILE, getFile());
+ outState.putParcelable(PreviewTextFileFragment.EXTRA_ACCOUNT, account);
+
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ void loadAndShowTextPreview() {
+ textLoadAsyncTask = new TextLoadAsyncTask(new WeakReference<>(mTextPreview));
+ textLoadAsyncTask.execute(getFile().getStoragePath());
+ }
+
+ /**
+ * Reads the file to preview and shows its contents. Too critical to be anonymous.
+ */
+ private class TextLoadAsyncTask extends AsyncTask