diff --git a/app/detekt.yml b/app/detekt.yml index 8423574a88..340c732786 100644 --- a/app/detekt.yml +++ b/app/detekt.yml @@ -69,7 +69,7 @@ complexity: excludes: ['**/androidTest/**'] LongParameterList: active: true - functionThreshold: 6 + functionThreshold: 7 constructorThreshold: 6 ignoreDefaultParameters: false MethodOverloading: diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index 7a3b8252f1..11d4979e94 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -107,20 +107,8 @@ public abstract class AbstractIT { } } - Account temp = new Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext)); - platformAccountManager.addAccountExplicitly(temp, "password", null); - platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "https://nextcloud.localhost"); - platformAccountManager.setUserData(temp, KEY_USER_ID, "test"); - - final UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext); - account = userAccountManager.getAccountByName("test@https://nextcloud.localhost"); - - if (account == null) { - throw new ActivityNotFoundException(); - } - - Optional optionalUser = userAccountManager.getUser(account.name); - user = optionalUser.orElseThrow(IllegalAccessError::new); + account = createAccount("test@https://nextcloud.localhost"); + user = getUser(account); client = OwnCloudClientFactory.createOwnCloudClient(account, targetContext); nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, targetContext); @@ -447,4 +435,31 @@ public abstract class AbstractIT { public static String getUserId(User user) { return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID); } + + protected static User getUser(Account account) { + Optional optionalUser = UserAccountManagerImpl.fromContext(targetContext).getUser(account.name); + return optionalUser.orElseThrow(IllegalAccessError::new); + } + + protected static Account createAccount(String name) { + AccountManager platformAccountManager = AccountManager.get(targetContext); + + Account temp = new Account(name, MainApp.getAccountType(targetContext)); + int atPos = name.lastIndexOf('@'); + platformAccountManager.addAccountExplicitly(temp, "password", null); + platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, + name.substring(atPos + 1)); + platformAccountManager.setUserData(temp, KEY_USER_ID, name.substring(0, atPos)); + + Account account = UserAccountManagerImpl.fromContext(targetContext).getAccountByName(name); + if (account == null) { + throw new ActivityNotFoundException(); + } + return account; + } + + protected static boolean removeAccount(Account account) { + return AccountManager.get(targetContext).removeAccountExplicitly(account); + } + } diff --git a/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt b/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt index 76bd440dca..4e92898407 100644 --- a/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/files/FileMenuFilterIT.kt @@ -269,6 +269,81 @@ class FileMenuFilterIT : AbstractIT() { } } + @Test + fun filter_select_all() { + configureCapability(OCCapability()) + + // not in single file fragment -> multi selection is possible under certain circumstances + + launchActivity().use { + it.onActivity { activity -> + val filterFactory = FileMenuFilter.Factory(mockStorageManager, activity, editorUtils) + + val files = listOf(OCFile("/foo.bin"), OCFile("/bar.bin"), OCFile("/baz.bin")) + + // single file, not in multi selection + // *Select all* and *Deselect all* should stay hidden + var sut = filterFactory.newInstance(files.first(), mockComponentsGetter, true, user) + + var toHide = sut.getToHide(false) + assertTrue(toHide.contains(R.id.action_select_all_action_menu)) + assertTrue(toHide.contains(R.id.action_deselect_all_action_menu)) + + // multiple files, all selected in multi selection + // *Deselect all* shown, *Select all* not + sut = filterFactory.newInstance(files.size, files, mockComponentsGetter, false, user) + + toHide = sut.getToHide(false) + assertTrue(toHide.contains(R.id.action_select_all_action_menu)) + assertFalse(toHide.contains(R.id.action_deselect_all_action_menu)) + + // multiple files, all but one selected + // both *Select all* and *Deselect all* should be shown + sut = filterFactory.newInstance(files.size + 1, files, mockComponentsGetter, false, user) + + toHide = sut.getToHide(false) + assertFalse(toHide.contains(R.id.action_select_all_action_menu)) + assertFalse(toHide.contains(R.id.action_deselect_all_action_menu)) + } + } + } + + fun filter_select_all_singleFileFragment() { + configureCapability(OCCapability()) + + // in single file fragment (e.g. FileDetailFragment or PreviewImageFragment), selecting multiple files + // is not possible -> *Select all* and *Deselect all* options should be hidden + + launchActivity().use { + it.onActivity { activity -> + val filterFactory = FileMenuFilter.Factory(mockStorageManager, activity, editorUtils) + + val files = listOf(OCFile("/foo.bin"), OCFile("/bar.bin"), OCFile("/baz.bin")) + + // single file + var sut = filterFactory.newInstance(files.first(), mockComponentsGetter, true, user) + + var toHide = sut.getToHide(true) + assertTrue(toHide.contains(R.id.action_select_all_action_menu)) + assertTrue(toHide.contains(R.id.action_deselect_all_action_menu)) + + // multiple files, all selected + sut = filterFactory.newInstance(files.size, files, mockComponentsGetter, false, user) + + toHide = sut.getToHide(true) + assertTrue(toHide.contains(R.id.action_select_all_action_menu)) + assertTrue(toHide.contains(R.id.action_deselect_all_action_menu)) + + // multiple files, all but one selected + sut = filterFactory.newInstance(files.size + 1, files, mockComponentsGetter, false, user) + + toHide = sut.getToHide(true) + assertTrue(toHide.contains(R.id.action_select_all_action_menu)) + assertTrue(toHide.contains(R.id.action_deselect_all_action_menu)) + } + } + } + private data class ExpectedLockVisibilities( val lockFile: Boolean, val unlockFile: Boolean diff --git a/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt index a4e534a560..1849c4fb2d 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt @@ -38,4 +38,12 @@ class ReceiveExternalFilesActivityIT : AbstractIT() { val sut: Activity = activityRule.launchActivity(null) screenshot(sut) } + + @Test + @ScreenshotTest + fun openMultiAccount() { + val secondAccount = createAccount("secondtest@https://nextcloud.localhost") + open() + removeAccount(secondAccount) + } } diff --git a/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt b/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt index 0ef22714e7..149533b8b6 100644 --- a/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt +++ b/app/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt @@ -20,7 +20,6 @@ package com.nextcloud.client.errorhandling import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -65,17 +64,11 @@ class ShowErrorActivity : AppCompatActivity() { private fun reportIssue() { ClipboardUtil.copyToClipboard(this, binding.textViewError.text.toString(), false) - val issueLink = getString(R.string.report_issue_link) - if (issueLink.isNotEmpty()) { - val uriUrl = Uri.parse( - String.format( - issueLink, - URLEncoder.encode(binding.textViewError.text.toString()) - ) - ) - val intent = Intent(Intent.ACTION_VIEW, uriUrl) - DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available) - } + val issueLink = String.format( + getString(R.string.report_issue_link), + URLEncoder.encode(binding.textViewError.text.toString(), Charsets.UTF_8.name()) + ) + DisplayUtils.startLinkIntent(this, issueLink) Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_LONG).show() } diff --git a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java index 8982911984..eb9f06f04e 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java +++ b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java @@ -28,7 +28,6 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.content.Intent; import android.content.res.Configuration; -import android.net.Uri; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; @@ -117,8 +116,8 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh defaultViewThemeUtils.platform.colorTextView(binding.hostOwnServer, ColorRole.ON_PRIMARY); binding.hostOwnServer.setVisibility(isProviderOrOwnInstallationVisible ? View.VISIBLE : View.GONE); - if (!isProviderOrOwnInstallationVisible) { - binding.hostOwnServer.setOnClickListener(v -> onHostYourOwnServerClick()); + if (isProviderOrOwnInstallationVisible) { + binding.hostOwnServer.setOnClickListener(v -> DisplayUtils.startLinkIntent(this, R.string.url_server_install)); } @@ -153,11 +152,7 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { - setSlideshowSize(true); - } else { - setSlideshowSize(false); - } + setSlideshowSize(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE); } @Override @@ -201,11 +196,6 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh // unused but to be implemented due to abstract parent } - public void onHostYourOwnServerClick() { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_server_install))); - DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available); - } - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt index 9ea52a0fc2..3ee0a99071 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt @@ -324,7 +324,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { @IdRes additionalToHide: List? = null ): FileActionsBottomSheet { - return newInstance(1, listOf(file), isOverflow, additionalToHide) + return newInstance(1, listOf(file), isOverflow, additionalToHide, true) } @JvmStatic @@ -334,13 +334,15 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { files: Collection, isOverflow: Boolean, @IdRes - additionalToHide: List? = null + additionalToHide: List? = null, + inSingleFileFragment: Boolean = false ): FileActionsBottomSheet { return FileActionsBottomSheet().apply { val argsBundle = bundleOf( FileActionsViewModel.ARG_ALL_FILES_COUNT to numberOfAllFiles, FileActionsViewModel.ARG_FILES to ArrayList(files), - FileActionsViewModel.ARG_IS_OVERFLOW to isOverflow + FileActionsViewModel.ARG_IS_OVERFLOW to isOverflow, + FileActionsViewModel.ARG_IN_SINGLE_FILE_FRAGMENT to inSingleFileFragment ) additionalToHide?.let { argsBundle.putIntArray(FileActionsViewModel.ARG_ADDITIONAL_FILTER, additionalToHide.toIntArray()) diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt index 2b559a5313..6856e4fc2e 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsViewModel.kt @@ -69,17 +69,21 @@ class FileActionsViewModel @Inject constructor( @IdRes get() = _clickActionId - fun load(arguments: Bundle, componentsGetter: ComponentsGetter) { + fun load( + arguments: Bundle, + componentsGetter: ComponentsGetter + ) { val files: List? = arguments.getParcelableArrayList(ARG_FILES) val numberOfAllFiles: Int = arguments.getInt(ARG_ALL_FILES_COUNT, 1) val isOverflow = arguments.getBoolean(ARG_IS_OVERFLOW, false) val additionalFilter: IntArray? = arguments.getIntArray(ARG_ADDITIONAL_FILTER) + val inSingleFileFragment = arguments.getBoolean(ARG_IN_SINGLE_FILE_FRAGMENT) if (files.isNullOrEmpty()) { logger.d(TAG, "No valid files argument for loading actions") _uiState.postValue(UiState.Error) } else { - load(componentsGetter, files.toList(), numberOfAllFiles, isOverflow, additionalFilter) + load(componentsGetter, files.toList(), numberOfAllFiles, isOverflow, additionalFilter, inSingleFileFragment) } } @@ -88,10 +92,11 @@ class FileActionsViewModel @Inject constructor( files: Collection, numberOfAllFiles: Int?, isOverflow: Boolean?, - additionalFilter: IntArray? + additionalFilter: IntArray?, + inSingleFileFragment: Boolean = false ) { viewModelScope.launch(Dispatchers.IO) { - val toHide = getHiddenActions(componentsGetter, numberOfAllFiles, files, isOverflow) + val toHide = getHiddenActions(componentsGetter, numberOfAllFiles, files, isOverflow, inSingleFileFragment) val availableActions = getActionsToShow(additionalFilter, toHide) updateStateLoaded(files, availableActions) } @@ -101,7 +106,8 @@ class FileActionsViewModel @Inject constructor( componentsGetter: ComponentsGetter, numberOfAllFiles: Int?, files: Collection, - isOverflow: Boolean? + isOverflow: Boolean?, + inSingleFileFragment: Boolean ): List { return filterFactory.newInstance( numberOfAllFiles ?: 1, @@ -110,7 +116,7 @@ class FileActionsViewModel @Inject constructor( isOverflow ?: false, currentAccountProvider.user ) - .getToHide(false) + .getToHide(inSingleFileFragment) } private fun getActionsToShow( @@ -161,6 +167,7 @@ class FileActionsViewModel @Inject constructor( const val ARG_FILES = "FILES" const val ARG_IS_OVERFLOW = "OVERFLOW" const val ARG_ADDITIONAL_FILTER = "ADDITIONAL_FILTER" + const val ARG_IN_SINGLE_FILE_FRAGMENT = "IN_SINGLE_FILE_FRAGMENT" private val TAG = FileActionsViewModel::class.simpleName!! } diff --git a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index c0d0a252f0..3a3dda3722 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -84,6 +84,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.content.res.ResourcesCompat; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import kotlin.text.Charsets; /** * Manager for concurrent access to thumbnails cache. @@ -1415,7 +1416,7 @@ public final class ThumbnailsCacheManager { GetMethod getMethod = null; try { String uri = mClient.getBaseUri() + "/index.php/core/preview.png?file=" - + URLEncoder.encode(file.getRemotePath()) + + URLEncoder.encode(file.getRemotePath(), Charsets.UTF_8.name()) + "&x=" + (pxW / 2) + "&y=" + (pxH / 2) + "&a=1&mode=cover&forceIcon=0"; Log_OC.d(TAG, "generate resized image: " + file.getFileName() + " URI: " + uri); getMethod = new GetMethod(uri); diff --git a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 74ac8f0ec9..f4092a9860 100644 --- a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -185,23 +185,19 @@ public class FileMenuFilter { return toHide; } + private void filterShareFile(List toHide, OCCapability capability) { - if (containsEncryptedFile() || (!isShareViaLinkAllowed() && !isShareWithUsersAllowed()) || - !isSingleSelection() || !isShareApiEnabled(capability) || !files.iterator().next().canReshare() - || overflowMenu) { + if (!isSingleSelection() || containsEncryptedFile() || + (!isShareViaLinkAllowed() && !isShareWithUsersAllowed()) || + !isShareApiEnabled(capability) || !files.iterator().next().canReshare()) { toHide.add(R.id.action_send_share_file); } } private void filterSendFiles(List toHide, boolean inSingleFileFragment) { - boolean show = true; - if (overflowMenu || SEND_OFF.equalsIgnoreCase(context.getString(R.string.send_files_to_other_apps)) || containsEncryptedFile()) { - show = false; - } - if (!inSingleFileFragment && (isSingleSelection() || !anyFileDown())) { - show = false; - } - if (!show) { + if ((overflowMenu || SEND_OFF.equalsIgnoreCase(context.getString(R.string.send_files_to_other_apps)) || containsEncryptedFile()) || + (!inSingleFileFragment && (isSingleSelection() || !allFileDown())) || + !toHide.contains(R.id.action_send_share_file)) { toHide.add(R.id.action_send_file); } } @@ -543,6 +539,15 @@ public class FileMenuFilter { return false; } + private boolean allFileDown() { + for (OCFile file: files) { + if(!file.isDown()) { + return false; + } + } + return true; + } + private boolean allFavorites() { for (OCFile file : files) { if (!file.isFavorite()) { 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 9a3d87ac58..d47ad343c8 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 @@ -580,8 +580,7 @@ public abstract class DrawerActivity extends ToolbarActivity for (ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) { if (menuItem.getTitle().toString().equalsIgnoreCase(link.getName())) { if (link.getRedirect()) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link.getUrl())); - DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available); + DisplayUtils.startLinkIntent(this, link.getUrl()); } else { Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.getName()); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 0563b3fb4a..87ca463056 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -680,19 +680,14 @@ public abstract class FileActivity extends DrawerActivity DisplayUtils.showSnackMessage(activity, R.string.dev_version_no_information_available, Snackbar.LENGTH_LONG); } if (latestVersion > currentVersion) { + String devApkLink = activity.getString(R.string.dev_link) + latestVersion + ".apk"; if (openDirectly) { - String devApkLink = (String) activity.getText(R.string.dev_link) + latestVersion + ".apk"; - Uri uriUrl = Uri.parse(devApkLink); - Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl); - DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available); + DisplayUtils.startLinkIntent(activity, devApkLink); } else { Snackbar.make(activity.findViewById(android.R.id.content), R.string.dev_version_new_version_available, Snackbar.LENGTH_LONG) .setAction(activity.getString(R.string.version_dev_download), v -> { - String devApkLink = (String) activity.getText(R.string.dev_link) + latestVersion + ".apk"; - Uri uriUrl = Uri.parse(devApkLink); - Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl); - DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available); + DisplayUtils.startLinkIntent(activity, devApkLink); }).show(); } } else { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index ad7e306728..9162852ef9 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1093,6 +1093,8 @@ public class FileDisplayActivity extends FileActivity } } else if (leftFragment instanceof PreviewTextStringFragment) { createMinFragments(null); + } else if (leftFragment instanceof PreviewPdfFragment) { + super.onBackPressed(); } else { // pop back resetScrolling(true); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index 467b444f39..aaf6f6bb4c 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -41,21 +41,10 @@ import android.os.Looper; import android.os.Parcelable; import android.text.TextUtils; import android.text.format.DateFormat; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.view.WindowManager.LayoutParams; -import android.widget.AdapterView; +import android.widget.*; import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; import com.google.android.material.button.MaterialButton; import com.nextcloud.client.account.User; @@ -79,19 +68,11 @@ import com.owncloud.android.operations.UploadFileOperation; import com.owncloud.android.syncadapter.FileSyncAdapter; import com.owncloud.android.ui.adapter.UploaderAdapter; import com.owncloud.android.ui.asynctasks.CopyAndUploadContentUrisTask; -import com.owncloud.android.ui.dialog.AccountChooserInterface; -import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; -import com.owncloud.android.ui.dialog.CreateFolderDialogFragment; -import com.owncloud.android.ui.dialog.MultipleAccountsDialog; -import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; +import com.owncloud.android.ui.dialog.*; import com.owncloud.android.ui.fragment.TaskRetainerFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.ui.helpers.UriUploader; -import com.owncloud.android.utils.DataHolderUtil; -import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.ErrorMessageAdapter; -import com.owncloud.android.utils.FileSortOrder; -import com.owncloud.android.utils.MimeType; +import com.owncloud.android.utils.*; import com.owncloud.android.utils.theme.ViewThemeUtils; import java.io.File; @@ -99,15 +80,7 @@ import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Method; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Stack; -import java.util.Vector; +import java.util.*; import javax.inject.Inject; @@ -1039,8 +1012,8 @@ public class ReceiveExternalFilesActivity extends FileActivity inflater.inflate(R.menu.activity_receive_external_files, menu); if (!isHaveMultipleAccount()) { - MenuItem switchAccountMenu = menu.findItem(R.id.action_switch_account); - switchAccountMenu.setVisible(false); + menu.findItem(R.id.action_switch_account).setVisible(false); + menu.findItem(R.id.action_create_dir).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } // tint search event 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 3971756f67..decd24ee2e 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 @@ -361,9 +361,7 @@ public class SettingsActivity extends PreferenceActivity String imprintWeb = getString(R.string.url_imprint); if (!imprintWeb.isEmpty()) { - Uri uriUrl = Uri.parse(imprintWeb); - Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl); - DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available); + DisplayUtils.startLinkIntent(this, imprintWeb); } //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG"); return true; @@ -539,12 +537,7 @@ public class SettingsActivity extends PreferenceActivity if (pHelp != null) { if (helpEnabled) { pHelp.setOnPreferenceClickListener(preference -> { - String helpWeb = getString(R.string.url_help); - if (!helpWeb.isEmpty()) { - Uri uriUrl = Uri.parse(helpWeb); - Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl); - DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available); - } + DisplayUtils.startLinkIntent(this, R.string.url_help); return true; }); } else { @@ -897,9 +890,7 @@ public class SettingsActivity extends PreferenceActivity startActivity(installIntent); } else { // no f-droid market app or Play store installed --> launch browser for f-droid url - Intent downloadIntent = new Intent(Intent.ACTION_VIEW, - Uri.parse("https://f-droid.org/repository/browse/?fdid=at.bitfire.davdroid")); - DisplayUtils.startIntentIfAppAvailable(downloadIntent, this, R.string.no_browser_available); + DisplayUtils.startLinkIntent(this, "https://f-droid.org/packages/at.bitfire.davdroid/"); DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_no_store_error); } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java index 23013f24c3..46b22444e8 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java @@ -123,7 +123,8 @@ public class NotificationListAdapter extends RecyclerView.Adapter openLink(notification.getLink())); + holder.binding.subject.setOnClickListener(v -> DisplayUtils.startLinkIntent(notificationsActivity, + notification.getLink())); holder.binding.subject.setText(subject); } else { if (!TextUtils.isEmpty(notification.subjectRich)) { @@ -329,8 +330,7 @@ public class NotificationListAdapter extends RecyclerView.Adapter fileNames; @@ -99,6 +100,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im private OCFile parentFolder; private OwnCloudClient client; private Button positiveButton; + private DialogFragment waitDialog; public enum Type { DOCUMENT, @@ -234,6 +236,8 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im } private void createFromTemplate(Template template, String path) { + waitDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false); + waitDialog.show(getParentFragmentManager(), WAIT_DIALOG_TAG); new CreateFileFromTemplateTask(this, client, template, path, currentAccount.getUser()).execute(); } @@ -364,8 +368,13 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im ChooseRichDocumentsTemplateDialogFragment fragment = chooseTemplateDialogFragmentWeakReference.get(); if (fragment != null && fragment.isAdded()) { + if (fragment.waitDialog != null) { + fragment.waitDialog.dismiss(); + } + if (url.isEmpty()) { - DisplayUtils.showSnackMessage(fragment.binding.list, R.string.error_creating_file_from_template); + fragment.dismiss(); + DisplayUtils.showSnackMessage(fragment.requireActivity(), R.string.error_creating_file_from_template); } else { Intent collaboraWebViewIntent = new Intent(MainApp.getAppContext(), RichDocumentsEditorWebView.class); collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Collabora"); @@ -416,7 +425,8 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im if (fragment != null) { if (templateList.isEmpty()) { - DisplayUtils.showSnackMessage(fragment.binding.list, R.string.error_retrieving_templates); + fragment.dismiss(); + DisplayUtils.showSnackMessage(fragment.requireActivity(), R.string.error_retrieving_templates); } else { if (templateList.size() == SINGLE_TEMPLATE) { fragment.onTemplateChosen(templateList.get(0)); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index f4034efa1f..4e58f295b6 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -282,7 +282,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, R.id.action_copy, R.id.action_stream_media, R.id.action_send_share_file, - R.id.action_select_all_action_menu)); + R.id.action_pin_to_homescreen + )); if (getFile().isFolder()) { additionalFilter.add(R.id.action_send_file); additionalFilter.add(R.id.action_sync_file); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index f588bdb07b..4755bdd041 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -1524,7 +1524,7 @@ public class OCFileListFragment extends ExtendedListFragment implements setTitle(R.string.drawer_item_shared); break; default: - setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext())); + setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext()), false); break; } } @@ -1591,7 +1591,7 @@ public class OCFileListFragment extends ExtendedListFragment implements ((FileDisplayActivity) activity).initSyncBroadcastReceiver(); } - setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext())); + setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext()), false); activity.getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT); } @@ -1843,18 +1843,30 @@ public class OCFileListFragment extends ExtendedListFragment implements } } + /** + * Theme default action bar according to provided parameters. + * Replaces back arrow with hamburger menu icon. + * + * @param title string res id of title to be shown in action bar + */ protected void setTitle(@StringRes final int title) { - setTitle(getContext().getString(title)); + setTitle(requireContext().getString(title), true); } - protected void setTitle(final String title) { - getActivity().runOnUiThread(() -> { + /** + * Theme default action bar according to provided parameters. + * + * @param title title to be shown in action bar + * @param showBackAsMenu iff true replace back arrow with hamburger menu icon + */ + protected void setTitle(final String title, Boolean showBackAsMenu) { + requireActivity().runOnUiThread(() -> { if (getActivity() != null) { final ActionBar actionBar = ((FileDisplayActivity) getActivity()).getSupportActionBar(); final Context context = getContext(); if (actionBar != null && context != null) { - viewThemeUtils.files.themeActionBar(context, actionBar, title, true); + viewThemeUtils.files.themeActionBar(context, actionBar, title, showBackAsMenu); } } }); diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java index a8dd9d8510..09e03ab9b6 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -375,11 +375,11 @@ public class PreviewImageFragment extends FileFragment implements Injectable { Arrays.asList( R.id.action_rename_file, R.id.action_sync_file, - R.id.action_select_all, R.id.action_move, R.id.action_copy, R.id.action_favorite, - R.id.action_unset_favorite + R.id.action_unset_favorite, + R.id.action_pin_to_homescreen )); if (getFile() != null && getFile().isSharedWithMe() && !getFile().canReshare()) { additionalFilter.add(R.id.action_send_share_file); diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java index 93ef97c279..18ee93690f 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java @@ -424,7 +424,6 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene Arrays.asList( R.id.action_rename_file, R.id.action_sync_file, - R.id.action_select_all, R.id.action_move, R.id.action_copy, R.id.action_favorite, diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java index a26988735d..e34031a51a 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java @@ -300,11 +300,11 @@ public class PreviewTextFileFragment extends PreviewTextFragment { Arrays.asList( R.id.action_rename_file, R.id.action_sync_file, - R.id.action_select_all, R.id.action_move, R.id.action_copy, R.id.action_favorite, - R.id.action_unset_favorite + R.id.action_unset_favorite, + R.id.action_pin_to_homescreen )); if (getFile() != null && getFile().isSharedWithMe() && !getFile().canReshare()) { additionalFilter.add(R.id.action_send_share_file); diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java index f8e2ad4e7d..f5822a6d2b 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java @@ -20,10 +20,8 @@ package com.owncloud.android.ui.preview; import android.app.Activity; -import android.content.Intent; import android.content.res.Resources; import android.graphics.Color; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.text.Html; @@ -195,10 +193,7 @@ public abstract class PreviewTextFragment extends FileFragment implements Search @Override public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { - builder.linkResolver((view, link) -> { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); - DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available); - }); + builder.linkResolver((view, link) -> DisplayUtils.startLinkIntent(activity, link)); } }) .usePlugin(TablePlugin.create(activity)) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFullscreenDialog.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFullscreenDialog.kt index 3438f9d667..90407930f5 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFullscreenDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFullscreenDialog.kt @@ -150,6 +150,9 @@ class PreviewVideoFullscreenDialog( } mExoPlayer.addListener(playListener) playingStateListener = playListener + + // Run once to set initial state of play or pause buttons + playListener.onIsPlayingChanged(sourceExoPlayer.isPlaying) } override fun onBackPressed() { diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java index bbf7bbe1de..eb64203b07 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -775,13 +775,15 @@ public final class DisplayUtils { startLinkIntent(activity, activity.getString(link)); } - static public void startLinkIntent(Activity activity, Uri url) { - Intent intent = new Intent(Intent.ACTION_VIEW, url); - DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available); + static public void startLinkIntent(Activity activity, String url) { + if (!TextUtils.isEmpty(url)) { + startLinkIntent(activity, Uri.parse(url)); + } } - static public void startLinkIntent(Activity activity, String url) { - startLinkIntent(activity, Uri.parse(url)); + static public void startLinkIntent(Activity activity, Uri uri) { + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available); } static public void startIntentIfAppAvailable(Intent intent, Activity activity, @StringRes int error) { diff --git a/app/src/main/res/layout/receive_external_files.xml b/app/src/main/res/layout/receive_external_files.xml index a8bcc10661..01b220ae51 100644 --- a/app/src/main/res/layout/receive_external_files.xml +++ b/app/src/main/res/layout/receive_external_files.xml @@ -38,6 +38,7 @@ android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/bg_default" android:divider="@color/transparent" android:dividerHeight="0dip" android:nestedScrollingEnabled="true" @@ -58,6 +59,7 @@ diff --git a/app/src/main/res/menu/activity_folder_picker.xml b/app/src/main/res/menu/activity_folder_picker.xml index 369b4efda5..fbd3fcf2a6 100644 --- a/app/src/main/res/menu/activity_folder_picker.xml +++ b/app/src/main/res/menu/activity_folder_picker.xml @@ -36,5 +36,6 @@ android:icon="@drawable/ic_action_create_dir" android:orderInCategory="1" android:title="@string/actionbar_mkdir" - app:showAsAction="never" /> + app:iconTint="?attr/colorOnSurface" + app:showAsAction="ifRoom"/> diff --git a/app/src/main/res/menu/activity_receive_external_files.xml b/app/src/main/res/menu/activity_receive_external_files.xml index 4c7a26e5af..8d8bbfb6da 100644 --- a/app/src/main/res/menu/activity_receive_external_files.xml +++ b/app/src/main/res/menu/activity_receive_external_files.xml @@ -39,13 +39,7 @@ android:icon="@drawable/ic_action_create_dir" android:orderInCategory="1" android:title="@string/actionbar_mkdir" - app:showAsAction="never"/> - diff --git a/build.gradle b/build.gradle index a926763a29..e0195d70f9 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { fidoVersion = "4.1.0-patch1" checkerVersion = "3.21.2" exoplayerVersion = "2.19.0" - documentScannerVersion = "1.0.1" + documentScannerVersion = "1.1.1" roomVersion = "2.5.2" ciBuild = System.getenv("CI") == "true" diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index aefe635326..2c0dfa2b0d 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 77 warnings + Lint Report: 79 warnings