Merge pull request #10148 from nextcloud/proseMirror

Test proseMirror with custom user agent
This commit is contained in:
Tobias Kaminsky 2022-05-25 15:40:54 +02:00 committed by GitHub
commit c1cf416872
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 95 additions and 69 deletions

View file

@ -22,24 +22,20 @@
package com.owncloud.android.files; package com.owncloud.android.files;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import com.google.gson.Gson;
import com.nextcloud.android.files.FileLockingHelper; import com.nextcloud.android.files.FileLockingHelper;
import com.nextcloud.client.account.User; import com.nextcloud.client.account.User;
import com.owncloud.android.R; import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
import com.owncloud.android.lib.common.DirectEditing;
import com.owncloud.android.lib.common.Editor;
import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
import com.owncloud.android.ui.activity.ComponentsGetter; import com.owncloud.android.ui.activity.ComponentsGetter;
import com.owncloud.android.utils.EditorUtils;
import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.NextcloudServer; import com.owncloud.android.utils.NextcloudServer;
@ -49,8 +45,6 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import androidx.annotation.Nullable;
/** /**
* Filters out the file actions available in a given {@link Menu} for a given {@link OCFile} * Filters out the file actions available in a given {@link Menu} for a given {@link OCFile}
* according to the current state of the latest. * according to the current state of the latest.
@ -352,44 +346,16 @@ public class FileMenuFilter {
String mimeType = files.iterator().next().getMimeType(); String mimeType = files.iterator().next().getMimeType();
if (isRichDocumentEditingSupported(capability, mimeType) || isEditorAvailable(context.getContentResolver(), if (isRichDocumentEditingSupported(capability, mimeType) ||
user, EditorUtils.isEditorAvailable(context.getContentResolver(),
mimeType)) { user,
mimeType)) {
toShow.add(R.id.action_edit); toShow.add(R.id.action_edit);
} else { } else {
toHide.add(R.id.action_edit); toHide.add(R.id.action_edit);
} }
} }
public static boolean isEditorAvailable(ContentResolver contentResolver, User user, String mimeType) {
return getEditor(contentResolver, user, mimeType) != null;
}
@Nullable
public static Editor getEditor(ContentResolver contentResolver, User user, String mimeType) {
String json = new ArbitraryDataProvider(contentResolver).getValue(user, ArbitraryDataProvider.DIRECT_EDITING);
if (json.isEmpty()) {
return null;
}
DirectEditing directEditing = new Gson().fromJson(json, DirectEditing.class);
for (Editor editor : directEditing.getEditors().values()) {
if (editor.getMimetypes().contains(mimeType)) {
return editor;
}
}
for (Editor editor : directEditing.getEditors().values()) {
if (editor.getOptionalMimetypes().contains(mimeType)) {
return editor;
}
}
return null;
}
/** /**
* This will be replaced by unified editor and can be removed once EOL of corresponding server version. * This will be replaced by unified editor and can be removed once EOL of corresponding server version.
*/ */

View file

@ -179,7 +179,7 @@ public class ExternalSiteWebView extends FileActivity {
webSettings.setLoadWithOverviewMode(true); webSettings.setLoadWithOverviewMode(true);
// user agent // user agent
webSettings.setUserAgentString(MainApp.getUserAgent()); setUserAgentString(webSettings);
// no private data storing // no private data storing
webSettings.setSavePassword(false); webSettings.setSavePassword(false);
@ -199,6 +199,10 @@ public class ExternalSiteWebView extends FileActivity {
} }
} }
protected void setUserAgentString(WebSettings webSettings) {
webSettings.setUserAgentString(MainApp.getUserAgent());
}
private void setupActionBar(String title) { private void setupActionBar(String title) {
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {

View file

@ -23,14 +23,15 @@ package com.owncloud.android.ui.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.net.Uri import android.net.Uri
import android.webkit.WebSettings
import android.widget.Toast import android.widget.Toast
import androidx.webkit.WebSettingsCompat import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature import androidx.webkit.WebViewFeature
import com.nextcloud.client.appinfo.AppInfo import com.nextcloud.client.appinfo.AppInfo
import com.nextcloud.client.device.DeviceInfo import com.nextcloud.client.device.DeviceInfo
import com.owncloud.android.R import com.owncloud.android.R
import com.owncloud.android.files.FileMenuFilter
import com.owncloud.android.ui.asynctasks.TextEditorLoadUrlTask import com.owncloud.android.ui.asynctasks.TextEditorLoadUrlTask
import com.owncloud.android.utils.EditorUtils
import com.owncloud.android.utils.theme.ThemeUtils import com.owncloud.android.utils.theme.ThemeUtils
import javax.inject.Inject import javax.inject.Inject
@ -53,29 +54,33 @@ class TextEditorWebView : EditorWebView() {
finish() finish()
} }
val editor = FileMenuFilter.getEditor(contentResolver, user.get(), file.mimeType) webView.addJavascriptInterface(MobileInterface(), "DirectEditingMobileInterface")
if (editor != null && editor.id == "onlyoffice") {
getWebView().settings.userAgentString = generateOnlyOfficeUserAgent()
}
getWebView().addJavascriptInterface(MobileInterface(), "DirectEditingMobileInterface")
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) { if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
WebSettingsCompat.setForceDarkStrategy( WebSettingsCompat.setForceDarkStrategy(
getWebView().settings, webView.settings,
WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY
) )
} }
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK) && themeUtils.isDarkModeActive(this)) { if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK) && themeUtils.isDarkModeActive(this)) {
WebSettingsCompat.setForceDark(getWebView().settings, WebSettingsCompat.FORCE_DARK_ON) WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
} }
getWebView().setDownloadListener { url, _, _, _, _ -> downloadFile(Uri.parse(url)) } webView.setDownloadListener { url, _, _, _, _ -> downloadFile(Uri.parse(url)) }
loadUrl(intent.getStringExtra(ExternalSiteWebView.EXTRA_URL)) loadUrl(intent.getStringExtra(ExternalSiteWebView.EXTRA_URL))
} }
override fun setUserAgentString(webSettings: WebSettings?) {
val editor = EditorUtils.getEditor(contentResolver, user.get(), file.mimeType)
if (editor != null && editor.id == ONLYOFFICE) {
webView.settings.userAgentString = generateOnlyOfficeUserAgent()
}
// For Text/prosemirror we keep default user agent to not mess around with their special treatments
}
override fun loadUrl(url: String?) { override fun loadUrl(url: String?) {
if (url.isNullOrEmpty()) { if (url.isNullOrEmpty()) {
TextEditorLoadUrlTask(this, user.get(), file).execute() TextEditorLoadUrlTask(this, user.get(), file).execute()
@ -87,4 +92,8 @@ class TextEditorWebView : EditorWebView() {
return String.format(userAgent, deviceInfo.androidVersion, appInfo.getAppVersion(this)) return String.format(userAgent, deviceInfo.androidVersion, appInfo.getAppVersion(this))
} }
companion object {
const val ONLYOFFICE = "onlyoffice"
}
} }

View file

@ -26,10 +26,10 @@ import android.os.AsyncTask;
import com.nextcloud.android.lib.resources.directediting.DirectEditingOpenFileRemoteOperation; import com.nextcloud.android.lib.resources.directediting.DirectEditingOpenFileRemoteOperation;
import com.nextcloud.client.account.User; import com.nextcloud.client.account.User;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.FileMenuFilter;
import com.owncloud.android.lib.common.Editor; import com.owncloud.android.lib.common.Editor;
import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.ui.activity.EditorWebView; import com.owncloud.android.ui.activity.EditorWebView;
import com.owncloud.android.utils.EditorUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -55,7 +55,7 @@ public class TextEditorLoadUrlTask extends AsyncTask<Void, Void, String> {
return ""; return "";
} }
Editor editor = FileMenuFilter.getEditor(editorWebView.getContentResolver(), user, file.getMimeType()); Editor editor = EditorUtils.getEditor(editorWebView.getContentResolver(), user, file.getMimeType());
if (editor == null) { if (editor == null) {
return ""; return "";

View file

@ -35,19 +35,17 @@ import com.owncloud.android.databinding.FileListActionsBottomSheetCreatorBinding
import com.owncloud.android.databinding.FileListActionsBottomSheetFragmentBinding; import com.owncloud.android.databinding.FileListActionsBottomSheetFragmentBinding;
import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.FileMenuFilter;
import com.owncloud.android.lib.common.Creator; import com.owncloud.android.lib.common.Creator;
import com.owncloud.android.lib.common.DirectEditing; import com.owncloud.android.lib.common.DirectEditing;
import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.ui.activity.AppScanActivity; import com.owncloud.android.ui.activity.AppScanActivity;
import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.utils.EditorUtils;
import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.theme.ThemeColorUtils; import com.owncloud.android.utils.theme.ThemeColorUtils;
import com.owncloud.android.utils.theme.ThemeDrawableUtils; import com.owncloud.android.utils.theme.ThemeDrawableUtils;
import com.owncloud.android.utils.theme.ThemeUtils; import com.owncloud.android.utils.theme.ThemeUtils;
import javax.inject.Inject;
/** /**
* FAB menu {@link android.app.Dialog} styled as a bottom sheet for main actions. * FAB menu {@link android.app.Dialog} styled as a bottom sheet for main actions.
*/ */
@ -155,9 +153,9 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog implements In
} }
// create rich workspace // create rich workspace
if (FileMenuFilter.isEditorAvailable(getContext().getContentResolver(), if (EditorUtils.isEditorAvailable(getContext().getContentResolver(),
user, user,
MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) && MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) &&
file != null && !file.isEncrypted()) { file != null && !file.isEncrypted()) {
// richWorkspace // richWorkspace
// == "": no info set -> show button // == "": no info set -> show button

View file

@ -104,6 +104,7 @@ import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.ui.preview.PreviewMediaFragment; import com.owncloud.android.ui.preview.PreviewMediaFragment;
import com.owncloud.android.ui.preview.PreviewTextFileFragment; import com.owncloud.android.ui.preview.PreviewTextFileFragment;
import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.EditorUtils;
import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.EncryptionUtils;
import com.owncloud.android.utils.FileSortOrder; import com.owncloud.android.utils.FileSortOrder;
import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.FileStorageUtils;
@ -1062,9 +1063,9 @@ public class OCFileListFragment extends ExtendedListFragment implements
setFabVisible(false); setFabVisible(false);
resetHeaderScrollingState(); resetHeaderScrollingState();
((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true, true, true); ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true, true, true);
} else if (FileMenuFilter.isEditorAvailable(requireContext().getContentResolver(), } else if (EditorUtils.isEditorAvailable(requireContext().getContentResolver(),
accountManager.getUser(), accountManager.getUser(),
file.getMimeType()) && file.getMimeType()) &&
!file.isEncrypted()) { !file.isEncrypted()) {
mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(file, getContext()); mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(file, getContext());
} else if (capability.getRichDocumentsMimeTypeList().contains(file.getMimeType()) && } else if (capability.getRichDocumentsMimeTypeList().contains(file.getMimeType()) &&
@ -1131,9 +1132,9 @@ public class OCFileListFragment extends ExtendedListFragment implements
return true; return true;
} else if (itemId == R.id.action_edit) { } else if (itemId == R.id.action_edit) {
// should not be necessary, as menu item is filtered, but better play safe // should not be necessary, as menu item is filtered, but better play safe
if (FileMenuFilter.isEditorAvailable(requireContext().getContentResolver(), if (EditorUtils.isEditorAvailable(requireContext().getContentResolver(),
accountManager.getUser(), accountManager.getUser(),
singleFile.getMimeType())) { singleFile.getMimeType())) {
mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(singleFile, getContext()); mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(singleFile, getContext());
} else { } else {
mContainerActivity.getFileOperationsHelper().openFileAsRichDocument(singleFile, getContext()); mContainerActivity.getFileOperationsHelper().openFileAsRichDocument(singleFile, getContext());

View file

@ -55,7 +55,6 @@ import com.owncloud.android.MainApp;
import com.owncloud.android.R; import com.owncloud.android.R;
import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.FileMenuFilter;
import com.owncloud.android.files.StreamMediaFileOperation; import com.owncloud.android.files.StreamMediaFileOperation;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
@ -82,6 +81,7 @@ import com.owncloud.android.ui.events.FavoriteEvent;
import com.owncloud.android.ui.events.FileLockEvent; import com.owncloud.android.ui.events.FileLockEvent;
import com.owncloud.android.ui.events.SyncEventFinished; import com.owncloud.android.ui.events.SyncEventFinished;
import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.EditorUtils;
import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.PermissionUtil;
import com.owncloud.android.utils.UriUtils; import com.owncloud.android.utils.UriUtils;
@ -302,9 +302,9 @@ public class FileOperationsHelper {
if (launchables.isEmpty()) { if (launchables.isEmpty()) {
Optional<User> optionalUser = fileActivity.getUser(); Optional<User> optionalUser = fileActivity.getUser();
if (optionalUser.isPresent() && FileMenuFilter.isEditorAvailable(fileActivity.getContentResolver(), if (optionalUser.isPresent() && EditorUtils.isEditorAvailable(fileActivity.getContentResolver(),
optionalUser.get(), optionalUser.get(),
file.getMimeType())) { file.getMimeType())) {
openFileWithTextEditor(file, fileActivity); openFileWithTextEditor(file, fileActivity);
} else { } else {
Account account = fileActivity.getAccount(); Account account = fileActivity.getAccount();

View file

@ -0,0 +1,48 @@
/*
*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2022 Tobias Kaminsky
* Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.utils
import android.content.ContentResolver
import com.google.gson.Gson
import com.nextcloud.client.account.User
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.lib.common.DirectEditing
import com.owncloud.android.lib.common.Editor
object EditorUtils {
@JvmStatic
fun isEditorAvailable(contentResolver: ContentResolver?, user: User?, mimeType: String?): Boolean {
return getEditor(contentResolver, user, mimeType) != null
}
@JvmStatic
fun getEditor(contentResolver: ContentResolver?, user: User?, mimeType: String?): Editor? {
val json = ArbitraryDataProvider(contentResolver).getValue(user, ArbitraryDataProvider.DIRECT_EDITING)
if (json.isEmpty()) {
return null
}
val directEditing = Gson().fromJson(json, DirectEditing::class.java)
val editors = directEditing.editors.values
return editors.firstOrNull { it.mimetypes.contains(mimeType) || it.optionalMimetypes.contains(mimeType) }
}
}