Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Tobias Kaminsky 2023-07-29 02:31:13 +02:00
commit 24f96857b3
30 changed files with 230 additions and 161 deletions

View file

@ -69,7 +69,7 @@ complexity:
excludes: ['**/androidTest/**'] excludes: ['**/androidTest/**']
LongParameterList: LongParameterList:
active: true active: true
functionThreshold: 6 functionThreshold: 7
constructorThreshold: 6 constructorThreshold: 6
ignoreDefaultParameters: false ignoreDefaultParameters: false
MethodOverloading: MethodOverloading:

View file

@ -107,20 +107,8 @@ public abstract class AbstractIT {
} }
} }
Account temp = new Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext)); account = createAccount("test@https://nextcloud.localhost");
platformAccountManager.addAccountExplicitly(temp, "password", null); user = getUser(account);
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<User> optionalUser = userAccountManager.getUser(account.name);
user = optionalUser.orElseThrow(IllegalAccessError::new);
client = OwnCloudClientFactory.createOwnCloudClient(account, targetContext); client = OwnCloudClientFactory.createOwnCloudClient(account, targetContext);
nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, targetContext); nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, targetContext);
@ -447,4 +435,31 @@ public abstract class AbstractIT {
public static String getUserId(User user) { public static String getUserId(User user) {
return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID); return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID);
} }
protected static User getUser(Account account) {
Optional<User> 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);
}
} }

View file

@ -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<TestActivity>().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<TestActivity>().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( private data class ExpectedLockVisibilities(
val lockFile: Boolean, val lockFile: Boolean,
val unlockFile: Boolean val unlockFile: Boolean

View file

@ -38,4 +38,12 @@ class ReceiveExternalFilesActivityIT : AbstractIT() {
val sut: Activity = activityRule.launchActivity(null) val sut: Activity = activityRule.launchActivity(null)
screenshot(sut) screenshot(sut)
} }
@Test
@ScreenshotTest
fun openMultiAccount() {
val secondAccount = createAccount("secondtest@https://nextcloud.localhost")
open()
removeAccount(secondAccount)
}
} }

View file

@ -20,7 +20,6 @@
package com.nextcloud.client.errorhandling package com.nextcloud.client.errorhandling
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -65,17 +64,11 @@ class ShowErrorActivity : AppCompatActivity() {
private fun reportIssue() { private fun reportIssue() {
ClipboardUtil.copyToClipboard(this, binding.textViewError.text.toString(), false) ClipboardUtil.copyToClipboard(this, binding.textViewError.text.toString(), false)
val issueLink = getString(R.string.report_issue_link) val issueLink = String.format(
if (issueLink.isNotEmpty()) { getString(R.string.report_issue_link),
val uriUrl = Uri.parse( URLEncoder.encode(binding.textViewError.text.toString(), Charsets.UTF_8.name())
String.format( )
issueLink, DisplayUtils.startLinkIntent(this, issueLink)
URLEncoder.encode(binding.textViewError.text.toString())
)
)
val intent = Intent(Intent.ACTION_VIEW, uriUrl)
DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available)
}
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_LONG).show()
} }

View file

@ -28,7 +28,6 @@ import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -117,8 +116,8 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh
defaultViewThemeUtils.platform.colorTextView(binding.hostOwnServer, ColorRole.ON_PRIMARY); defaultViewThemeUtils.platform.colorTextView(binding.hostOwnServer, ColorRole.ON_PRIMARY);
binding.hostOwnServer.setVisibility(isProviderOrOwnInstallationVisible ? View.VISIBLE : View.GONE); binding.hostOwnServer.setVisibility(isProviderOrOwnInstallationVisible ? View.VISIBLE : View.GONE);
if (!isProviderOrOwnInstallationVisible) { if (isProviderOrOwnInstallationVisible) {
binding.hostOwnServer.setOnClickListener(v -> onHostYourOwnServerClick()); 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 @Override
public void onConfigurationChanged(Configuration newConfig) { public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { setSlideshowSize(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
setSlideshowSize(true);
} else {
setSlideshowSize(false);
}
} }
@Override @Override
@ -201,11 +196,6 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh
// unused but to be implemented due to abstract parent // 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 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);

View file

@ -324,7 +324,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable {
@IdRes @IdRes
additionalToHide: List<Int>? = null additionalToHide: List<Int>? = null
): FileActionsBottomSheet { ): FileActionsBottomSheet {
return newInstance(1, listOf(file), isOverflow, additionalToHide) return newInstance(1, listOf(file), isOverflow, additionalToHide, true)
} }
@JvmStatic @JvmStatic
@ -334,13 +334,15 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable {
files: Collection<OCFile>, files: Collection<OCFile>,
isOverflow: Boolean, isOverflow: Boolean,
@IdRes @IdRes
additionalToHide: List<Int>? = null additionalToHide: List<Int>? = null,
inSingleFileFragment: Boolean = false
): FileActionsBottomSheet { ): FileActionsBottomSheet {
return FileActionsBottomSheet().apply { return FileActionsBottomSheet().apply {
val argsBundle = bundleOf( val argsBundle = bundleOf(
FileActionsViewModel.ARG_ALL_FILES_COUNT to numberOfAllFiles, FileActionsViewModel.ARG_ALL_FILES_COUNT to numberOfAllFiles,
FileActionsViewModel.ARG_FILES to ArrayList<OCFile>(files), FileActionsViewModel.ARG_FILES to ArrayList<OCFile>(files),
FileActionsViewModel.ARG_IS_OVERFLOW to isOverflow FileActionsViewModel.ARG_IS_OVERFLOW to isOverflow,
FileActionsViewModel.ARG_IN_SINGLE_FILE_FRAGMENT to inSingleFileFragment
) )
additionalToHide?.let { additionalToHide?.let {
argsBundle.putIntArray(FileActionsViewModel.ARG_ADDITIONAL_FILTER, additionalToHide.toIntArray()) argsBundle.putIntArray(FileActionsViewModel.ARG_ADDITIONAL_FILTER, additionalToHide.toIntArray())

View file

@ -69,17 +69,21 @@ class FileActionsViewModel @Inject constructor(
@IdRes @IdRes
get() = _clickActionId get() = _clickActionId
fun load(arguments: Bundle, componentsGetter: ComponentsGetter) { fun load(
arguments: Bundle,
componentsGetter: ComponentsGetter
) {
val files: List<OCFile>? = arguments.getParcelableArrayList(ARG_FILES) val files: List<OCFile>? = arguments.getParcelableArrayList(ARG_FILES)
val numberOfAllFiles: Int = arguments.getInt(ARG_ALL_FILES_COUNT, 1) val numberOfAllFiles: Int = arguments.getInt(ARG_ALL_FILES_COUNT, 1)
val isOverflow = arguments.getBoolean(ARG_IS_OVERFLOW, false) val isOverflow = arguments.getBoolean(ARG_IS_OVERFLOW, false)
val additionalFilter: IntArray? = arguments.getIntArray(ARG_ADDITIONAL_FILTER) val additionalFilter: IntArray? = arguments.getIntArray(ARG_ADDITIONAL_FILTER)
val inSingleFileFragment = arguments.getBoolean(ARG_IN_SINGLE_FILE_FRAGMENT)
if (files.isNullOrEmpty()) { if (files.isNullOrEmpty()) {
logger.d(TAG, "No valid files argument for loading actions") logger.d(TAG, "No valid files argument for loading actions")
_uiState.postValue(UiState.Error) _uiState.postValue(UiState.Error)
} else { } 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<OCFile>, files: Collection<OCFile>,
numberOfAllFiles: Int?, numberOfAllFiles: Int?,
isOverflow: Boolean?, isOverflow: Boolean?,
additionalFilter: IntArray? additionalFilter: IntArray?,
inSingleFileFragment: Boolean = false
) { ) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val toHide = getHiddenActions(componentsGetter, numberOfAllFiles, files, isOverflow) val toHide = getHiddenActions(componentsGetter, numberOfAllFiles, files, isOverflow, inSingleFileFragment)
val availableActions = getActionsToShow(additionalFilter, toHide) val availableActions = getActionsToShow(additionalFilter, toHide)
updateStateLoaded(files, availableActions) updateStateLoaded(files, availableActions)
} }
@ -101,7 +106,8 @@ class FileActionsViewModel @Inject constructor(
componentsGetter: ComponentsGetter, componentsGetter: ComponentsGetter,
numberOfAllFiles: Int?, numberOfAllFiles: Int?,
files: Collection<OCFile>, files: Collection<OCFile>,
isOverflow: Boolean? isOverflow: Boolean?,
inSingleFileFragment: Boolean
): List<Int> { ): List<Int> {
return filterFactory.newInstance( return filterFactory.newInstance(
numberOfAllFiles ?: 1, numberOfAllFiles ?: 1,
@ -110,7 +116,7 @@ class FileActionsViewModel @Inject constructor(
isOverflow ?: false, isOverflow ?: false,
currentAccountProvider.user currentAccountProvider.user
) )
.getToHide(false) .getToHide(inSingleFileFragment)
} }
private fun getActionsToShow( private fun getActionsToShow(
@ -161,6 +167,7 @@ class FileActionsViewModel @Inject constructor(
const val ARG_FILES = "FILES" const val ARG_FILES = "FILES"
const val ARG_IS_OVERFLOW = "OVERFLOW" const val ARG_IS_OVERFLOW = "OVERFLOW"
const val ARG_ADDITIONAL_FILTER = "ADDITIONAL_FILTER" const val ARG_ADDITIONAL_FILTER = "ADDITIONAL_FILTER"
const val ARG_IN_SINGLE_FILE_FRAGMENT = "IN_SINGLE_FILE_FRAGMENT"
private val TAG = FileActionsViewModel::class.simpleName!! private val TAG = FileActionsViewModel::class.simpleName!!
} }

View file

@ -84,6 +84,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.core.content.res.ResourcesCompat; import androidx.core.content.res.ResourcesCompat;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import kotlin.text.Charsets;
/** /**
* Manager for concurrent access to thumbnails cache. * Manager for concurrent access to thumbnails cache.
@ -1415,7 +1416,7 @@ public final class ThumbnailsCacheManager {
GetMethod getMethod = null; GetMethod getMethod = null;
try { try {
String uri = mClient.getBaseUri() + "/index.php/core/preview.png?file=" 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"; + "&x=" + (pxW / 2) + "&y=" + (pxH / 2) + "&a=1&mode=cover&forceIcon=0";
Log_OC.d(TAG, "generate resized image: " + file.getFileName() + " URI: " + uri); Log_OC.d(TAG, "generate resized image: " + file.getFileName() + " URI: " + uri);
getMethod = new GetMethod(uri); getMethod = new GetMethod(uri);

View file

@ -185,23 +185,19 @@ public class FileMenuFilter {
return toHide; return toHide;
} }
private void filterShareFile(List<Integer> toHide, OCCapability capability) { private void filterShareFile(List<Integer> toHide, OCCapability capability) {
if (containsEncryptedFile() || (!isShareViaLinkAllowed() && !isShareWithUsersAllowed()) || if (!isSingleSelection() || containsEncryptedFile() ||
!isSingleSelection() || !isShareApiEnabled(capability) || !files.iterator().next().canReshare() (!isShareViaLinkAllowed() && !isShareWithUsersAllowed()) ||
|| overflowMenu) { !isShareApiEnabled(capability) || !files.iterator().next().canReshare()) {
toHide.add(R.id.action_send_share_file); toHide.add(R.id.action_send_share_file);
} }
} }
private void filterSendFiles(List<Integer> toHide, boolean inSingleFileFragment) { private void filterSendFiles(List<Integer> toHide, boolean inSingleFileFragment) {
boolean show = true; if ((overflowMenu || SEND_OFF.equalsIgnoreCase(context.getString(R.string.send_files_to_other_apps)) || containsEncryptedFile()) ||
if (overflowMenu || SEND_OFF.equalsIgnoreCase(context.getString(R.string.send_files_to_other_apps)) || containsEncryptedFile()) { (!inSingleFileFragment && (isSingleSelection() || !allFileDown())) ||
show = false; !toHide.contains(R.id.action_send_share_file)) {
}
if (!inSingleFileFragment && (isSingleSelection() || !anyFileDown())) {
show = false;
}
if (!show) {
toHide.add(R.id.action_send_file); toHide.add(R.id.action_send_file);
} }
} }
@ -543,6 +539,15 @@ public class FileMenuFilter {
return false; return false;
} }
private boolean allFileDown() {
for (OCFile file: files) {
if(!file.isDown()) {
return false;
}
}
return true;
}
private boolean allFavorites() { private boolean allFavorites() {
for (OCFile file : files) { for (OCFile file : files) {
if (!file.isFavorite()) { if (!file.isFavorite()) {

View file

@ -580,8 +580,7 @@ public abstract class DrawerActivity extends ToolbarActivity
for (ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) { for (ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) {
if (menuItem.getTitle().toString().equalsIgnoreCase(link.getName())) { if (menuItem.getTitle().toString().equalsIgnoreCase(link.getName())) {
if (link.getRedirect()) { if (link.getRedirect()) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link.getUrl())); DisplayUtils.startLinkIntent(this, link.getUrl());
DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available);
} else { } else {
Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.getName()); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.getName());

View file

@ -680,19 +680,14 @@ public abstract class FileActivity extends DrawerActivity
DisplayUtils.showSnackMessage(activity, R.string.dev_version_no_information_available, Snackbar.LENGTH_LONG); DisplayUtils.showSnackMessage(activity, R.string.dev_version_no_information_available, Snackbar.LENGTH_LONG);
} }
if (latestVersion > currentVersion) { if (latestVersion > currentVersion) {
String devApkLink = activity.getString(R.string.dev_link) + latestVersion + ".apk";
if (openDirectly) { if (openDirectly) {
String devApkLink = (String) activity.getText(R.string.dev_link) + latestVersion + ".apk"; DisplayUtils.startLinkIntent(activity, devApkLink);
Uri uriUrl = Uri.parse(devApkLink);
Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available);
} else { } else {
Snackbar.make(activity.findViewById(android.R.id.content), R.string.dev_version_new_version_available, Snackbar.make(activity.findViewById(android.R.id.content), R.string.dev_version_new_version_available,
Snackbar.LENGTH_LONG) Snackbar.LENGTH_LONG)
.setAction(activity.getString(R.string.version_dev_download), v -> { .setAction(activity.getString(R.string.version_dev_download), v -> {
String devApkLink = (String) activity.getText(R.string.dev_link) + latestVersion + ".apk"; DisplayUtils.startLinkIntent(activity, devApkLink);
Uri uriUrl = Uri.parse(devApkLink);
Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available);
}).show(); }).show();
} }
} else { } else {

View file

@ -1093,6 +1093,8 @@ public class FileDisplayActivity extends FileActivity
} }
} else if (leftFragment instanceof PreviewTextStringFragment) { } else if (leftFragment instanceof PreviewTextStringFragment) {
createMinFragments(null); createMinFragments(null);
} else if (leftFragment instanceof PreviewPdfFragment) {
super.onBackPressed();
} else { } else {
// pop back // pop back
resetScrolling(true); resetScrolling(true);

View file

@ -41,21 +41,10 @@ import android.os.Looper;
import android.os.Parcelable; import android.os.Parcelable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.LayoutInflater; import android.view.*;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams;
import android.widget.AdapterView; import android.widget.*;
import android.widget.AdapterView.OnItemClickListener; 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.google.android.material.button.MaterialButton;
import com.nextcloud.client.account.User; 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.syncadapter.FileSyncAdapter;
import com.owncloud.android.ui.adapter.UploaderAdapter; import com.owncloud.android.ui.adapter.UploaderAdapter;
import com.owncloud.android.ui.asynctasks.CopyAndUploadContentUrisTask; import com.owncloud.android.ui.asynctasks.CopyAndUploadContentUrisTask;
import com.owncloud.android.ui.dialog.AccountChooserInterface; import com.owncloud.android.ui.dialog.*;
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.fragment.TaskRetainerFragment; import com.owncloud.android.ui.fragment.TaskRetainerFragment;
import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.ui.helpers.FileOperationsHelper;
import com.owncloud.android.ui.helpers.UriUploader; import com.owncloud.android.ui.helpers.UriUploader;
import com.owncloud.android.utils.DataHolderUtil; import com.owncloud.android.utils.*;
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.theme.ViewThemeUtils; import com.owncloud.android.utils.theme.ViewThemeUtils;
import java.io.File; import java.io.File;
@ -99,15 +80,7 @@ import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.*;
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 javax.inject.Inject; import javax.inject.Inject;
@ -1039,8 +1012,8 @@ public class ReceiveExternalFilesActivity extends FileActivity
inflater.inflate(R.menu.activity_receive_external_files, menu); inflater.inflate(R.menu.activity_receive_external_files, menu);
if (!isHaveMultipleAccount()) { if (!isHaveMultipleAccount()) {
MenuItem switchAccountMenu = menu.findItem(R.id.action_switch_account); menu.findItem(R.id.action_switch_account).setVisible(false);
switchAccountMenu.setVisible(false); menu.findItem(R.id.action_create_dir).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
} }
// tint search event // tint search event

View file

@ -361,9 +361,7 @@ public class SettingsActivity extends PreferenceActivity
String imprintWeb = getString(R.string.url_imprint); String imprintWeb = getString(R.string.url_imprint);
if (!imprintWeb.isEmpty()) { if (!imprintWeb.isEmpty()) {
Uri uriUrl = Uri.parse(imprintWeb); DisplayUtils.startLinkIntent(this, imprintWeb);
Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available);
} }
//ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG"); //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG");
return true; return true;
@ -539,12 +537,7 @@ public class SettingsActivity extends PreferenceActivity
if (pHelp != null) { if (pHelp != null) {
if (helpEnabled) { if (helpEnabled) {
pHelp.setOnPreferenceClickListener(preference -> { pHelp.setOnPreferenceClickListener(preference -> {
String helpWeb = getString(R.string.url_help); DisplayUtils.startLinkIntent(this, 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);
}
return true; return true;
}); });
} else { } else {
@ -897,9 +890,7 @@ public class SettingsActivity extends PreferenceActivity
startActivity(installIntent); startActivity(installIntent);
} else { } else {
// no f-droid market app or Play store installed --> launch browser for f-droid url // no f-droid market app or Play store installed --> launch browser for f-droid url
Intent downloadIntent = new Intent(Intent.ACTION_VIEW, DisplayUtils.startLinkIntent(this, "https://f-droid.org/packages/at.bitfire.davdroid/");
Uri.parse("https://f-droid.org/repository/browse/?fdid=at.bitfire.davdroid"));
DisplayUtils.startIntentIfAppAvailable(downloadIntent, this, R.string.no_browser_available);
DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_no_store_error); DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_no_store_error);
} }

View file

@ -123,7 +123,8 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
subject = subject + ""; subject = subject + "";
holder.binding.subject.setTypeface(holder.binding.subject.getTypeface(), holder.binding.subject.setTypeface(holder.binding.subject.getTypeface(),
Typeface.BOLD); Typeface.BOLD);
holder.binding.subject.setOnClickListener(v -> openLink(notification.getLink())); holder.binding.subject.setOnClickListener(v -> DisplayUtils.startLinkIntent(notificationsActivity,
notification.getLink()));
holder.binding.subject.setText(subject); holder.binding.subject.setText(subject);
} else { } else {
if (!TextUtils.isEmpty(notification.subjectRich)) { if (!TextUtils.isEmpty(notification.subjectRich)) {
@ -329,8 +330,7 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
closingBrace = openingBrace + name.length(); closingBrace = openingBrace + name.length();
ssb.setSpan(styleSpanBold, openingBrace, closingBrace, 0); ssb.setSpan(styleSpanBold, openingBrace, closingBrace, 0);
ssb.setSpan(foregroundColorSpanBlack, openingBrace, closingBrace, ssb.setSpan(foregroundColorSpanBlack, openingBrace, closingBrace, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
openingBrace = text.indexOf('{', closingBrace); openingBrace = text.indexOf('{', closingBrace);
} }
@ -382,12 +382,6 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
.into(itemViewType); .into(itemViewType);
} }
private void openLink(String link) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
DisplayUtils.startIntentIfAppAvailable(intent, notificationsActivity, R.string.no_browser_available);
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return notificationsList.size(); return notificationsList.size();

View file

@ -87,6 +87,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
private static final String TAG = ChooseRichDocumentsTemplateDialogFragment.class.getSimpleName(); private static final String TAG = ChooseRichDocumentsTemplateDialogFragment.class.getSimpleName();
private static final String DOT = "."; private static final String DOT = ".";
public static final int SINGLE_TEMPLATE = 1; public static final int SINGLE_TEMPLATE = 1;
private static final String WAIT_DIALOG_TAG = "WAIT";
private Set<String> fileNames; private Set<String> fileNames;
@ -99,6 +100,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
private OCFile parentFolder; private OCFile parentFolder;
private OwnCloudClient client; private OwnCloudClient client;
private Button positiveButton; private Button positiveButton;
private DialogFragment waitDialog;
public enum Type { public enum Type {
DOCUMENT, DOCUMENT,
@ -234,6 +236,8 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
} }
private void createFromTemplate(Template template, String path) { 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(); new CreateFileFromTemplateTask(this, client, template, path, currentAccount.getUser()).execute();
} }
@ -364,8 +368,13 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
ChooseRichDocumentsTemplateDialogFragment fragment = chooseTemplateDialogFragmentWeakReference.get(); ChooseRichDocumentsTemplateDialogFragment fragment = chooseTemplateDialogFragmentWeakReference.get();
if (fragment != null && fragment.isAdded()) { if (fragment != null && fragment.isAdded()) {
if (fragment.waitDialog != null) {
fragment.waitDialog.dismiss();
}
if (url.isEmpty()) { 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 { } else {
Intent collaboraWebViewIntent = new Intent(MainApp.getAppContext(), RichDocumentsEditorWebView.class); Intent collaboraWebViewIntent = new Intent(MainApp.getAppContext(), RichDocumentsEditorWebView.class);
collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Collabora"); collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Collabora");
@ -416,7 +425,8 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
if (fragment != null) { if (fragment != null) {
if (templateList.isEmpty()) { 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 { } else {
if (templateList.size() == SINGLE_TEMPLATE) { if (templateList.size() == SINGLE_TEMPLATE) {
fragment.onTemplateChosen(templateList.get(0)); fragment.onTemplateChosen(templateList.get(0));

View file

@ -282,7 +282,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
R.id.action_copy, R.id.action_copy,
R.id.action_stream_media, R.id.action_stream_media,
R.id.action_send_share_file, R.id.action_send_share_file,
R.id.action_select_all_action_menu)); R.id.action_pin_to_homescreen
));
if (getFile().isFolder()) { if (getFile().isFolder()) {
additionalFilter.add(R.id.action_send_file); additionalFilter.add(R.id.action_send_file);
additionalFilter.add(R.id.action_sync_file); additionalFilter.add(R.id.action_sync_file);

View file

@ -1524,7 +1524,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
setTitle(R.string.drawer_item_shared); setTitle(R.string.drawer_item_shared);
break; break;
default: default:
setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext())); setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext()), false);
break; break;
} }
} }
@ -1591,7 +1591,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
((FileDisplayActivity) activity).initSyncBroadcastReceiver(); ((FileDisplayActivity) activity).initSyncBroadcastReceiver();
} }
setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext())); setTitle(themeUtils.getDefaultDisplayNameForRootFolder(getContext()), false);
activity.getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT); 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) { 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) { if (getActivity() != null) {
final ActionBar actionBar = ((FileDisplayActivity) getActivity()).getSupportActionBar(); final ActionBar actionBar = ((FileDisplayActivity) getActivity()).getSupportActionBar();
final Context context = getContext(); final Context context = getContext();
if (actionBar != null && context != null) { if (actionBar != null && context != null) {
viewThemeUtils.files.themeActionBar(context, actionBar, title, true); viewThemeUtils.files.themeActionBar(context, actionBar, title, showBackAsMenu);
} }
} }
}); });

View file

@ -375,11 +375,11 @@ public class PreviewImageFragment extends FileFragment implements Injectable {
Arrays.asList( Arrays.asList(
R.id.action_rename_file, R.id.action_rename_file,
R.id.action_sync_file, R.id.action_sync_file,
R.id.action_select_all,
R.id.action_move, R.id.action_move,
R.id.action_copy, R.id.action_copy,
R.id.action_favorite, 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()) { if (getFile() != null && getFile().isSharedWithMe() && !getFile().canReshare()) {
additionalFilter.add(R.id.action_send_share_file); additionalFilter.add(R.id.action_send_share_file);

View file

@ -424,7 +424,6 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene
Arrays.asList( Arrays.asList(
R.id.action_rename_file, R.id.action_rename_file,
R.id.action_sync_file, R.id.action_sync_file,
R.id.action_select_all,
R.id.action_move, R.id.action_move,
R.id.action_copy, R.id.action_copy,
R.id.action_favorite, R.id.action_favorite,

View file

@ -300,11 +300,11 @@ public class PreviewTextFileFragment extends PreviewTextFragment {
Arrays.asList( Arrays.asList(
R.id.action_rename_file, R.id.action_rename_file,
R.id.action_sync_file, R.id.action_sync_file,
R.id.action_select_all,
R.id.action_move, R.id.action_move,
R.id.action_copy, R.id.action_copy,
R.id.action_favorite, 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()) { if (getFile() != null && getFile().isSharedWithMe() && !getFile().canReshare()) {
additionalFilter.add(R.id.action_send_share_file); additionalFilter.add(R.id.action_send_share_file);

View file

@ -20,10 +20,8 @@
package com.owncloud.android.ui.preview; package com.owncloud.android.ui.preview;
import android.app.Activity; import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.Html; import android.text.Html;
@ -195,10 +193,7 @@ public abstract class PreviewTextFragment extends FileFragment implements Search
@Override @Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.linkResolver((view, link) -> { builder.linkResolver((view, link) -> DisplayUtils.startLinkIntent(activity, link));
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available);
});
} }
}) })
.usePlugin(TablePlugin.create(activity)) .usePlugin(TablePlugin.create(activity))

View file

@ -150,6 +150,9 @@ class PreviewVideoFullscreenDialog(
} }
mExoPlayer.addListener(playListener) mExoPlayer.addListener(playListener)
playingStateListener = playListener playingStateListener = playListener
// Run once to set initial state of play or pause buttons
playListener.onIsPlayingChanged(sourceExoPlayer.isPlaying)
} }
override fun onBackPressed() { override fun onBackPressed() {

View file

@ -775,13 +775,15 @@ public final class DisplayUtils {
startLinkIntent(activity, activity.getString(link)); startLinkIntent(activity, activity.getString(link));
} }
static public void startLinkIntent(Activity activity, Uri url) { static public void startLinkIntent(Activity activity, String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, url); if (!TextUtils.isEmpty(url)) {
DisplayUtils.startIntentIfAppAvailable(intent, activity, R.string.no_browser_available); startLinkIntent(activity, Uri.parse(url));
}
} }
static public void startLinkIntent(Activity activity, String url) { static public void startLinkIntent(Activity activity, Uri uri) {
startLinkIntent(activity, Uri.parse(url)); 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) { static public void startIntentIfAppAvailable(Intent intent, Activity activity, @StringRes int error) {

View file

@ -38,6 +38,7 @@
android:id="@android:id/list" android:id="@android:id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/bg_default"
android:divider="@color/transparent" android:divider="@color/transparent"
android:dividerHeight="0dip" android:dividerHeight="0dip"
android:nestedScrollingEnabled="true" android:nestedScrollingEnabled="true"
@ -58,6 +59,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/bg_default"
android:gravity="center" android:gravity="center"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="@dimen/standard_padding"> android:padding="@dimen/standard_padding">

View file

@ -36,5 +36,6 @@
android:icon="@drawable/ic_action_create_dir" android:icon="@drawable/ic_action_create_dir"
android:orderInCategory="1" android:orderInCategory="1"
android:title="@string/actionbar_mkdir" android:title="@string/actionbar_mkdir"
app:showAsAction="never" /> app:iconTint="?attr/colorOnSurface"
app:showAsAction="ifRoom"/>
</menu> </menu>

View file

@ -39,13 +39,7 @@
android:icon="@drawable/ic_action_create_dir" android:icon="@drawable/ic_action_create_dir"
android:orderInCategory="1" android:orderInCategory="1"
android:title="@string/actionbar_mkdir" android:title="@string/actionbar_mkdir"
app:showAsAction="never"/> app:iconTint="?attr/colorOnSurface"
<item
android:id="@+id/action_select_all"
android:contentDescription="@string/select_all"
android:icon="@drawable/ic_select_all"
android:orderInCategory="1"
android:title="@string/select_all"
app:showAsAction="never"/> app:showAsAction="never"/>
</menu> </menu>

View file

@ -17,7 +17,7 @@ buildscript {
fidoVersion = "4.1.0-patch1" fidoVersion = "4.1.0-patch1"
checkerVersion = "3.21.2" checkerVersion = "3.21.2"
exoplayerVersion = "2.19.0" exoplayerVersion = "2.19.0"
documentScannerVersion = "1.0.1" documentScannerVersion = "1.1.1"
roomVersion = "2.5.2" roomVersion = "2.5.2"
ciBuild = System.getenv("CI") == "true" ciBuild = System.getenv("CI") == "true"

View file

@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE DO NOT TOUCH; GENERATED BY DRONE
<span class="mdl-layout-title">Lint Report: 77 warnings</span> <span class="mdl-layout-title">Lint Report: 79 warnings</span>