From 6bb7dc86c7aa22eb0335394ab51ba37f244646d4 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 11 May 2018 11:50:56 +0200 Subject: [PATCH 01/75] butterknife for details fragment + proper empty content handling --- drawable_resources/alert-octagon.svg | 1 + .../android/ui/activity/ToolbarActivity.java | 62 +++- .../ui/fragment/FileDetailFragment.java | 348 +++++++++--------- .../owncloud/android/utils/ThemeUtils.java | 8 + src/main/res/drawable/ic_alert_octagon.xml | 23 ++ src/main/res/layout/file_details_empty.xml | 32 -- src/main/res/layout/file_details_fragment.xml | 18 +- 7 files changed, 278 insertions(+), 214 deletions(-) create mode 100644 drawable_resources/alert-octagon.svg create mode 100644 src/main/res/drawable/ic_alert_octagon.xml delete mode 100644 src/main/res/layout/file_details_empty.xml diff --git a/drawable_resources/alert-octagon.svg b/drawable_resources/alert-octagon.svg new file mode 100644 index 000000000000..590dfad0802f --- /dev/null +++ b/drawable_resources/alert-octagon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 509b92ba3156..90eb9dbf4683 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -22,17 +22,21 @@ package com.owncloud.android.ui.activity; +import android.graphics.Bitmap; import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; +import android.widget.ImageView; import android.widget.ProgressBar; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.utils.ThemeUtils; /** @@ -40,6 +44,7 @@ */ public abstract class ToolbarActivity extends BaseActivity { private ProgressBar mProgressBar; + private ImageView mPreviewImage; @Override protected void onCreate(Bundle savedInstanceState) { @@ -66,6 +71,8 @@ protected void setupToolbar(boolean useBackgroundImage) { ThemeUtils.colorToolbarProgressBar(this, ThemeUtils.primaryColor(this)); } + mPreviewImage = findViewById(R.id.preview_image); + ThemeUtils.colorStatusBar(this, primaryDarkColor); if (toolbar.getOverflowIcon() != null) { @@ -146,7 +153,60 @@ public boolean isRoot(OCFile file) { * @param indeterminate true to enable the indeterminate mode */ public void setIndeterminate(boolean indeterminate) { - mProgressBar.setIndeterminate(indeterminate); + if (mProgressBar != null) { + mProgressBar.setIndeterminate(indeterminate); + } + } + + /** + * Change the visibility for the toolbar's progress bar. + * + * @param visibility visibility of the progress bar + */ + public void setProgressBarVisibility(int visibility) { + if (mProgressBar != null) { + mProgressBar.setVisibility(visibility); + } + } + + /** + * Change the visibility for the toolbar's preview image. + * + * @param visibility visibility of the preview image + */ + public void setPreviewImageVisibility(int visibility) { + if (mPreviewImage != null) { + mPreviewImage.setVisibility(visibility); + } + } + + /** + * Change the bitmap for the toolbar's preview image. + * + * @param bitmap bitmap of the preview image + */ + public void setPreviewImageBitmap(Bitmap bitmap) { + if (mPreviewImage != null) { + mPreviewImage.setImageBitmap(bitmap); + } + } + + /** + * Change the drawable for the toolbar's preview image. + * + * @param drawable drawable of the preview image + */ + public void setPreviewImageDrawable(Drawable drawable) { + if (mPreviewImage != null) { + mPreviewImage.setImageDrawable(drawable); + } + } + + /** + * get the toolbar's preview image view. + */ + public ImageView getPreviewImageView() { + return mPreviewImage; } /** diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 108b9266cb41..ba15208944e5 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -22,10 +22,11 @@ package com.owncloud.android.ui.fragment; import android.accounts.Account; +import android.content.Context; import android.graphics.Bitmap; -import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v4.view.ViewPager; @@ -35,7 +36,9 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.ImageButton; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.TextView; @@ -62,27 +65,75 @@ import java.lang.ref.WeakReference; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Optional; +import butterknife.Unbinder; /** * This Fragment is used to display the details about a file. */ public class FileDetailFragment extends FileFragment implements OnClickListener { + private static final String TAG = FileDetailFragment.class.getSimpleName(); + public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT"; + public static final String FTAG_RENAME_FILE = "RENAME_FILE_FRAGMENT"; + + private static final String ARG_FILE = "FILE"; + private static final String ARG_ACCOUNT = "ACCOUNT"; + + @Nullable @BindView(R.id.fdProgressBlock) + View downloadProgressContainer; + + @Nullable @BindView(R.id.fdCancelBtn) + ImageButton cancelButton; + + @Nullable @BindView(R.id.fdProgressBar) + ProgressBar progressBar; + + @Nullable @BindView(R.id.fdProgressText) + TextView progressText; + + @Nullable @BindView(R.id.fdFilename) + TextView fileName; + + @Nullable @BindView(R.id.fdSize) + TextView fileSize; + + @Nullable @BindView(R.id.fdModified) + TextView fileModifiedTimestamp; + + @Nullable @BindView(R.id.fdFavorite) + ImageView favoriteIcon; + + @Nullable @BindView(R.id.overflow_menu) + ImageView overflowMenu; + + @Nullable @BindView(R.id.tab_layout) + TabLayout tabLayout; + + @Nullable @BindView(R.id.pager) + ViewPager viewPager; + + @Nullable @BindView(R.id.empty_list_view_text) + protected TextView emptyContentMessage; + + @Nullable @BindView(R.id.empty_list_view_headline) + protected TextView emptyContentHeadline; + + @Nullable @BindView(R.id.empty_list_icon) + protected ImageView emptyContentIcon; + + @Nullable @BindView(R.id.empty_list_progress) + protected ProgressBar emptyProgressBar; private int layout; private View view; - private ImageView previewImage; - private ProgressBar toolbarProgressBar; private boolean previewLoaded; private Account account; + private Unbinder unbinder; public ProgressListener progressListener; - - private static final String TAG = FileDetailFragment.class.getSimpleName(); - public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT"; - public static final String FTAG_RENAME_FILE = "RENAME_FILE_FRAGMENT"; - - private static final String ARG_FILE = "FILE"; - private static final String ARG_ACCOUNT = "ACCOUNT"; + private ToolbarActivity activity; /** * Public factory method to create new FileDetailFragment instances. @@ -111,7 +162,7 @@ public static FileDetailFragment newInstance(OCFile fileToDetail, Account accoun public FileDetailFragment() { super(); account = null; - layout = R.layout.file_details_empty; + layout = R.layout.empty_list; progressListener = null; } @@ -119,17 +170,21 @@ public FileDetailFragment() { public void onResume() { super.onResume(); - if (previewImage != null && MimeTypeUtil.isImage(getFile()) && previewLoaded) { + if (getFile()!= null && MimeTypeUtil.isImage(getFile()) && previewLoaded) { activatePreviewImage(); } } private void activatePreviewImage() { - previewImage.setVisibility(View.VISIBLE); - toolbarProgressBar.setVisibility(View.GONE); - ((ToolbarActivity) getActivity()).getSupportActionBar().setTitle(null); - ((ToolbarActivity) getActivity()).getSupportActionBar().setBackgroundDrawable(null); - makeStatusBarBlack(); + if (activity != null) { + activity.setPreviewImageVisibility(View.VISIBLE); + activity.setProgressBarVisibility(View.GONE); + ThemeUtils.setStatusBarColor(activity, activity.getResources().getColor(R.color.black)); + if (activity.getSupportActionBar() != null) { + activity.getSupportActionBar().setTitle(null); + activity.getSupportActionBar().setBackgroundDrawable(null); + } + } } @Override @@ -150,32 +205,40 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT); } - toolbarProgressBar = getActivity().findViewById(R.id.progressBar); - if (getFile() != null && account != null) { layout = R.layout.file_details_fragment; } view = inflater.inflate(layout, null); - - if (layout == R.layout.file_details_fragment) { - ProgressBar progressBar = view.findViewById(R.id.fdProgressBar); + unbinder = ButterKnife.bind(this, view); + + return view; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + if (getFile() != null && account != null) { ThemeUtils.colorHorizontalProgressBar(progressBar, ThemeUtils.primaryAccentColor(getContext())); progressListener = new ProgressListener(progressBar); - view.findViewById(R.id.fdCancelBtn).setOnClickListener(this); - view.findViewById(R.id.fdFavorite).setOnClickListener(this); - view.findViewById(R.id.overflow_menu).setOnClickListener(this); - previewImage = getActivity().findViewById(R.id.preview_image); + cancelButton.setOnClickListener(this); + favoriteIcon.setOnClickListener(this); + overflowMenu.setOnClickListener(this); // TODO use whenever we switch to use glide for preview images /* - if (getFile() != null && account != null && MimeTypeUtil.isImage(getFile())) { + if (MimeTypeUtil.isImage(getFile())) { setHeaderImage(); } */ - } - updateFileDetails(false, false); - return view; + updateFileDetails(false, false); + } else { + emptyContentMessage.setText(R.string.filedetails_select_file); + emptyContentMessage.setVisibility(View.VISIBLE); + emptyContentHeadline.setText(R.string.common_error); + emptyContentIcon.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_alert_octagon)); + emptyContentIcon.setVisibility(View.VISIBLE); + emptyProgressBar.setVisibility(View.GONE); + } } // TODO use whenever we switch to use glide for preview images /* @@ -236,8 +299,7 @@ public void onOverflowIconClicked(View view) { popup.show(); } - private void setupViewPager(View view) { - TabLayout tabLayout = view.findViewById(R.id.tab_layout); + private void setupViewPager() { tabLayout.removeAllTabs(); tabLayout.addTab(tabLayout.newTab().setText(R.string.drawer_item_activities)); @@ -246,7 +308,6 @@ private void setupViewPager(View view) { tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); tabLayout.setSelectedTabIndicatorColor(ThemeUtils.primaryAccentColor(getContext())); - final ViewPager viewPager = view.findViewById(R.id.pager); final FileDetailTabAdapter adapter = new FileDetailTabAdapter (getFragmentManager(), getFile(), account); viewPager.setAdapter(adapter); @@ -285,17 +346,32 @@ public void onStart() { @Override public void onStop() { leaveTransferProgress(); - if (previewImage != null) { - previewImage.setVisibility(View.GONE); - toolbarProgressBar.setVisibility(View.VISIBLE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getActivity().getWindow().setStatusBarColor(ThemeUtils.primaryDarkColor(getContext())); - } + + if(activity != null) { + activity.setPreviewImageVisibility(View.GONE); + activity.setProgressBarVisibility(View.VISIBLE); + ThemeUtils.setStatusBarColor(activity, ThemeUtils.primaryColor(getContext())); } super.onStop(); } + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof ToolbarActivity) { + activity = (ToolbarActivity) context; + } else { + activity = null; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + unbinder.unbind(); + } + @Override public View getView() { return super.getView() == null ? view : super.getView(); @@ -405,16 +481,11 @@ public void onClick(View v) { } case R.id.fdFavorite: { if (getFile().getIsFavorite()) { - ((ImageView)getView().findViewById(R.id.fdFavorite)). - setImageDrawable(getResources() - .getDrawable(R.drawable.ic_star_outline)); mContainerActivity.getFileOperationsHelper().toggleFavoriteFile(getFile(), false); } else { - ((ImageView)getView().findViewById(R.id.fdFavorite)) - .setImageDrawable(getResources() - .getDrawable(R.drawable.ic_star)); mContainerActivity.getFileOperationsHelper().toggleFavoriteFile(getFile(), true); } + setFavoriteIconStatus(!getFile().getIsFavorite()); break; } case R.id.overflow_menu: { @@ -433,7 +504,7 @@ public void onClick(View v) { * @return True when the fragment was created with the empty layout. */ public boolean isEmpty() { - return (layout == R.layout.file_details_empty || getFile() == null || account == null); + return (layout == R.layout.empty_list || getFile() == null || account == null); } /** @@ -471,16 +542,15 @@ public void updateFileDetails(boolean transferring, boolean refresh) { OCFile file = getFile(); // set file details - setFilename(file.getFileName()); - setFiletype(file); - setFilesize(file.getFileLength()); - setTimeModified(file.getModificationTimestamp()); - - if (file.getIsFavorite()) { - ((ImageView)getView().findViewById(R.id.fdFavorite)).setImageDrawable(getResources().getDrawable(R.drawable.ic_star)); + if (MimeTypeUtil.isImage(file)) { + fileName.setText(file.getFileName()); } else { - ((ImageView)getView().findViewById(R.id.fdFavorite)).setImageDrawable(getResources().getDrawable(R.drawable.ic_star_outline)); + fileName.setVisibility(View.GONE); } + fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength())); + fileModifiedTimestamp.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())); + setFilePreview(file); + setFavoriteIconStatus(file.getIsFavorite()); // configure UI for depending upon local state of the file FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); @@ -502,11 +572,19 @@ public void updateFileDetails(boolean transferring, boolean refresh) { } } - setupViewPager(getView()); + setupViewPager(); getView().invalidate(); } + private void setFavoriteIconStatus(boolean isFavorite) { + if (isFavorite) { + favoriteIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_star)); + } else { + favoriteIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_star_outline)); + } + } + /** * Checks if the fragment is ready to show details of a OCFile * @@ -517,124 +595,68 @@ private boolean readyToShow() { } /** - * Updates the filename in view + * Updates the file preview if possible * - * @param filename to set + * @param file a {@link OCFile} to be previewed */ - private void setFilename(String filename) { - TextView tv = getView().findViewById(R.id.fdFilename); - if (tv != null) { - tv.setText(filename); - } - } + private void setFilePreview(OCFile file) { + Bitmap resizedImage; - /** - * Updates the MIME type in view - * @param file : An {@link OCFile} - */ - private void setFiletype(OCFile file) { - ImageView iv = getView().findViewById(R.id.fdIcon); - View v = getView().findViewById(R.id.fdIcon_divider); + if (MimeTypeUtil.isImage(file) && activity != null) { + String tagId = String.valueOf(ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + getFile().getRemoteId()); + resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache(tagId); - if (iv != null) { - iv.setTag(file.getFileId()); - // Name of the file, to deduce the icon to use in case the MIME type is not precise enough - iv.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(), account, - getContext())); + if (resizedImage != null && !file.needsUpdateThumbnail()) { + activity.setPreviewImageBitmap(resizedImage); + activatePreviewImage(); + previewLoaded = true; + } else { + // show thumbnail while loading resized image + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + String.valueOf(ThumbnailsCacheManager.PREFIX_THUMBNAIL + getFile().getRemoteId())); - Bitmap resizedImage; + if (thumbnail != null) { + activity.setPreviewImageBitmap(thumbnail); + } else { + thumbnail = ThumbnailsCacheManager.mDefaultImg; + } - if (MimeTypeUtil.isImage(file)) { - String tagId = String.valueOf(ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + getFile().getRemoteId()); - resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache(tagId); + // generate new resized image + if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(getFile(), activity.getPreviewImageView()) && + mContainerActivity.getStorageManager() != null) { + final ThumbnailsCacheManager.ResizedImageGenerationTask task = + new ThumbnailsCacheManager.ResizedImageGenerationTask(FileDetailFragment.this, + activity.getPreviewImageView(), + mContainerActivity.getStorageManager(), + mContainerActivity.getStorageManager().getAccount()); + + if (resizedImage == null) { + resizedImage = thumbnail; + } - if (resizedImage != null && !file.needsUpdateThumbnail()) { - iv.setVisibility(View.GONE); - v.setVisibility(View.GONE); - previewImage.setImageBitmap(resizedImage); + final ThumbnailsCacheManager.AsyncResizedImageDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncResizedImageDrawable( + MainApp.getAppContext().getResources(), + resizedImage, + task + ); + + activity.setPreviewImageDrawable(asyncDrawable); activatePreviewImage(); previewLoaded = true; - } else { - // show thumbnail while loading resized image - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(ThumbnailsCacheManager.PREFIX_THUMBNAIL + getFile().getRemoteId())); - - if (thumbnail != null) { - iv.setImageBitmap(thumbnail); - } else { - thumbnail = ThumbnailsCacheManager.mDefaultImg; - } - - // generate new resized image - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(getFile(), iv) && - mContainerActivity.getStorageManager() != null) { - final ThumbnailsCacheManager.ResizedImageGenerationTask task = - new ThumbnailsCacheManager.ResizedImageGenerationTask(FileDetailFragment.this, - iv, - mContainerActivity.getStorageManager(), - mContainerActivity.getStorageManager().getAccount()); - if (resizedImage == null) { - resizedImage = thumbnail; - } - final ThumbnailsCacheManager.AsyncResizedImageDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncResizedImageDrawable( - MainApp.getAppContext().getResources(), - resizedImage, - task - ); - iv.setVisibility(View.GONE); - v.setVisibility(View.GONE); - previewImage.setImageDrawable(asyncDrawable); - activatePreviewImage(); - previewLoaded = true; - task.execute(getFile()); - } + task.execute(getFile()); } - } else { - iv.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimetype(), file.getFileName(), account, - getContext())); } } } - private void makeStatusBarBlack() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.black)); - } - } - - /** - * Updates the file size in view - * - * @param fileSize in bytes to set - */ - private void setFilesize(long fileSize) { - TextView tv = getView().findViewById(R.id.fdSize); - if (tv != null) { - tv.setText(DisplayUtils.bytesToHumanReadable(fileSize)); - } - } - - /** - * Updates the time that the file was last modified - * - * @param milliseconds Unix time to set - */ - private void setTimeModified(long milliseconds) { - TextView tv = getView().findViewById(R.id.fdModified); - if (tv != null) { - tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds)); - } - } - /** * Enables or disables buttons for a file being downloaded */ private void setButtonsForTransferring() { if (!isEmpty()) { // show the progress bar for the transfer - getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE); - TextView progressText = getView().findViewById(R.id.fdProgressText); + downloadProgressContainer.setVisibility(View.VISIBLE); progressText.setVisibility(View.VISIBLE); FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); @@ -656,9 +678,7 @@ private void setButtonsForTransferring() { private void setButtonsForDown() { if (!isEmpty()) { // hides the progress bar - getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE); - TextView progressText = getView().findViewById(R.id.fdProgressText); - progressText.setVisibility(View.GONE); + downloadProgressContainer.setVisibility(View.GONE); } } @@ -668,9 +688,7 @@ private void setButtonsForDown() { private void setButtonsForRemote() { if (!isEmpty()) { // hides the progress bar - getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE); - TextView progressText = getView().findViewById(R.id.fdProgressText); - progressText.setVisibility(View.GONE); + downloadProgressContainer.setVisibility(View.GONE); } } @@ -706,25 +724,25 @@ public void leaveTransferProgress() { * Helper class responsible for updating the progress bar shown for file downloading. */ private class ProgressListener implements OnDatatransferProgressListener { - private int mLastPercent = 0; - private WeakReference mProgressBar; + private int lastPercent = 0; + private WeakReference progressBarReference; ProgressListener(ProgressBar progressBar) { - mProgressBar = new WeakReference<>(progressBar); + progressBarReference = new WeakReference<>(progressBar); } @Override public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) { int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer)); - if (percent != mLastPercent) { - ProgressBar pb = mProgressBar.get(); + if (percent != lastPercent) { + ProgressBar pb = progressBarReference.get(); if (pb != null) { pb.setProgress(percent); pb.postInvalidate(); } } - mLastPercent = percent; + lastPercent = percent; } } } diff --git a/src/main/java/com/owncloud/android/utils/ThemeUtils.java b/src/main/java/com/owncloud/android/utils/ThemeUtils.java index 2f29aa72b010..07822a4d1427 100644 --- a/src/main/java/com/owncloud/android/utils/ThemeUtils.java +++ b/src/main/java/com/owncloud/android/utils/ThemeUtils.java @@ -23,6 +23,7 @@ package com.owncloud.android.utils; import android.accounts.Account; +import android.app.Activity; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; @@ -44,6 +45,7 @@ import android.support.v7.widget.SwitchCompat; import android.text.Html; import android.text.Spanned; +import android.view.Window; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.SeekBar; @@ -212,6 +214,12 @@ public static String getDefaultDisplayNameForRootFolder(Context context) { } } + public static void setStatusBarColor(Activity activity, @ColorInt int color) { + if (activity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().setStatusBarColor(color); + } + } + /** * Adjust lightness of given color * diff --git a/src/main/res/drawable/ic_alert_octagon.xml b/src/main/res/drawable/ic_alert_octagon.xml new file mode 100644 index 000000000000..4c26c245f993 --- /dev/null +++ b/src/main/res/drawable/ic_alert_octagon.xml @@ -0,0 +1,23 @@ + + + + \ No newline at end of file diff --git a/src/main/res/layout/file_details_empty.xml b/src/main/res/layout/file_details_empty.xml deleted file mode 100644 index 3df52a590833..000000000000 --- a/src/main/res/layout/file_details_empty.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/main/res/layout/file_details_fragment.xml b/src/main/res/layout/file_details_fragment.xml index 9804d97e26e7..f41979dd410d 100644 --- a/src/main/res/layout/file_details_fragment.xml +++ b/src/main/res/layout/file_details_fragment.xml @@ -29,27 +29,13 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - - - Date: Fri, 11 May 2018 13:34:46 +0200 Subject: [PATCH 02/75] lambda: use method reference --- .../com/owncloud/android/ui/fragment/FileDetailFragment.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index ba15208944e5..cc46d50231c3 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -293,9 +293,7 @@ public void onOverflowIconClicked(View view) { popup.inflate(R.menu.file_actions_menu); prepareOptionsMenu(popup.getMenu()); - popup.setOnMenuItemClickListener(item -> { - return optionsItemSelected(item); - }); + popup.setOnMenuItemClickListener(this::optionsItemSelected); popup.show(); } From 683167805175ad290929e2435ea20bc55b084a33 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 11 May 2018 14:35:03 +0200 Subject: [PATCH 03/75] butterknife for the sharing tabs of file details --- .../FileDetailActivitiesFragment.java | 1 - .../fragment/FileDetailSharingFragment.java | 68 ++++++++++++------- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index bc8c3a93bb46..cbe6835beeb6 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -167,7 +167,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, private void setLoadingMessage() { emptyContentHeadline.setText(R.string.file_list_loading); emptyContentMessage.setText(""); - emptyContentIcon.setVisibility(View.GONE); emptyContentProgressBar.setVisibility(View.VISIBLE); } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index ccecf47cbb27..3cdfaa33594a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -42,6 +42,10 @@ import java.util.ArrayList; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; + public class FileDetailSharingFragment extends Fragment { private static final String ARG_FILE = "FILE"; @@ -53,6 +57,26 @@ public class FileDetailSharingFragment extends Fragment { private OCFile file; private Account account; + private Unbinder unbinder; + + @BindView(R.id.fdShareTitle) + TextView shareTitle; + + @BindView(R.id.fdShareWithUsersTitle) + TextView shareWithUsersTitle; + + @BindView(R.id.fdSharebyLink) + TextView sharebyLink; + + @BindView(R.id.fdShareLinkIcon) + ImageView linkIcon; + + @BindView(R.id.fdshareUsersList) + ListView usersList; + + @BindView(R.id.fdShareNoUsers) + TextView noList; + public static FileDetailSharingFragment newInstance(OCFile file, Account account) { FileDetailSharingFragment fragment = new FileDetailSharingFragment(); Bundle args = new Bundle(); @@ -76,19 +100,25 @@ public View onCreateView(@NonNull LayoutInflater inflater, } View view = inflater.inflate(R.layout.file_details_sharing_fragment, container, false); + unbinder = ButterKnife.bind(this, view); - setupView(view); + setupView(); return view; } - private void setupView(View view) { - ((TextView)view.findViewById(R.id.fdShareTitle)).setTextColor(ThemeUtils.primaryAccentColor(getContext())); - ((TextView)view.findViewById(R.id.fdShareWithUsersTitle)).setTextColor(ThemeUtils.primaryAccentColor(getContext())); + @Override + public void onDestroy() { + super.onDestroy(); + unbinder.unbind(); + } - setShareByLinkInfo(file.isSharedViaLink(), view); + private void setupView() { + shareTitle.setTextColor(ThemeUtils.primaryAccentColor(getContext())); + shareWithUsersTitle.setTextColor(ThemeUtils.primaryAccentColor(getContext())); - setShareWithUserInfo(view); + setShareByLinkInfo(file.isSharedViaLink()); + setShareWithUserInfo(); } /** @@ -96,22 +126,17 @@ private void setupView(View view) { * * @param isShareByLink flag is share by link is enable */ - private void setShareByLinkInfo(boolean isShareByLink, View view) { - TextView tv = view.findViewById(R.id.fdSharebyLink); - if (tv != null) { - tv.setText(isShareByLink ? R.string.filedetails_share_link_enable : + private void setShareByLinkInfo(boolean isShareByLink) { + sharebyLink.setText(isShareByLink ? R.string.filedetails_share_link_enable : R.string.filedetails_share_link_disable); - } - ImageView linkIcon = view.findViewById(R.id.fdShareLinkIcon); - if (linkIcon != null) { - linkIcon.setVisibility(isShareByLink ? View.VISIBLE : View.GONE); - } + + linkIcon.setVisibility(isShareByLink ? View.VISIBLE : View.GONE); } /** * Update Share With data */ - private void setShareWithUserInfo(View view){ + private void setShareWithUserInfo(){ // Get Users and Groups if (((FileActivity) getActivity()).getStorageManager() != null) { FileDataStorageManager fileDataStorageManager = ((FileActivity) getActivity()).getStorageManager(); @@ -120,11 +145,11 @@ private void setShareWithUserInfo(View view){ ); // Update list of users/groups - updateListOfUserGroups(view); + updateListOfUserGroups(); } } - private void updateListOfUserGroups(View view) { + private void updateListOfUserGroups() { // Update list of users/groups // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed UserListAdapter mUserGroupsAdapter = new UserListAdapter( @@ -132,18 +157,11 @@ private void updateListOfUserGroups(View view) { R.layout.share_user_item, mShares ); - // Show data - ListView usersList = view.findViewById(R.id.fdshareUsersList); - - // No data - TextView noList = view.findViewById(R.id.fdShareNoUsers); - if (mShares.size() > 0) { usersList.setVisibility(View.VISIBLE); usersList.setAdapter(mUserGroupsAdapter); noList.setVisibility(View.GONE); setListViewHeightBasedOnChildren(usersList); - } else { usersList.setVisibility(View.GONE); noList.setVisibility(View.VISIBLE); From c9328dcc256c3ec75a3a9e7bdd17d7962f505cbb Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 11 May 2018 23:01:22 +0200 Subject: [PATCH 04/75] add new lines --- drawable_resources/alert-octagon.svg | 2 +- src/main/res/drawable/ic_alert_octagon.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drawable_resources/alert-octagon.svg b/drawable_resources/alert-octagon.svg index 590dfad0802f..47476ee27919 100644 --- a/drawable_resources/alert-octagon.svg +++ b/drawable_resources/alert-octagon.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/main/res/drawable/ic_alert_octagon.xml b/src/main/res/drawable/ic_alert_octagon.xml index 4c26c245f993..8f739bacd2e3 100644 --- a/src/main/res/drawable/ic_alert_octagon.xml +++ b/src/main/res/drawable/ic_alert_octagon.xml @@ -20,4 +20,4 @@ android:viewportWidth="24" android:viewportHeight="24"> - \ No newline at end of file + From cd200b2e9f0c531f371573d17ee21f05b6626544 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 11 May 2018 23:29:00 +0200 Subject: [PATCH 05/75] empty menu implementation per sharee --- .../android/ui/adapter/UserListAdapter.java | 39 ++++++++++++++++ .../layout/file_details_share_user_item.xml | 12 +++++ .../res/menu/file_detail_sharing_menu.xml | 44 +++++++++++++++++++ src/main/res/values/strings.xml | 1 + 4 files changed, 96 insertions(+) create mode 100644 src/main/res/menu/file_detail_sharing_menu.xml diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 6d7d120fbede..a9ba45921f79 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -23,10 +23,13 @@ import android.content.Context; import android.support.annotation.NonNull; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; +import android.widget.PopupMenu; import android.widget.TextView; import com.owncloud.android.R; @@ -82,6 +85,7 @@ public long getItemId(int position) { OCShare share = mShares.get(position); TextView userName = view.findViewById(R.id.userOrGroupName); + final ImageView editShareButton = view.findViewById(R.id.editShareButton); ImageView icon = view.findViewById(R.id.userIcon); String name = share.getSharedWithDisplayName(); if (share.getShareType() == ShareType.GROUP) { @@ -107,7 +111,42 @@ public long getItemId(int position) { } userName.setText(name); + /// bind listener to edit privileges + editShareButton.setOnClickListener(v -> onOverflowIconClicked(v,mShares.get(position))); } return view; } + + private void onOverflowIconClicked(View view, OCShare share) { + PopupMenu popup = new PopupMenu(mContext, view); + popup.inflate(R.menu.file_detail_sharing_menu); + + prepareOptionsMenu(popup.getMenu(), share); + + popup.setOnMenuItemClickListener(this::optionsItemSelected); + popup.show(); + } + + private void prepareOptionsMenu(Menu menu, OCShare share) { + // TODO implement menu filtering based on OCShare type + } + + private boolean optionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_can_edit: { + // TODO implement de/-selecting can edit + return true; + } + case R.id.action_can_reshare: { + // TODO implement de/-selecting can share + return true; + } + case R.id.action_unshare: { + // TODO implement unshare + return true; + } + default: + return true; + } + } } \ No newline at end of file diff --git a/src/main/res/layout/file_details_share_user_item.xml b/src/main/res/layout/file_details_share_user_item.xml index 616c432dba60..e30ecd06bc6a 100644 --- a/src/main/res/layout/file_details_share_user_item.xml +++ b/src/main/res/layout/file_details_share_user_item.xml @@ -42,6 +42,7 @@ + + diff --git a/src/main/res/menu/file_detail_sharing_menu.xml b/src/main/res/menu/file_detail_sharing_menu.xml new file mode 100644 index 000000000000..d812daa5a3b2 --- /dev/null +++ b/src/main/res/menu/file_detail_sharing_menu.xml @@ -0,0 +1,44 @@ + + + + + + + + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 6e521dd4c5f6..7c04acebbc53 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -500,6 +500,7 @@ Share by link enabled Not shared by link Users and groups with access + Unshare can share can edit create From c86f0076018cf10d6f8f7ec88b58d292dbb35d1e Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 13 May 2018 13:20:49 +0200 Subject: [PATCH 06/75] add listener interface + organized imports --- .../android/ui/activity/ToolbarActivity.java | 1 - .../android/ui/adapter/UserListAdapter.java | 99 ++++++++++++++----- .../ui/fragment/FileDetailFragment.java | 2 - .../fragment/FileDetailSharingFragment.java | 29 +++++- 4 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 90eb9dbf4683..7c33f2fc3801 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -36,7 +36,6 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.utils.ThemeUtils; /** diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index a9ba45921f79..8ee709b92c54 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -20,6 +20,7 @@ package com.owncloud.android.ui.adapter; +import android.accounts.Account; import android.content.Context; import android.support.annotation.NonNull; import android.view.LayoutInflater; @@ -33,8 +34,11 @@ import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; +import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.ui.TextDrawable; import java.io.UnsupportedEncodingException; @@ -46,26 +50,36 @@ */ public class UserListAdapter extends ArrayAdapter { - private Context mContext; - private ArrayList mShares; - private float mAvatarRadiusDimension; - public UserListAdapter(Context context, int resource, ArrayList shares) { + private ShareeListAdapterListener listener; + private OCCapability capabilities; + private Context context; + private ArrayList shares; + private float avatarRadiusDimension; + + public UserListAdapter(Context context, int resource, ArrayList shares, + Account account, ShareeListAdapterListener listener) { super(context, resource); - mContext = context; - mShares = shares; + this.context = context; + this.shares = shares; + this.listener = listener; + + this.capabilities = new FileDataStorageManager( + account, + getContext().getContentResolver() + ).getCapability(account.name); - mAvatarRadiusDimension = context.getResources().getDimension(R.dimen.standard_padding); + avatarRadiusDimension = context.getResources().getDimension(R.dimen.standard_padding); } @Override public int getCount() { - return mShares.size(); + return shares.size(); } @Override public Object getItem(int position) { - return mShares.get(position); + return shares.get(position); } @Override @@ -74,15 +88,16 @@ public long getItemId(int position) { } @Override - public @NonNull View getView(final int position, View convertView, @NonNull ViewGroup parent) { + public @NonNull + View getView(final int position, View convertView, @NonNull ViewGroup parent) { View view = convertView; if (view == null) { - LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.file_details_share_user_item, parent, false); } - if (mShares != null && mShares.size() > position) { - OCShare share = mShares.get(position); + if (shares != null && shares.size() > position) { + OCShare share = shares.get(position); TextView userName = view.findViewById(R.id.userOrGroupName); final ImageView editShareButton = view.findViewById(R.id.editShareButton); @@ -91,20 +106,20 @@ public long getItemId(int position) { if (share.getShareType() == ShareType.GROUP) { name = getContext().getString(R.string.share_group_clarification, name); try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { icon.setImageResource(R.drawable.ic_group); } } else if (share.getShareType() == ShareType.EMAIL) { name = getContext().getString(R.string.share_email_clarification, name); try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { icon.setImageResource(R.drawable.ic_email); } } else { try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { icon.setImageResource(R.drawable.ic_user); } @@ -112,41 +127,75 @@ public long getItemId(int position) { userName.setText(name); /// bind listener to edit privileges - editShareButton.setOnClickListener(v -> onOverflowIconClicked(v,mShares.get(position))); + editShareButton.setOnClickListener(v -> onOverflowIconClicked(v, shares.get(position))); } return view; } private void onOverflowIconClicked(View view, OCShare share) { - PopupMenu popup = new PopupMenu(mContext, view); + PopupMenu popup = new PopupMenu(context, view); popup.inflate(R.menu.file_detail_sharing_menu); prepareOptionsMenu(popup.getMenu(), share); - popup.setOnMenuItemClickListener(this::optionsItemSelected); + popup.setOnMenuItemClickListener(item -> optionsItemSelected(item, share)); popup.show(); } private void prepareOptionsMenu(Menu menu, OCShare share) { - // TODO implement menu filtering based on OCShare type + refresMenuForShare(share, menu); } - private boolean optionsItemSelected(MenuItem item) { + private boolean optionsItemSelected(MenuItem item, OCShare share) { switch (item.getItemId()) { case R.id.action_can_edit: { - // TODO implement de/-selecting can edit + listener.toggleCanEdit(share); return true; } case R.id.action_can_reshare: { - // TODO implement de/-selecting can share + listener.toggleCanReshare(share); return true; } case R.id.action_unshare: { - // TODO implement unshare + listener.unshareWith(share); + shares.remove(share); + notifyDataSetChanged(); return true; } default: return true; } } -} \ No newline at end of file + + /** + * Updates the sharee's menu with the current permissions of the {@link OCShare} + * + * @param share the shared file + * @param menu the menu of the sharee/shared file + */ + private void refresMenuForShare(OCShare share, Menu menu) { + // TODO needs a complete rewrite + // since it seems permissions on server are now completely different to the client(s) + int sharePermissions = share.getPermissions(); + boolean isFederated = ShareType.FEDERATED.equals(share.getShareType()); + + MenuItem reshareItem = menu.findItem(R.id.action_can_reshare); + if (isFederated || + (capabilities != null && capabilities.getFilesSharingResharing().isFalse())) { + reshareItem.setVisible(false); + } + reshareItem.setChecked((sharePermissions & OCShare.SHARE_PERMISSION_FLAG) > 0); + + MenuItem editItem = menu.findItem(R.id.action_can_edit); + int anyUpdatePermission = OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | + OCShare.DELETE_PERMISSION_FLAG; + boolean canEdit = (sharePermissions & anyUpdatePermission) > 0; + editItem.setChecked(canEdit); + } + + public interface ShareeListAdapterListener { + void unshareWith(OCShare share); + void toggleCanEdit(OCShare share); + void toggleCanReshare(OCShare share); + } +} diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index cc46d50231c3..82f426b7c400 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -38,7 +38,6 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.TextView; @@ -67,7 +66,6 @@ import butterknife.BindView; import butterknife.ButterKnife; -import butterknife.Optional; import butterknife.Unbinder; /** diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 3cdfaa33594a..3b0d918ad00e 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -21,6 +21,7 @@ package com.owncloud.android.ui.fragment; import android.accounts.Account; +import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; @@ -46,7 +47,7 @@ import butterknife.ButterKnife; import butterknife.Unbinder; -public class FileDetailSharingFragment extends Fragment { +public class FileDetailSharingFragment extends Fragment implements UserListAdapter.ShareeListAdapterListener { private static final String ARG_FILE = "FILE"; private static final String ARG_ACCOUNT = "ACCOUNT"; @@ -113,6 +114,14 @@ public void onDestroy() { unbinder.unbind(); } + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (!(getActivity() instanceof FileActivity)) { + throw new IllegalArgumentException("Calling activity must be of type FileActivity"); + } + } + private void setupView() { shareTitle.setTextColor(ThemeUtils.primaryAccentColor(getContext())); shareWithUsersTitle.setTextColor(ThemeUtils.primaryAccentColor(getContext())); @@ -154,7 +163,7 @@ private void updateListOfUserGroups() { // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed UserListAdapter mUserGroupsAdapter = new UserListAdapter( getActivity().getApplicationContext(), - R.layout.share_user_item, mShares + R.layout.share_user_item, mShares, account, this ); if (mShares.size() > 0) { @@ -192,4 +201,20 @@ private static void setListViewHeightBasedOnChildren(ListView listView) { listView.setLayoutParams(params); listView.requestLayout(); } + + @Override + public void unshareWith(OCShare share) { + ((FileActivity)getActivity()).getFileOperationsHelper() + .unshareFileWithUserOrGroup(file, share.getShareType(), share.getShareWith()); + } + + @Override + public void toggleCanEdit(OCShare share) { + // TODO re-implement + } + + @Override + public void toggleCanReshare(OCShare share) { + // TODO re-implement + } } From 5200dd0881875b5b8a096d1087b91d7fa03e75a2 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 13 May 2018 13:39:59 +0200 Subject: [PATCH 07/75] WiP permission handling (Don't use for productive shares yet!) --- .../android/ui/adapter/UserListAdapter.java | 31 ++++++++++++++----- .../fragment/FileDetailSharingFragment.java | 28 ++++++++++++----- .../owncloud/android/utils/ThemeUtils.java | 1 - 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 8ee709b92c54..503c08137595 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -35,7 +35,6 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OCCapability; @@ -134,6 +133,7 @@ View getView(final int position, View convertView, @NonNull ViewGroup parent) { private void onOverflowIconClicked(View view, OCShare share) { PopupMenu popup = new PopupMenu(context, view); + // TODO add folder edit detail permissions popup.inflate(R.menu.file_detail_sharing_menu); prepareOptionsMenu(popup.getMenu(), share); @@ -149,13 +149,16 @@ private void prepareOptionsMenu(Menu menu, OCShare share) { private boolean optionsItemSelected(MenuItem item, OCShare share) { switch (item.getItemId()) { case R.id.action_can_edit: { - listener.toggleCanEdit(share); + // TODO calculate boolean flags + listener.updatePermissionsToShare(share, true, true, true, true, true); return true; } case R.id.action_can_reshare: { - listener.toggleCanReshare(share); + // TODO calculate boolean flags + listener.updatePermissionsToShare(share, true, true, true, true, true); return true; } + // TODO add folder edit detail permissions case R.id.action_unshare: { listener.unshareWith(share); shares.remove(share); @@ -174,8 +177,7 @@ private boolean optionsItemSelected(MenuItem item, OCShare share) { * @param menu the menu of the sharee/shared file */ private void refresMenuForShare(OCShare share, Menu menu) { - // TODO needs a complete rewrite - // since it seems permissions on server are now completely different to the client(s) + // TODO add folder edit detail permissions int sharePermissions = share.getPermissions(); boolean isFederated = ShareType.FEDERATED.equals(share.getShareType()); @@ -194,8 +196,23 @@ private void refresMenuForShare(OCShare share, Menu menu) { } public interface ShareeListAdapterListener { + /** + * unshare with given sharee. + * + * @param share the share + */ void unshareWith(OCShare share); - void toggleCanEdit(OCShare share); - void toggleCanReshare(OCShare share); + + /** + * Updates the permissions of the {@link OCShare}. + * + * @param share the share to be updated + * @param canReshare reshare permission + * @param canEdit edit permission + * @param canEditCreate create permission (folders only) + * @param canEditChange change permission (folders only) + * @param canEditDelete delete permission (folders only) + */ + void updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate, boolean canEditChange, boolean canEditDelete); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 3b0d918ad00e..c549d7d0360d 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -37,6 +37,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.UserListAdapter; import com.owncloud.android.utils.ThemeUtils; @@ -209,12 +210,25 @@ public void unshareWith(OCShare share) { } @Override - public void toggleCanEdit(OCShare share) { - // TODO re-implement - } - - @Override - public void toggleCanReshare(OCShare share) { - // TODO re-implement + public void updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, + boolean canEditCreate, boolean canEditChange, + boolean canEditDelete) { + SharePermissionsBuilder spb = new SharePermissionsBuilder(); + spb.setSharePermission(canReshare); + if (file.isFolder()) { + spb.setUpdatePermission(canEditChange) + .setCreatePermission(canEditCreate) + .setDeletePermission(canEditDelete); + } else { + spb.setUpdatePermission(canEdit); + } + int permissions = spb.build(); + + ((FileActivity) getActivity()).getFileOperationsHelper(). + setPermissionsToShare( + share, + permissions + ) + ; } } diff --git a/src/main/java/com/owncloud/android/utils/ThemeUtils.java b/src/main/java/com/owncloud/android/utils/ThemeUtils.java index 07822a4d1427..951244a9bb24 100644 --- a/src/main/java/com/owncloud/android/utils/ThemeUtils.java +++ b/src/main/java/com/owncloud/android/utils/ThemeUtils.java @@ -45,7 +45,6 @@ import android.support.v7.widget.SwitchCompat; import android.text.Html; import android.text.Spanned; -import android.view.Window; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.SeekBar; From 5054df0ba3d514a00122496b405d964879d968ab Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 13 May 2018 16:06:49 +0200 Subject: [PATCH 08/75] updating of permissions for file/folder (folder not reachable yet) --- .../android/ui/adapter/UserListAdapter.java | 155 ++++++++++++------ .../fragment/FileDetailSharingFragment.java | 6 +- .../res/menu/file_detail_sharing_menu.xml | 21 +++ src/main/res/values/strings.xml | 6 +- 4 files changed, 132 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 503c08137595..3bb77b4f867a 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -1,21 +1,21 @@ -/** - * ownCloud Android client application - * - * @author masensio - * Copyright (C) 2015 ownCloud Inc. +/* + * Nextcloud Android client application * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. + * @author Andy Scherzinger + * Copyright (C) 2018 Andy Scherzinger * - * 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 General Public License for more details. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . */ package com.owncloud.android.ui.adapter; @@ -34,10 +34,13 @@ import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OCCapability; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.TextDrawable; import java.io.UnsupportedEncodingException; @@ -45,23 +48,26 @@ import java.util.ArrayList; /** - * Adapter to show a user/group in Share With List in Details View + * Adapter to show a user/group/email/remote in Sharing list in file details view. */ public class UserListAdapter extends ArrayAdapter { - private ShareeListAdapterListener listener; private OCCapability capabilities; private Context context; private ArrayList shares; private float avatarRadiusDimension; + private Account account; + private OCFile file; public UserListAdapter(Context context, int resource, ArrayList shares, - Account account, ShareeListAdapterListener listener) { + Account account, OCFile file, ShareeListAdapterListener listener) { super(context, resource); this.context = context; this.shares = shares; this.listener = listener; + this.account = account; + this.file = file; this.capabilities = new FileDataStorageManager( account, @@ -133,32 +139,87 @@ View getView(final int position, View convertView, @NonNull ViewGroup parent) { private void onOverflowIconClicked(View view, OCShare share) { PopupMenu popup = new PopupMenu(context, view); - // TODO add folder edit detail permissions popup.inflate(R.menu.file_detail_sharing_menu); prepareOptionsMenu(popup.getMenu(), share); - popup.setOnMenuItemClickListener(item -> optionsItemSelected(item, share)); + popup.setOnMenuItemClickListener(item -> optionsItemSelected(popup.getMenu(), item, share)); popup.show(); } + /** + * Updates the sharee's menu with the current permissions of the {@link OCShare} + * + * @param menu the menu of the sharee/shared file + * @param share the shared file + */ private void prepareOptionsMenu(Menu menu, OCShare share) { - refresMenuForShare(share, menu); + int sharePermissions = share.getPermissions(); + boolean isFederated = ShareType.FEDERATED.equals(share.getShareType()); + + MenuItem reshareItem = menu.findItem(R.id.action_can_reshare); + if (isFederated || + (capabilities != null && capabilities.getFilesSharingResharing().isFalse())) { + reshareItem.setVisible(false); + } + reshareItem.setChecked((sharePermissions & OCShare.SHARE_PERMISSION_FLAG) > 0); + + MenuItem editItem = menu.findItem(R.id.action_can_edit); + int anyUpdatePermission = OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | + OCShare.DELETE_PERMISSION_FLAG; + boolean canEdit = (sharePermissions & anyUpdatePermission) > 0; + editItem.setChecked(canEdit); + + OwnCloudVersion serverVersion = AccountUtils.getServerVersion(account); + boolean isNotReshareableFederatedSupported = serverVersion.isNotReshareableFederatedSupported(); + boolean areEditOptionsAvailable = !isFederated || isNotReshareableFederatedSupported; + MenuItem editCreateItem = menu.findItem(R.id.action_can_edit_create); + MenuItem editChangeItem = menu.findItem(R.id.action_can_edit_change); + MenuItem editDeleteItem = menu.findItem(R.id.action_can_edit_delete); + if (file.isFolder() && areEditOptionsAvailable) { + /// TODO change areEditOptionsAvailable in order to delete !isFederated + editCreateItem.setChecked((sharePermissions & OCShare.CREATE_PERMISSION_FLAG) > 0); + editCreateItem.setVisible(canEdit); + + editChangeItem.setChecked((sharePermissions & OCShare.UPDATE_PERMISSION_FLAG) > 0); + editChangeItem.setVisible(canEdit); + + editDeleteItem.setChecked((sharePermissions & OCShare.DELETE_PERMISSION_FLAG) > 0); + editDeleteItem.setVisible(canEdit); + } else { + editCreateItem.setVisible(false); + editChangeItem.setVisible(false); + editDeleteItem.setVisible(false); + } } - private boolean optionsItemSelected(MenuItem item, OCShare share) { + private boolean optionsItemSelected(Menu menu, MenuItem item, OCShare share) { switch (item.getItemId()) { case R.id.action_can_edit: { - // TODO calculate boolean flags - listener.updatePermissionsToShare(share, true, true, true, true, true); + item.setChecked(!item.isChecked()); + if (file.isFolder() && !item.isChecked()) { + menu.findItem(R.id.action_can_edit_create).setChecked(false); + menu.findItem(R.id.action_can_edit_change).setChecked(false); + menu.findItem(R.id.action_can_edit_delete).setChecked(false); + } + share.setPermissions(updatePermissionsToShare(share, menu)); + return true; + } + case R.id.action_can_edit_create: + case R.id.action_can_edit_change: + case R.id.action_can_edit_delete: { + item.setChecked(!item.isChecked()); + if (item.isChecked() && !menu.findItem(R.id.action_can_edit).isChecked()) { + menu.findItem(R.id.action_can_edit).setChecked(true); + } + share.setPermissions(updatePermissionsToShare(share, menu)); return true; } case R.id.action_can_reshare: { - // TODO calculate boolean flags - listener.updatePermissionsToShare(share, true, true, true, true, true); + item.setChecked(!item.isChecked()); + share.setPermissions(updatePermissionsToShare(share, menu)); return true; } - // TODO add folder edit detail permissions case R.id.action_unshare: { listener.unshareWith(share); shares.remove(share); @@ -170,34 +231,20 @@ private boolean optionsItemSelected(MenuItem item, OCShare share) { } } - /** - * Updates the sharee's menu with the current permissions of the {@link OCShare} - * - * @param share the shared file - * @param menu the menu of the sharee/shared file - */ - private void refresMenuForShare(OCShare share, Menu menu) { - // TODO add folder edit detail permissions - int sharePermissions = share.getPermissions(); - boolean isFederated = ShareType.FEDERATED.equals(share.getShareType()); - - MenuItem reshareItem = menu.findItem(R.id.action_can_reshare); - if (isFederated || - (capabilities != null && capabilities.getFilesSharingResharing().isFalse())) { - reshareItem.setVisible(false); - } - reshareItem.setChecked((sharePermissions & OCShare.SHARE_PERMISSION_FLAG) > 0); - - MenuItem editItem = menu.findItem(R.id.action_can_edit); - int anyUpdatePermission = OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | - OCShare.DELETE_PERMISSION_FLAG; - boolean canEdit = (sharePermissions & anyUpdatePermission) > 0; - editItem.setChecked(canEdit); + private int updatePermissionsToShare(OCShare share, Menu menu) { + return listener.updatePermissionsToShare( + share, + menu.findItem(R.id.action_can_reshare).isChecked(), + menu.findItem(R.id.action_can_edit).isChecked(), + menu.findItem(R.id.action_can_edit_create).isChecked(), + menu.findItem(R.id.action_can_edit_change).isChecked(), + menu.findItem(R.id.action_can_edit_delete).isChecked() + ); } public interface ShareeListAdapterListener { /** - * unshare with given sharee. + * unshare with given sharee {@link OCShare}. * * @param share the share */ @@ -212,7 +259,13 @@ public interface ShareeListAdapterListener { * @param canEditCreate create permission (folders only) * @param canEditChange change permission (folders only) * @param canEditDelete delete permission (folders only) + * @return permissions value set */ - void updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate, boolean canEditChange, boolean canEditDelete); + int updatePermissionsToShare(OCShare share, + boolean canReshare, + boolean canEdit, + boolean canEditCreate, + boolean canEditChange, + boolean canEditDelete); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index c549d7d0360d..90cdcc6c59c2 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -164,7 +164,7 @@ private void updateListOfUserGroups() { // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed UserListAdapter mUserGroupsAdapter = new UserListAdapter( getActivity().getApplicationContext(), - R.layout.share_user_item, mShares, account, this + R.layout.share_user_item, mShares, account, file,this ); if (mShares.size() > 0) { @@ -210,7 +210,7 @@ public void unshareWith(OCShare share) { } @Override - public void updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, + public int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate, boolean canEditChange, boolean canEditDelete) { SharePermissionsBuilder spb = new SharePermissionsBuilder(); @@ -230,5 +230,7 @@ public void updatePermissionsToShare(OCShare share, boolean canReshare, boolean permissions ) ; + + return permissions; } } diff --git a/src/main/res/menu/file_detail_sharing_menu.xml b/src/main/res/menu/file_detail_sharing_menu.xml index d812daa5a3b2..647a8330f977 100644 --- a/src/main/res/menu/file_detail_sharing_menu.xml +++ b/src/main/res/menu/file_detail_sharing_menu.xml @@ -29,6 +29,27 @@ android:showAsAction="never" android:title="@string/share_privilege_can_edit" app:showAsAction="never" /> + + + Unshare can share can edit - create - change - delete + can create + can change + can delete Retry failed uploads Clear failed uploads From 72681e95b2b9ec0f08cb2925c201be0a39a77213 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 13 May 2018 16:39:58 +0200 Subject: [PATCH 09/75] add proper permission editing for folders --- src/main/java/com/owncloud/android/files/FileMenuFilter.java | 2 +- .../com/owncloud/android/ui/adapter/UserListAdapter.java | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 0ef99cada9ce..67e1dbb5be26 100644 --- a/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -260,7 +260,7 @@ private void filter(List toShow, List toHide, boolean inSingle } // SEE DETAILS - if (!isSingleFile()) { + if (!isSingleSelection()) { toHide.add(R.id.action_see_details); } else { toShow.add(R.id.action_see_details); diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 3bb77b4f867a..b815f3b49d1f 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -179,13 +179,8 @@ private void prepareOptionsMenu(Menu menu, OCShare share) { if (file.isFolder() && areEditOptionsAvailable) { /// TODO change areEditOptionsAvailable in order to delete !isFederated editCreateItem.setChecked((sharePermissions & OCShare.CREATE_PERMISSION_FLAG) > 0); - editCreateItem.setVisible(canEdit); - editChangeItem.setChecked((sharePermissions & OCShare.UPDATE_PERMISSION_FLAG) > 0); - editChangeItem.setVisible(canEdit); - editDeleteItem.setChecked((sharePermissions & OCShare.DELETE_PERMISSION_FLAG) > 0); - editDeleteItem.setVisible(canEdit); } else { editCreateItem.setVisible(false); editChangeItem.setVisible(false); From 2c30e3842f1d892148e7de9079d7f910cd2c6580 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 13 May 2018 22:39:10 +0200 Subject: [PATCH 10/75] Wip user/group search (not fully working yet) --- src/main/AndroidManifest.xml | 5 ++ .../UsersAndGroupsSearchProvider.java | 2 +- .../ui/activity/FileDisplayActivity.java | 65 +++++++++++++++++++ .../fragment/FileDetailSharingFragment.java | 39 +++++++++++ .../layout/file_details_sharing_fragment.xml | 7 ++ src/main/res/values/strings.xml | 2 +- 6 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 74bddddb6acc..f7007b20245a 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -88,6 +88,11 @@ + + + + diff --git a/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java b/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java index 802163068126..586a11e451a9 100644 --- a/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java +++ b/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java @@ -161,7 +161,7 @@ private Cursor searchForUsersOrGroups(Uri uri) { userQuery, REQUESTED_PAGE, RESULTS_PER_PAGE ); RemoteOperationResult result = searchRequest.execute(account, getContext()); - List names = new ArrayList(); + List names = new ArrayList<>(); if (result.isSuccess()) { for (Object o : result.getData()) { // Get JSonObjects from response diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index f532bf101045..f04ea242bd6d 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -26,6 +26,7 @@ import android.accounts.AuthenticatorException; import android.annotation.TargetApi; import android.app.Activity; +import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -38,6 +39,7 @@ import android.content.SyncRequest; import android.content.pm.PackageManager; import android.content.res.Resources.NotFoundException; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.IBinder; @@ -75,6 +77,8 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.media.MediaService; import com.owncloud.android.media.MediaServiceBinder; import com.owncloud.android.operations.CopyFileOperation; @@ -85,6 +89,7 @@ import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.UploadFileOperation; +import com.owncloud.android.providers.UsersAndGroupsSearchProvider; import com.owncloud.android.syncadapter.FileSyncAdapter; import com.owncloud.android.ui.dialog.SendShareDialog; import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; @@ -484,6 +489,66 @@ protected void onNewIntent(Intent intent) { if (intent.getAction() != null && intent.getAction().equalsIgnoreCase(ACTION_DETAILS)) { setIntent(intent); setFile(intent.getParcelableExtra(EXTRA_FILE)); + } else // Verify the action and get the query + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + String query = intent.getStringExtra(SearchManager.QUERY); + Log_OC.w(TAG, "Ignored Intent requesting to query for " + query); + + } else if (UsersAndGroupsSearchProvider.ACTION_SHARE_WITH.equals(intent.getAction())) { + Uri data = intent.getData(); + String dataString = intent.getDataString(); + String shareWith = dataString.substring(dataString.lastIndexOf('/') + 1); + + ArrayList shareeNames = new ArrayList<>(); + for (OCShare share : getStorageManager().getSharesWithForAFile(getFile().getRemotePath(), getAccount().name)) { + shareeNames.add(share.getShareWith()); + } + + if (!shareeNames.contains(shareWith)) { + + doShareWith( + shareWith, + data.getAuthority() + ); + } + + } else { + Log_OC.e(TAG, "Unexpected intent " + intent.toString()); + } + } + + private void doShareWith(String shareeName, String dataAuthority) { + + ShareType shareType = UsersAndGroupsSearchProvider.getShareType(dataAuthority); + + getFileOperationsHelper().shareFileWithSharee( + getFile(), + shareeName, + shareType, + getAppropiatePermissions(shareType) + ); + } + + private int getAppropiatePermissions(ShareType shareType) { + + // check if the Share is FEDERATED + boolean isFederated = ShareType.FEDERATED.equals(shareType); + + if (getFile().isSharedWithMe()) { + return OCShare.READ_PERMISSION_FLAG; // minimum permissions + + } else if (isFederated) { + if (com.owncloud.android.authentication.AccountUtils + .getServerVersion(getAccount()).isNotReshareableFederatedSupported()) { + return (getFile().isFolder() ? OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER_AFTER_OC9 : + OCShare.FEDERATED_PERMISSIONS_FOR_FILE_AFTER_OC9); + } else { + return (getFile().isFolder() ? OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER_UP_TO_OC9 : + OCShare.FEDERATED_PERMISSIONS_FOR_FILE_UP_TO_OC9); + } + } else { + return (getFile().isFolder() ? OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER : + OCShare.MAXIMUM_PERMISSIONS_FOR_FILE); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 90cdcc6c59c2..9509d4c60687 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -21,13 +21,17 @@ package com.owncloud.android.ui.fragment; import android.accounts.Account; +import android.app.SearchManager; import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; +import android.support.v7.widget.SearchView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; @@ -36,6 +40,7 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder; import com.owncloud.android.ui.activity.FileActivity; @@ -49,6 +54,7 @@ import butterknife.Unbinder; public class FileDetailSharingFragment extends Fragment implements UserListAdapter.ShareeListAdapterListener { + private static final String TAG = FileDetailSharingFragment.class.getSimpleName(); private static final String ARG_FILE = "FILE"; private static final String ARG_ACCOUNT = "ACCOUNT"; @@ -73,6 +79,9 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt @BindView(R.id.fdShareLinkIcon) ImageView linkIcon; + @BindView(R.id.searchView) + SearchView searchView; + @BindView(R.id.fdshareUsersList) ListView usersList; @@ -129,6 +138,36 @@ private void setupView() { setShareByLinkInfo(file.isSharedViaLink()); setShareWithUserInfo(); + setupSearchView(); + } + + private void setupSearchView() { + SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); + // assumes parent activity is the searchable activity + searchView.setSearchableInfo(searchManager + .getSearchableInfo(getActivity().getComponentName()) + ); + + // do not iconify the widget; expand it by default + searchView.setIconifiedByDefault(false); + + // avoid fullscreen with softkeyboard + searchView.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + Log_OC.v(TAG, "onQueryTextSubmit intercepted, query: " + query); + // return true to prevent the query is processed to be queried; + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + // leave it for the parent listener in the hierarchy / default behaviour + return false; + } + }); } /** diff --git a/src/main/res/layout/file_details_sharing_fragment.xml b/src/main/res/layout/file_details_sharing_fragment.xml index ace16c377294..e205cba3f026 100644 --- a/src/main/res/layout/file_details_sharing_fragment.xml +++ b/src/main/res/layout/file_details_sharing_fragment.xml @@ -81,6 +81,13 @@ android:text="@string/filedetails_share_users_with_access" android:textColor="@color/color_accent" /> + + Share with… Share with %1$s - Search + Name, federated cloud ID or email address… Search users and groups %1$s (group) From 27d04c8823c44784850c2a347e5ef029eb5b1c32 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 13 May 2018 22:48:28 +0200 Subject: [PATCH 11/75] organize imports --- .../owncloud/android/ui/fragment/FileDetailSharingFragment.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 9509d4c60687..1c488c5b9111 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -31,7 +31,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; From a2c0b180eca74fc85854576103fe0f220f422d9e Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 14 May 2018 11:18:27 +0200 Subject: [PATCH 12/75] lint: Inefficient layout weight --- src/main/res/layout/file_details_share_user_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/res/layout/file_details_share_user_item.xml b/src/main/res/layout/file_details_share_user_item.xml index e30ecd06bc6a..120b8d3769bb 100644 --- a/src/main/res/layout/file_details_share_user_item.xml +++ b/src/main/res/layout/file_details_share_user_item.xml @@ -40,7 +40,7 @@ android:contentDescription="@string/user_icon"/> Date: Mon, 14 May 2018 19:15:46 +0200 Subject: [PATCH 13/75] fix theming issue --- .../java/com/owncloud/android/ui/activity/ToolbarActivity.java | 2 +- .../com/owncloud/android/ui/fragment/FileDetailFragment.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 7c33f2fc3801..2a111c92d9af 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -87,7 +87,7 @@ protected void setupToolbar(boolean useBackgroundImage) { } } - protected void setupToolbar() { + public void setupToolbar() { setupToolbar(false); } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 82f426b7c400..c395f20d408e 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -346,7 +346,7 @@ public void onStop() { if(activity != null) { activity.setPreviewImageVisibility(View.GONE); activity.setProgressBarVisibility(View.VISIBLE); - ThemeUtils.setStatusBarColor(activity, ThemeUtils.primaryColor(getContext())); + activity.setupToolbar(); } super.onStop(); From 5589b8c0e9eba1b8436424c6e9bc9e159ed38e93 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Mon, 14 May 2018 17:29:08 +0200 Subject: [PATCH 14/75] add avatar icons on sharee list Signed-off-by: tobiasKaminsky --- .../datamodel/ThumbnailsCacheManager.java | 62 +++++++------ .../com/owncloud/android/ui/TextDrawable.java | 20 ++++- .../android/ui/activity/DrawerActivity.java | 12 +-- .../android/ui/activity/UserInfoActivity.java | 2 +- .../ui/adapter/AccountListAdapter.java | 2 +- .../android/ui/adapter/UserListAdapter.java | 36 +++++--- .../owncloud/android/utils/BitmapUtils.java | 10 +-- .../owncloud/android/utils/DisplayUtils.java | 87 ++++++++++++------- 8 files changed, 142 insertions(+), 89 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index fc6005590ee7..3291a4ce12ca 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -22,7 +22,6 @@ package com.owncloud.android.datamodel; import android.accounts.Account; -import android.accounts.AccountManager; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; @@ -773,12 +772,14 @@ public static class AvatarGenerationTask extends AsyncTask(avatarGenerationListener); mCallContext = callContext; if (storageManager == null) { @@ -787,6 +788,9 @@ public AvatarGenerationTask(AvatarGenerationListener avatarGenerationListener, O mAccount = account; mResources = resources; mAvatarRadius = avatarRadius; + mUserId = userId; + mServerName = serverName; + mContext = context; } @SuppressFBWarnings("Dm") @@ -796,20 +800,17 @@ protected Drawable doInBackground(String... params) { try { if (mAccount != null) { - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, - MainApp.getAppContext()); - mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); + mClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, mContext); } - mUsername = params[0]; thumbnail = doAvatarInBackground(); } catch(OutOfMemoryError oome) { Log_OC.e(TAG, "Out of memory"); } catch(Throwable t){ // the app should never break due to a problem with avatars - Log_OC.e(TAG, "Generation of avatar for " + mUsername + " failed", t); + Log_OC.e(TAG, "Generation of avatar for " + mUserId + " failed", t); } return thumbnail; @@ -820,7 +821,7 @@ protected void onPostExecute(Drawable drawable) { AvatarGenerationListener listener = mAvatarGenerationListener.get(); AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(mCallContext); - if (this == avatarWorkerTask && listener.shouldCallGeneratedCallback(mUsername, mCallContext)) { + if (this == avatarWorkerTask && listener.shouldCallGeneratedCallback(mUserId, mCallContext)) { listener.avatarGenerated(drawable, mCallContext); } } @@ -830,7 +831,7 @@ protected void onPostExecute(Drawable drawable) { * Converts size of file icon from dp to pixel * @return int */ - private int getAvatarDimension(){ + private int getAvatarDimension() { // Converts dp to pixel Resources r = MainApp.getAppContext().getResources(); return Math.round(r.getDimension(R.dimen.file_avatar_size)); @@ -839,13 +840,14 @@ private int getAvatarDimension(){ private @Nullable Drawable doAvatarInBackground() { Bitmap avatar = null; - String username = mUsername; - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( - MainApp.getAppContext().getContentResolver()); + String accountName = mUserId + "@" + mServerName; + + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(mContext.getContentResolver()); + + String eTag = arbitraryDataProvider.getValue(accountName, ThumbnailsCacheManager.AVATAR); + String avatarKey = "a_" + mUserId + "_" + mServerName + "_" + eTag; - String eTag = arbitraryDataProvider.getValue(mAccount, AVATAR); - final String imageKey = "a_" + username + "_" + eTag; int px = getAvatarDimension(); // Download avatar from server @@ -854,18 +856,13 @@ Drawable doAvatarInBackground() { if (serverOCVersion.supportsRemoteThumbnails()) { GetMethod get = null; try { - String userId = AccountManager.get(MainApp.getAppContext()).getUserData(mAccount, - com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); - - if (TextUtils.isEmpty(userId)) { - userId = AccountUtils.getAccountUsername(username); - } - - String uri = mClient.getBaseUri() + "/index.php/avatar/" + Uri.encode(userId) + "/" + px; + String uri = mClient.getBaseUri() + "/index.php/avatar/" + Uri.encode(mUserId) + "/" + px; Log_OC.d("Avatar", "URI: " + uri); get = new GetMethod(uri); - if (!eTag.isEmpty()) { + // only use eTag if available and corresponding avatar is still there + // (might be deleted from cache) + if (!eTag.isEmpty() && getBitmapFromDiskCache(avatarKey) != null) { get.setRequestHeader("If-None-Match", eTag); } @@ -877,18 +874,19 @@ Drawable doAvatarInBackground() { // new avatar InputStream inputStream = get.getResponseBodyAsStream(); + String newETag = null; if (get.getResponseHeader(ETAG) != null) { - eTag = get.getResponseHeader(ETAG).getValue().replace("\"", ""); - arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, AVATAR, eTag); + newETag = get.getResponseHeader(ETAG).getValue().replace("\"", ""); + arbitraryDataProvider.storeOrUpdateKeyValue(accountName, AVATAR, newETag); } Bitmap bitmap = BitmapFactory.decodeStream(inputStream); avatar = ThumbnailUtils.extractThumbnail(bitmap, px, px); // Add avatar to cache - if (avatar != null) { + if (avatar != null && !TextUtils.isEmpty(newETag)) { avatar = handlePNG(avatar, px, px); - String newImageKey = "a_" + username + "_" + eTag; + String newImageKey = "a_" + mUserId + "_" + mServerName + "_" + newETag; addBitmapToCache(newImageKey, avatar); } else { return TextDrawable.createAvatar(mAccount.name, mAvatarRadius); @@ -897,7 +895,7 @@ Drawable doAvatarInBackground() { case HttpStatus.SC_NOT_MODIFIED: // old avatar - avatar = getBitmapFromDiskCache(imageKey); + avatar = getBitmapFromDiskCache(avatarKey); mClient.exhaustResponse(get.getResponseBodyAsStream()); break; @@ -965,7 +963,7 @@ public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); if (avatarWorkerTask != null) { - final Object usernameData = avatarWorkerTask.mUsername; + final Object usernameData = avatarWorkerTask.mUserId; // If usernameData is not yet set or it differs from the new data if (usernameData == null || !usernameData.equals(file)) { // Cancel previous task @@ -984,7 +982,7 @@ public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); if (avatarWorkerTask != null) { - final Object usernameData = avatarWorkerTask.mUsername; + final Object usernameData = avatarWorkerTask.mUserId; // If usernameData is not yet set or it differs from the new data if (usernameData == null || !usernameData.equals(file)) { // Cancel previous task diff --git a/src/main/java/com/owncloud/android/ui/TextDrawable.java b/src/main/java/com/owncloud/android/ui/TextDrawable.java index 5e6fb6e49a1b..9996e5317046 100644 --- a/src/main/java/com/owncloud/android/ui/TextDrawable.java +++ b/src/main/java/com/owncloud/android/ui/TextDrawable.java @@ -103,6 +103,23 @@ public static TextDrawable createAvatar(String accountName, float radiusInDp) th return createNamedAvatar(username, radiusInDp); } + /** + * creates an avatar in form of a TextDrawable with the first letter of the account name in a circle with the + * given radius. + * + * @param userId userId to use + * @param radiusInDp the circle's radius + * @return the avatar as a TextDrawable + * @throws UnsupportedEncodingException if the charset is not supported when calculating the color values + * @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values + */ + @NonNull + @NextcloudServer(max = 12) + public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) throws + UnsupportedEncodingException, NoSuchAlgorithmException { + return createNamedAvatar(userId, radiusInDp); + } + /** * creates an avatar in form of a TextDrawable with the first letter of a name in a circle with the * given radius. @@ -114,8 +131,7 @@ public static TextDrawable createAvatar(String accountName, float radiusInDp) th * @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values */ @NonNull - public static TextDrawable createNamedAvatar(String name, float radiusInDp) throws - UnsupportedEncodingException, NoSuchAlgorithmException { + public static TextDrawable createNamedAvatar(String name, float radiusInDp) { int[] hsl = BitmapUtils.calculateHSL(name); int[] rgb = BitmapUtils.HSLtoRGB(hsl[0], hsl[1], hsl[2], 1); diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index a99959452206..f13f509628de 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -648,8 +648,8 @@ public void updateAccountList() { View accountEndView = findNavigationViewChildById(R.id.drawer_account_end); accountEndView.setTag(mAvatars[1].name); - DisplayUtils.setAvatar(mAvatars[1], this, - mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(), accountEndView); + DisplayUtils.setAvatar(mAvatars[1], this, mOtherAccountAvatarRadiusDimension, getResources(), + getStorageManager(), accountEndView, this); mAccountEndAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountEndAccountAvatar.setVisibility(View.GONE); @@ -660,8 +660,8 @@ public void updateAccountList() { View accountMiddleView = findNavigationViewChildById(R.id.drawer_account_middle); accountMiddleView.setTag(mAvatars[2].name); - DisplayUtils.setAvatar(mAvatars[2], this, - mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(), accountMiddleView); + DisplayUtils.setAvatar(mAvatars[2], this, mOtherAccountAvatarRadiusDimension, getResources(), + getStorageManager(), accountMiddleView, this); mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountMiddleAccountAvatar.setVisibility(View.GONE); @@ -695,7 +695,7 @@ private void repopulateAccountList(ArrayList accounts) { account.name) .setIcon(TextDrawable.createAvatar(account.name, mMenuAccountAvatarRadiusDimension)); DisplayUtils.setAvatar(account, this, mMenuAccountAvatarRadiusDimension, getResources(), - getStorageManager(), accountMenuItem); + getStorageManager(), accountMenuItem, this); } } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e); @@ -761,7 +761,7 @@ protected void setAccountInDrawer(Account account) { currentAccountView.setTag(account.name); DisplayUtils.setAvatar(account, this, mCurrentAccountAvatarRadiusDimension, getResources(), - getStorageManager(), currentAccountView); + getStorageManager(), currentAccountView, this); // check and show quota info if available getAndDisplayUserQuota(); diff --git a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index d2edb0795a1f..f30172c0d4dd 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -263,7 +263,7 @@ private void populateUserInfoUi(UserInfo userInfo) { userName.setText(account.name); avatar.setTag(account.name); DisplayUtils.setAvatar(account, UserInfoActivity.this, mCurrentAccountAvatarRadiusDimension, getResources(), - getStorageManager(), avatar); + getStorageManager(), avatar, this); int tint = ThemeUtils.primaryColor(account, this); diff --git a/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java index 935faf4cc261..ec353d57aadf 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -153,7 +153,7 @@ private void setAvatar(AccountViewHolderItem viewHolder, Account account) { View viewItem = viewHolder.imageViewItem; viewItem.setTag(account.name); DisplayUtils.setAvatar(account, this, mAccountAvatarRadiusDimension, mContext.getResources(), - mContext.getStorageManager(), viewItem); + mContext.getStorageManager(), viewItem, mContext); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account list item.", e); // use user icon as a fallback diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index b815f3b49d1f..812b22057335 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -22,6 +22,7 @@ import android.accounts.Account; import android.content.Context; +import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.view.LayoutInflater; import android.view.Menu; @@ -42,6 +43,7 @@ import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.TextDrawable; +import com.owncloud.android.utils.DisplayUtils; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; @@ -50,7 +52,7 @@ /** * Adapter to show a user/group/email/remote in Sharing list in file details view. */ -public class UserListAdapter extends ArrayAdapter { +public class UserListAdapter extends ArrayAdapter implements DisplayUtils.AvatarGenerationListener { private ShareeListAdapterListener listener; private OCCapability capabilities; @@ -59,6 +61,7 @@ public class UserListAdapter extends ArrayAdapter { private float avatarRadiusDimension; private Account account; private OCFile file; + private FileDataStorageManager storageManager; public UserListAdapter(Context context, int resource, ArrayList shares, Account account, OCFile file, ShareeListAdapterListener listener) { @@ -69,10 +72,8 @@ public UserListAdapter(Context context, int resource, ArrayList shares, this.account = account; this.file = file; - this.capabilities = new FileDataStorageManager( - account, - getContext().getContentResolver() - ).getCapability(account.name); + storageManager = new FileDataStorageManager(account, getContext().getContentResolver()); + capabilities = storageManager.getCapability(account.name); avatarRadiusDimension = context.getResources().getDimension(R.dimen.standard_padding); } @@ -123,11 +124,9 @@ View getView(final int position, View convertView, @NonNull ViewGroup parent) { icon.setImageResource(R.drawable.ic_email); } } else { - try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_user); - } + icon.setTag(share.getShareWith()); + DisplayUtils.setAvatar(account, share.getShareWith(), this, avatarRadiusDimension, + context.getResources(), storageManager, icon, context); } userName.setText(name); @@ -237,6 +236,23 @@ private int updatePermissionsToShare(OCShare share, Menu menu) { ); } + @Override + public void avatarGenerated(Drawable avatarDrawable, Object callContext) { + if (callContext instanceof ImageView) { + ImageView iv = (ImageView) callContext; + iv.setImageDrawable(avatarDrawable); + } + } + + @Override + public boolean shouldCallGeneratedCallback(String tag, Object callContext) { + if (callContext instanceof ImageView) { + ImageView iv = (ImageView) callContext; + return String.valueOf(iv.getTag()).equals(tag); + } + return false; + } + public interface ShareeListAdapterListener { /** * unshare with given sharee {@link OCShare}. diff --git a/src/main/java/com/owncloud/android/utils/BitmapUtils.java b/src/main/java/com/owncloud/android/utils/BitmapUtils.java index 982b4034c42a..733a20ea4f87 100644 --- a/src/main/java/com/owncloud/android/utils/BitmapUtils.java +++ b/src/main/java/com/owncloud/android/utils/BitmapUtils.java @@ -34,9 +34,7 @@ import org.apache.commons.codec.binary.Hex; -import java.io.UnsupportedEncodingException; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Locale; /** @@ -269,10 +267,8 @@ private static float HueToRGB(float p, float q, float h) { * * @param name The name * @return corresponding RGB color - * @throws UnsupportedEncodingException if the charset is not supported - * @throws NoSuchAlgorithmException if the specified algorithm is not available */ - public static int[] calculateHSL(String name) throws UnsupportedEncodingException, NoSuchAlgorithmException { + public static int[] calculateHSL(String name) { // using adapted algorithm from https://github.com/nextcloud/server/blob/master/core/js/placeholder.js#L126 String[] result = new String[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}; @@ -375,6 +371,10 @@ public static String md5(String string) { * @return the circular bitmap */ public static RoundedBitmapDrawable bitmapToCircularBitmapDrawable(Resources resources, Bitmap bitmap) { + if (bitmap == null) { + return null; + } + RoundedBitmapDrawable roundedBitmap = RoundedBitmapDrawableFactory.create(resources, bitmap); roundedBitmap.setCircular(true); return roundedBitmap; diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index d821573bd092..72e2c858b1f8 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -25,6 +25,7 @@ package com.owncloud.android.utils; import android.accounts.Account; +import android.accounts.AccountManager; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; @@ -433,50 +434,72 @@ public interface AvatarGenerationListener { } /** - * fetches and sets the avatar of the current account in the drawer in case the drawer is available. + * fetches and sets the avatar of the given account in the passed callContext * - * @param account the account to be set in the drawer + * @param account the account to be used to connect to server * @param avatarRadius the avatar radius * @param resources reference for density information * @param storageManager reference for caching purposes + * @param callContext which context is called to set the generated avatar */ - public static void setAvatar(Account account, AvatarGenerationListener listener, float avatarRadius, - Resources resources, FileDataStorageManager storageManager, Object callContext) { - if (account != null) { - if (callContext instanceof View) { - ((View) callContext).setContentDescription(account.name); - } + public static void setAvatar(@NonNull Account account, AvatarGenerationListener listener, + float avatarRadius, Resources resources, FileDataStorageManager storageManager, + Object callContext, Context context) { + + AccountManager accountManager = AccountManager.get(context); + String userId = accountManager.getUserData(account, + com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); + + setAvatar(account, userId, listener, avatarRadius, resources, storageManager, callContext, context); + } + + /** + * fetches and sets the avatar of the given account in the passed callContext + * + * @param account the account to be used to connect to server + * @param userId the userId which avatar should be set + * @param avatarRadius the avatar radius + * @param resources reference for density information + * @param storageManager reference for caching purposes + * @param callContext which context is called to set the generated avatar + */ + public static void setAvatar(@NonNull Account account, @NonNull String userId, AvatarGenerationListener listener, + float avatarRadius, Resources resources, FileDataStorageManager storageManager, + Object callContext, Context context) { + if (callContext instanceof View) { + ((View) callContext).setContentDescription(userId); + } - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( - MainApp.getAppContext().getContentResolver()); + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); - String eTag = arbitraryDataProvider.getValue(account, ThumbnailsCacheManager.AVATAR); + String serverName = account.name.substring(account.name.lastIndexOf('@') + 1, account.name.length()); + String eTag = arbitraryDataProvider.getValue(userId + "@" + serverName, ThumbnailsCacheManager.AVATAR); + String avatarKey = "a_" + userId + "_" + serverName + "_" + eTag; - // first show old one - Drawable avatar = BitmapUtils.bitmapToCircularBitmapDrawable(resources, - ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name + "_" + eTag)); + // first show old one + Drawable avatar = BitmapUtils.bitmapToCircularBitmapDrawable(resources, + ThumbnailsCacheManager.getBitmapFromDiskCache(avatarKey)); - // if no one exists, show colored icon with initial char - if (avatar == null) { - try { - avatar = TextDrawable.createAvatar(account.name, avatarRadius); - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - avatar = resources.getDrawable(R.drawable.account_circle_white); - } + // if no one exists, show colored icon with initial char + if (avatar == null) { + try { + avatar = TextDrawable.createAvatarByUserId(userId, avatarRadius); + } catch (Exception e) { + Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); + avatar = resources.getDrawable(R.drawable.account_circle_white); } + } - // check for new avatar, eTag is compared, so only new one is downloaded - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, callContext)) { - final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, storageManager, - account, resources, avatarRadius); + // check for new avatar, eTag is compared, so only new one is downloaded + if (ThumbnailsCacheManager.cancelPotentialAvatarWork(userId, callContext)) { + final ThumbnailsCacheManager.AvatarGenerationTask task = + new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, storageManager, + account, resources, avatarRadius, userId, serverName, context); - final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, avatar, task); - listener.avatarGenerated(asyncDrawable, callContext); - task.execute(account.name); - } + final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, avatar, task); + listener.avatarGenerated(asyncDrawable, callContext); + task.execute(userId); } } From 90529c5841d2f6e081f052d21bdafe940c6d09b7 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 15 May 2018 10:13:08 +0200 Subject: [PATCH 15/75] fix not needed try/catch blocks Signed-off-by: tobiasKaminsky --- .../com/owncloud/android/ui/TextDrawable.java | 10 +++------- .../ui/adapter/ShareUserListAdapter.java | 20 +++---------------- .../android/ui/adapter/UserListAdapter.java | 14 ++----------- 3 files changed, 8 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/TextDrawable.java b/src/main/java/com/owncloud/android/ui/TextDrawable.java index 9996e5317046..b488783ec198 100644 --- a/src/main/java/com/owncloud/android/ui/TextDrawable.java +++ b/src/main/java/com/owncloud/android/ui/TextDrawable.java @@ -97,8 +97,7 @@ public TextDrawable(String text, int r, int g, int b, float radius) { */ @NonNull @NextcloudServer(max = 12) - public static TextDrawable createAvatar(String accountName, float radiusInDp) throws - UnsupportedEncodingException, NoSuchAlgorithmException { + public static TextDrawable createAvatar(String accountName, float radiusInDp) { String username = AccountUtils.getAccountUsername(accountName); return createNamedAvatar(username, radiusInDp); } @@ -115,8 +114,7 @@ public static TextDrawable createAvatar(String accountName, float radiusInDp) th */ @NonNull @NextcloudServer(max = 12) - public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) throws - UnsupportedEncodingException, NoSuchAlgorithmException { + public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) { return createNamedAvatar(userId, radiusInDp); } @@ -127,8 +125,6 @@ public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) * @param name the name * @param radiusInDp the circle's radius * @return the avatar as a TextDrawable - * @throws UnsupportedEncodingException if the charset is not supported when calculating the color values - * @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values */ @NonNull public static TextDrawable createNamedAvatar(String name, float radiusInDp) { @@ -146,7 +142,7 @@ public static TextDrawable createNamedAvatar(String name, float radiusInDp) { * @param canvas The canvas to draw into */ @Override - public void draw(Canvas canvas) { + public void draw(@NonNull Canvas canvas) { canvas.drawCircle(mRadius, mRadius, mRadius, mBackground); canvas.drawText(mText, mRadius, mRadius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2), mTextPaint); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java index af726e9b4108..bb6111fc01d2 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java @@ -34,8 +34,6 @@ import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.ui.TextDrawable; -import java.io.UnsupportedEncodingException; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** @@ -93,24 +91,12 @@ public View getView(final int position, View convertView, @NonNull ViewGroup par String name = share.getSharedWithDisplayName(); if (share.getShareType() == ShareType.GROUP) { name = getContext().getString(R.string.share_group_clarification, name); - try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_group); - } + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); } else if (share.getShareType() == ShareType.EMAIL) { name = getContext().getString(R.string.share_email_clarification, name); - try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_email); - } + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); } else { - try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_user); - } + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); } userName.setText(name); diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 812b22057335..c2bd3aa79ff1 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -45,8 +45,6 @@ import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.utils.DisplayUtils; -import java.io.UnsupportedEncodingException; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** @@ -111,18 +109,10 @@ View getView(final int position, View convertView, @NonNull ViewGroup parent) { String name = share.getSharedWithDisplayName(); if (share.getShareType() == ShareType.GROUP) { name = getContext().getString(R.string.share_group_clarification, name); - try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_group); - } + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); } else if (share.getShareType() == ShareType.EMAIL) { name = getContext().getString(R.string.share_email_clarification, name); - try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_email); - } + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); } else { icon.setTag(share.getShareWith()); DisplayUtils.setAvatar(account, share.getShareWith(), this, avatarRadiusDimension, From 11edb451b0fca962f341dbbefb55a234d982e793 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 15 May 2018 12:03:35 +0200 Subject: [PATCH 16/75] use own, distinct menu definition, fix strings, use relative date --- .../ui/fragment/FileDetailFragment.java | 84 +++------------- .../layout/file_details_sharing_fragment.xml | 2 +- .../res/menu/file_details_actions_menu.xml | 99 +++++++++++++++++++ src/main/res/values/strings.xml | 2 + 4 files changed, 115 insertions(+), 72 deletions(-) create mode 100644 src/main/res/menu/file_details_actions_menu.xml diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index c395f20d408e..3a5b1362bba8 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -238,57 +238,10 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat emptyProgressBar.setVisibility(View.GONE); } } - // TODO use whenever we switch to use glide for preview images - /* - private void setHeaderImage() { - if (mContainerActivity.getStorageManager().getCapability(account.name) - .getServerBackground() != null && previewImage != null) { - - String background = mContainerActivity.getStorageManager().getCapability(account.name).getServerBackground(); - - int primaryColor = ThemeUtils.primaryColor(account, getContext()); - - if (URLUtil.isValidUrl(background)) { - // background image - SimpleTarget target = new SimpleTarget() { - @Override - public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) { - Drawable[] drawables = {new ColorDrawable(primaryColor), resource}; - LayerDrawable layerDrawable = new LayerDrawable(drawables); - previewImage.setImageDrawable(layerDrawable); - previewImage.setVisibility(View.VISIBLE); - ((ToolbarActivity) getActivity()).getSupportActionBar().setTitle(null); - ((ToolbarActivity) getActivity()).getSupportActionBar().setBackgroundDrawable(null); - toolbarProgressBar.setVisibility(View.GONE); - previewLoaded = true; - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - previewImage.setVisibility(View.GONE); - toolbarProgressBar.setVisibility(View.VISIBLE); - } - }; - - Glide.with(this) - .load(background) - .centerCrop() - .placeholder(R.drawable.background) - .error(R.drawable.background) - .crossFade() - .into(target); - } else { - // hide image - previewImage.setVisibility(View.GONE); - toolbarProgressBar.setVisibility(View.VISIBLE); - } - } - } - */ public void onOverflowIconClicked(View view) { PopupMenu popup = new PopupMenu(getActivity(), view); - popup.inflate(R.menu.file_actions_menu); + popup.inflate(R.menu.file_details_actions_menu); prepareOptionsMenu(popup.getMenu()); popup.setOnMenuItemClickListener(this::optionsItemSelected); @@ -392,41 +345,22 @@ private void prepareOptionsMenu(Menu menu) { mf.filter(menu, true); } - // restriction for this fragment - FileMenuFilter.hideMenuItems( - menu.findItem(R.id.action_see_details), - menu.findItem(R.id.action_select_all), - menu.findItem(R.id.action_move), - menu.findItem(R.id.action_copy), - menu.findItem(R.id.action_favorite), - menu.findItem(R.id.action_unset_favorite), - menu.findItem(R.id.action_search) - ); - // dual pane restrictions if (!getResources().getBoolean(R.bool.large_land_layout)){ FileMenuFilter.hideMenuItems( - menu.findItem(R.id.action_switch_view), - menu.findItem(R.id.action_sync_account), - menu.findItem(R.id.action_sort) + menu.findItem(R.id.action_sync_account) ); } - - // share restrictions - if (getFile().isSharedWithMe() && !getFile().canReshare()) { - FileMenuFilter.hideMenuItems(menu.findItem(R.id.action_send_share_file)); - } } public boolean optionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.action_send_share_file: { + case R.id.action_send_file: { if(getFile().isSharedWithMe() && !getFile().canReshare()){ Snackbar.make(getView(), R.string.resharing_is_not_allowed, Snackbar.LENGTH_LONG - ) - .show(); + ).show(); } else { mContainerActivity.getFileOperationsHelper().sendShareFile(getFile()); } @@ -463,6 +397,14 @@ public boolean optionsItemSelected(MenuItem item) { mContainerActivity.getFileOperationsHelper().toggleOfflineFile(getFile(), false); return true; } + case R.id.action_encrypted: { + // TODO implement or remove + return true; + } + case R.id.action_unset_encrypted: { + // TODO implement or remove + return true; + } default: return super.onOptionsItemSelected(item); } @@ -544,7 +486,7 @@ public void updateFileDetails(boolean transferring, boolean refresh) { fileName.setVisibility(View.GONE); } fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength())); - fileModifiedTimestamp.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())); + fileModifiedTimestamp.setText(DisplayUtils.getRelativeTimestamp(getContext(), file.getModificationTimestamp())); setFilePreview(file); setFavoriteIconStatus(file.getIsFavorite()); diff --git a/src/main/res/layout/file_details_sharing_fragment.xml b/src/main/res/layout/file_details_sharing_fragment.xml index e205cba3f026..db6e66ae40e8 100644 --- a/src/main/res/layout/file_details_sharing_fragment.xml +++ b/src/main/res/layout/file_details_sharing_fragment.xml @@ -38,7 +38,7 @@ android:layout_marginBottom="@dimen/standard_half_margin" android:paddingLeft="@dimen/standard_padding" android:paddingRight="@dimen/standard_padding" - android:text="@string/action_send_share" + android:text="@string/common_share" android:textColor="@color/color_accent" /> + + + + + + + + + + + + + + + + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index e3a5ee0ff91f..ccf60123dac8 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -132,6 +132,8 @@ Unknown error Pending Delete + Share + Send About Remove account Remove account %s and delete all local files?\n\nDeletion cannot be undone. From 4a22b932b1a45ddf822f9ccadaad843908bf4e74 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 15 May 2018 12:27:03 +0200 Subject: [PATCH 17/75] fix send menu action + send/share dialog extension --- .../android/ui/dialog/SendShareDialog.java | 15 ++++++++++++-- .../ui/fragment/FileDetailFragment.java | 18 ++++++++--------- .../ui/helpers/FileOperationsHelper.java | 8 ++++++-- .../res/menu/file_details_actions_menu.xml | 20 +++++++++---------- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java index 1ab9a4b1ad50..65ecc1882d04 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java @@ -16,6 +16,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import com.owncloud.android.R; @@ -53,20 +54,23 @@ public class SendShareDialog extends BottomSheetDialogFragment { private static final String KEY_OCFILE = "KEY_OCFILE"; + private static final String KEY_HIDE_NCSHARING_OPTIONS = "KEY_HIDE_NCSHARING_OPTIONS"; private static final String TAG = SendShareDialog.class.getSimpleName(); public static final String PACKAGE_NAME = "PACKAGE_NAME"; public static final String ACTIVITY_NAME = "ACTIVITY_NAME"; private View view; private OCFile file; + private boolean hideNcSharingOptions; private FileOperationsHelper fileOperationsHelper; - public static SendShareDialog newInstance(OCFile file) { + public static SendShareDialog newInstance(OCFile file, boolean hideNcSharingOptions) { SendShareDialog dialogFragment = new SendShareDialog(); Bundle args = new Bundle(); args.putParcelable(KEY_OCFILE, file); + args.putBoolean(KEY_HIDE_NCSHARING_OPTIONS, hideNcSharingOptions); dialogFragment.setArguments(args); return dialogFragment; @@ -81,6 +85,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { view = null; file = getArguments().getParcelable(KEY_OCFILE); + hideNcSharingOptions = getArguments().getBoolean(KEY_HIDE_NCSHARING_OPTIONS, false); } @Nullable @@ -89,6 +94,9 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, view = inflater.inflate(R.layout.send_share_fragment, container, false); + LinearLayout sendShareButtons = view.findViewById(R.id.send_share_buttons); + View divider = view.findViewById(R.id.divider); + // Share with people TextView sharePeopleText = view.findViewById(R.id.share_people_button); sharePeopleText.setOnClickListener(v -> shareFile(file)); @@ -105,7 +113,10 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, themeShareButtonImage(shareLinkImageView); shareLinkImageView.setOnClickListener(v -> shareFile(file)); - if (file.isSharedWithMe() && !file.canReshare()) { + if (hideNcSharingOptions) { + sendShareButtons.setVisibility(View.GONE); + divider.setVisibility(View.GONE); + } else if (file.isSharedWithMe() && !file.canReshare()) { showResharingNotAllowedSnackbar(); if (file.isFolder()) { diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 3a5b1362bba8..f0bdc65a68c8 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -27,7 +27,6 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; @@ -345,6 +344,12 @@ private void prepareOptionsMenu(Menu menu) { mf.filter(menu, true); } + if (getFile().isFolder()) { + FileMenuFilter.hideMenuItems( + menu.findItem(R.id.action_send_file) + ); + } + // dual pane restrictions if (!getResources().getBoolean(R.bool.large_land_layout)){ FileMenuFilter.hideMenuItems( @@ -353,17 +358,10 @@ private void prepareOptionsMenu(Menu menu) { } } - public boolean optionsItemSelected(MenuItem item) { + private boolean optionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_send_file: { - if(getFile().isSharedWithMe() && !getFile().canReshare()){ - Snackbar.make(getView(), - R.string.resharing_is_not_allowed, - Snackbar.LENGTH_LONG - ).show(); - } else { - mContainerActivity.getFileOperationsHelper().sendShareFile(getFile()); - } + mContainerActivity.getFileOperationsHelper().sendShareFile(getFile(), true); return true; } case R.id.action_open_file_with: { diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 0ba7ae18d8c2..65217d76107c 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -615,17 +615,21 @@ public boolean isSearchUserSupportedSupported() { return false; } - public void sendShareFile(OCFile file) { + public void sendShareFile(OCFile file, boolean hideNcSharingOptions) { // Show dialog FragmentManager fm = mFileActivity.getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); ft.addToBackStack(null); - SendShareDialog mSendShareDialog = SendShareDialog.newInstance(file); + SendShareDialog mSendShareDialog = SendShareDialog.newInstance(file, hideNcSharingOptions); mSendShareDialog.setFileOperationsHelper(this); mSendShareDialog.show(ft, "TAG_SEND_SHARE_DIALOG"); } + public void sendShareFile(OCFile file) { + sendShareFile(file, false); + } + public void syncFiles(Collection files) { for (OCFile file : files) { syncFile(file); diff --git a/src/main/res/menu/file_details_actions_menu.xml b/src/main/res/menu/file_details_actions_menu.xml index ef0a816bd89a..8ed5110b01c9 100644 --- a/src/main/res/menu/file_details_actions_menu.xml +++ b/src/main/res/menu/file_details_actions_menu.xml @@ -1,21 +1,21 @@ Date: Tue, 15 May 2018 12:36:26 +0200 Subject: [PATCH 18/75] replace anonymous classes with lambdas --- .../com/owncloud/android/ui/adapter/ShareUserListAdapter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java index bb6111fc01d2..125a0c4abbe1 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java @@ -105,6 +105,7 @@ public View getView(final int position, View convertView, @NonNull ViewGroup par /// bind listener to unshare unshareButton.setOnClickListener(v -> mListener.unshareButtonPressed(mShares.get(position))); + } return view; } From 1a99f655b13685124e56c4af098f6f7dd9e8a66a Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 15 May 2018 13:58:52 +0200 Subject: [PATCH 19/75] add content description to preview image --- src/main/res/layout/toolbar_standard.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/res/layout/toolbar_standard.xml b/src/main/res/layout/toolbar_standard.xml index 006044f79563..6b18fb92eeb2 100644 --- a/src/main/res/layout/toolbar_standard.xml +++ b/src/main/res/layout/toolbar_standard.xml @@ -34,6 +34,7 @@ android:id="@+id/preview_image" android:layout_width="match_parent" android:layout_height="@dimen/nav_drawer_header_height" + android:contentDescription="@string/preview_image_description" android:scaleType="centerCrop" android:visibility="gone" /> From d7e8f6141a7d6339ef0ebce258a17d621547e2a2 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 16 May 2018 14:51:45 +0200 Subject: [PATCH 20/75] WiP share link functionality --- .../ui/activity/FileDisplayActivity.java | 105 +++++++- .../ui/adapter/FileDetailTabAdapter.java | 9 +- .../FileDetailActivitiesFragment.java | 29 +-- .../ui/fragment/FileDetailFragment.java | 17 +- .../fragment/FileDetailSharingFragment.java | 246 ++++++++++++++++-- .../layout/file_details_sharing_fragment.xml | 87 +++---- .../menu/file_detail_sharing_link_menu.xml | 41 +++ .../res/menu/file_detail_sharing_menu.xml | 4 - src/main/res/values/strings.xml | 6 +- 9 files changed, 450 insertions(+), 94 deletions(-) create mode 100644 src/main/res/menu/file_detail_sharing_link_menu.xml diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index f04ea242bd6d..015af8112c93 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -47,6 +47,7 @@ import android.support.annotation.NonNull; import android.support.design.widget.BottomNavigationView; import android.support.design.widget.Snackbar; +import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; @@ -73,6 +74,8 @@ import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; @@ -83,6 +86,7 @@ import com.owncloud.android.media.MediaServiceBinder; import com.owncloud.android.operations.CopyFileOperation; import com.owncloud.android.operations.CreateFolderOperation; +import com.owncloud.android.operations.CreateShareViaLinkOperation; import com.owncloud.android.operations.MoveFileOperation; import com.owncloud.android.operations.RefreshFolderOperation; import com.owncloud.android.operations.RemoveFileOperation; @@ -92,13 +96,16 @@ import com.owncloud.android.providers.UsersAndGroupsSearchProvider; import com.owncloud.android.syncadapter.FileSyncAdapter; import com.owncloud.android.ui.dialog.SendShareDialog; +import com.owncloud.android.ui.dialog.ShareLinkToDialog; import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; import com.owncloud.android.ui.events.SyncEventFinished; import com.owncloud.android.ui.events.TokenPushEvent; import com.owncloud.android.ui.fragment.ExtendedListFragment; import com.owncloud.android.ui.fragment.FileDetailFragment; +import com.owncloud.android.ui.fragment.FileDetailSharingFragment; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.ui.fragment.OCFileListFragment; +import com.owncloud.android.ui.fragment.ShareFileFragment; import com.owncloud.android.ui.fragment.TaskRetainerFragment; import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; @@ -146,6 +153,9 @@ public class FileDisplayActivity extends HookActivity private View mLeftFragmentContainer; private View mRightFragmentContainer; + private static final String TAG_PUBLIC_LINK = "PUBLIC_LINK"; + private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG"; + private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW"; private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS"; private static final String KEY_WAITING_TO_SEND = "WAITING_TO_SEND"; @@ -1694,8 +1704,9 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe } else if (operation instanceof CopyFileOperation) { onCopyFileOperationFinish((CopyFileOperation) operation, result); + } else if (operation instanceof CreateShareViaLinkOperation) { + onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); } - } private void refreshShowDetails() { @@ -1788,6 +1799,98 @@ private void onMoveFileOperationFinish(MoveFileOperation operation, } } + private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation, + RemoteOperationResult result) { + if (result.isSuccess()) { + updateFileFromDB(); + + // Create dialog to allow the user choose an app to send the link + Intent intentToShareLink = new Intent(Intent.ACTION_SEND); + + // if share to user and share via link multiple ocshares are returned, + // therefore filtering for public_link + String link = ""; + for (Object object : result.getData()) { + OCShare shareLink = (OCShare) object; + if (TAG_PUBLIC_LINK.equalsIgnoreCase(shareLink.getShareType().name())) { + link = shareLink.getShareLink(); + break; + } + } + + intentToShareLink.putExtra(Intent.EXTRA_TEXT, link); + intentToShareLink.setType("text/plain"); + + String username; + try { + OwnCloudAccount oca = new OwnCloudAccount(getAccount(), this); + if (oca.getDisplayName() != null && !oca.getDisplayName().isEmpty()) { + username = oca.getDisplayName(); + } else { + username = AccountUtils.getUsernameForAccount(getAccount()); + } + } catch (Exception e) { + username = AccountUtils.getUsernameForAccount(getAccount()); + } + + if (username != null) { + intentToShareLink.putExtra( + Intent.EXTRA_SUBJECT, + getString( + R.string.subject_user_shared_with_you, + username, + getFile().getFileName() + ) + ); + } else { + intentToShareLink.putExtra( + Intent.EXTRA_SUBJECT, + getString( + R.string.subject_shared_with_you, + getFile().getFileName() + ) + ); + } + + String[] packagesToExclude = new String[]{getPackageName()}; + DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intentToShareLink, packagesToExclude); + chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); + + } else { + // Detect Failure (403) --> maybe needs password + String password = operation.getPassword(); + if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_FORBIDDEN && + (password == null || password.length() == 0) && + getCapabilities().getFilesSharingPublicEnabled().isUnknown()) { + // Was tried without password, but not sure that it's optional. + + // Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener + FileDetailFragment fileDetailFragment = getShareFileFragment(); + if (fileDetailFragment != null + && fileDetailFragment.isAdded()) { // only if added to the view hierarchy!! + + fileDetailFragment.getFileDetailSharingFragment().requestPasswordForShareViaLink(true); + } + + } else { + Snackbar.make( + findViewById(android.R.id.content), + ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()), + Snackbar.LENGTH_LONG + ).show(); + } + } + } + + /** + * Shortcut to get access to the {@link FileDetailFragment} instance, if any + * + * @return A {@link FileDetailFragment} instance, or null + */ + private FileDetailFragment getShareFileFragment() { + return (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(TAG_SECOND_FRAGMENT); + } + /** * Updates the view associated to the activity after the finish of an operation trying to copy a * file. diff --git a/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java index 8b71890d99e3..11ae8ff74763 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java @@ -36,6 +36,8 @@ public class FileDetailTabAdapter extends FragmentStatePagerAdapter { private OCFile file; private Account account; + private FileDetailSharingFragment fileDetailSharingFragment; + public FileDetailTabAdapter(FragmentManager fm, OCFile file, Account account) { super(fm); @@ -49,12 +51,17 @@ public Fragment getItem(int position) { case 0: return FileDetailActivitiesFragment.newInstance(file, account); case 1: - return FileDetailSharingFragment.newInstance(file, account); + fileDetailSharingFragment = FileDetailSharingFragment.newInstance(file, account); + return fileDetailSharingFragment; default: return null; } } + public FileDetailSharingFragment getFileDetailSharingFragment() { + return fileDetailSharingFragment; + } + @Override public int getCount() { return 2; diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index cbe6835beeb6..d90b5303a390 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -143,27 +143,22 @@ public View onCreateView(@NonNull LayoutInflater inflater, fetchAndSetData(null); - swipeListRefreshLayout.setOnRefreshListener(() -> { - setLoadingMessage(); - if (swipeListRefreshLayout != null && swipeListRefreshLayout.isRefreshing()) { - swipeListRefreshLayout.setRefreshing(false); - } - fetchAndSetData(null); - } - ); - - swipeEmptyListRefreshLayout.setOnRefreshListener(() -> { - setLoadingMessage(); - if (swipeEmptyListRefreshLayout != null && swipeEmptyListRefreshLayout.isRefreshing()) { - swipeEmptyListRefreshLayout.setRefreshing(false); - } - fetchAndSetData(null); - } - ); + swipeListRefreshLayout.setOnRefreshListener( + () -> onRefreshListLayout(swipeListRefreshLayout)); + swipeEmptyListRefreshLayout.setOnRefreshListener( + () -> onRefreshListLayout(swipeEmptyListRefreshLayout)); return view; } + private void onRefreshListLayout(SwipeRefreshLayout refreshLayout) { + setLoadingMessage(); + if (refreshLayout != null && refreshLayout.isRefreshing()) { + refreshLayout.setRefreshing(false); + } + fetchAndSetData(null); + } + private void setLoadingMessage() { emptyContentHeadline.setText(R.string.file_list_loading); emptyContentMessage.setText(""); diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index f0bdc65a68c8..26e0ce4a0a3a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -163,6 +163,15 @@ public FileDetailFragment() { progressListener = null; } + /** + * return the reference to the file detail sharing fragment to communicate with it. + * + * @return reference to the {@link FileDetailSharingFragment} + */ + public FileDetailSharingFragment getFileDetailSharingFragment() { + return ((FileDetailTabAdapter)viewPager.getAdapter()).getFileDetailSharingFragment(); + } + @Override public void onResume() { super.onResume(); @@ -345,16 +354,12 @@ private void prepareOptionsMenu(Menu menu) { } if (getFile().isFolder()) { - FileMenuFilter.hideMenuItems( - menu.findItem(R.id.action_send_file) - ); + FileMenuFilter.hideMenuItems(menu.findItem(R.id.action_send_file)); } // dual pane restrictions if (!getResources().getBoolean(R.bool.large_land_layout)){ - FileMenuFilter.hideMenuItems( - menu.findItem(R.id.action_sync_account) - ); + FileMenuFilter.hideMenuItems(menu.findItem(R.id.action_sync_account)); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 1c488c5b9111..029319a0d8b5 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -23,17 +23,26 @@ import android.accounts.Account; import android.app.SearchManager; import android.content.Context; +import android.content.res.Resources; +import android.graphics.PorterDuff; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; +import android.support.v7.widget.AppCompatButton; import android.support.v7.widget.SearchView; +import android.support.v7.widget.SwitchCompat; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; +import android.widget.CheckBox; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; +import android.widget.PopupMenu; import android.widget.TextView; import com.owncloud.android.R; @@ -42,14 +51,21 @@ import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder; +import com.owncloud.android.lib.resources.shares.ShareType; +import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.UserListAdapter; +import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; +import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.utils.ThemeUtils; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; import butterknife.Unbinder; public class FileDetailSharingFragment extends Fragment implements UserListAdapter.ShareeListAdapterListener { @@ -63,21 +79,11 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt private OCFile file; private Account account; + private OCCapability capabilities; + private OCShare publicShare; private Unbinder unbinder; - @BindView(R.id.fdShareTitle) - TextView shareTitle; - - @BindView(R.id.fdShareWithUsersTitle) - TextView shareWithUsersTitle; - - @BindView(R.id.fdSharebyLink) - TextView sharebyLink; - - @BindView(R.id.fdShareLinkIcon) - ImageView linkIcon; - @BindView(R.id.searchView) SearchView searchView; @@ -87,6 +93,19 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt @BindView(R.id.fdShareNoUsers) TextView noList; + @BindView(R.id.share_by_link) + CheckBox shareByLink; + + @BindView(R.id.overflow_menu_share_link) + ImageView overflowMenuShareLink; + + @BindView(R.id.share_by_link_allow_editing) + CheckBox shareByLinkAllowEditing; + + @BindView(R.id.share_by_link_container) + LinearLayout shareByLinkContainer; + + public static FileDetailSharingFragment newInstance(OCFile file, Account account) { FileDetailSharingFragment fragment = new FileDetailSharingFragment(); Bundle args = new Bundle(); @@ -96,6 +115,15 @@ public static FileDetailSharingFragment newInstance(OCFile file, Account account return fragment; } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + Log_OC.d(TAG, "onActivityCreated"); + + // Load known capabilities of the server from DB + refreshCapabilitiesFromDB(); + } + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, @@ -132,9 +160,6 @@ public void onAttach(Context context) { } private void setupView() { - shareTitle.setTextColor(ThemeUtils.primaryAccentColor(getContext())); - shareWithUsersTitle.setTextColor(ThemeUtils.primaryAccentColor(getContext())); - setShareByLinkInfo(file.isSharedViaLink()); setShareWithUserInfo(); setupSearchView(); @@ -170,15 +195,23 @@ public boolean onQueryTextChange(String newText) { } /** - * Updates Share by link data + * Updates Share by link UI * * @param isShareByLink flag is share by link is enable */ private void setShareByLinkInfo(boolean isShareByLink) { - sharebyLink.setText(isShareByLink ? R.string.filedetails_share_link_enable : - R.string.filedetails_share_link_disable); + shareByLink.setChecked(isShareByLink); + setLinkDetailVisible(isShareByLink); + } - linkIcon.setVisibility(isShareByLink ? View.VISIBLE : View.GONE); + private void setLinkDetailVisible(boolean visible) { + if (visible) { + shareByLinkAllowEditing.setVisibility(View.VISIBLE); + overflowMenuShareLink.setVisibility(View.VISIBLE); + } else { + shareByLinkAllowEditing.setVisibility(View.INVISIBLE); + overflowMenuShareLink.setVisibility(View.INVISIBLE); + } } /** @@ -187,7 +220,8 @@ private void setShareByLinkInfo(boolean isShareByLink) { private void setShareWithUserInfo(){ // Get Users and Groups if (((FileActivity) getActivity()).getStorageManager() != null) { - FileDataStorageManager fileDataStorageManager = ((FileActivity) getActivity()).getStorageManager(); + FileDataStorageManager fileDataStorageManager = ((FileActivity) getActivity()) + .getStorageManager(); mShares = fileDataStorageManager.getSharesWithForAFile( file.getRemotePath(),account.name ); @@ -241,6 +275,99 @@ private static void setListViewHeightBasedOnChildren(ListView listView) { listView.requestLayout(); } + @OnClick(R.id.share_by_link) + public void toggleShareByLink() { + if (shareByLink.isChecked()) { + if (capabilities != null && + capabilities.getFilesSharingPublicPasswordEnforced().isTrue()) { + // password enforced by server, request to the user before trying to create + requestPasswordForShareViaLink(true); + + } else { + // create without password if not enforced by server or we don't know if enforced; + ((FileActivity) getActivity()).getFileOperationsHelper().shareFileViaLink(file, null); + + // ShareActivity#onCreateShareViaLinkOperationFinish will take care if password + // is enforced by the server but app doesn't know, or if server version is + // older than OwnCloudVersion#MINIMUM_VERSION_CAPABILITIES_API + } + + } else { + ((FileActivity) getActivity()).getFileOperationsHelper().unshareFileViaLink(file); + } + } + + @OnClick(R.id.share_link_label) + public void showSendLinkTo() { + if (file.isSharedViaLink()) { + ((FileActivity) getActivity()).getFileOperationsHelper().getFileWithLink(file); + } + } + + @OnClick(R.id.share_by_link_allow_editing) + public void toggleShareLinkAllowEditing() { + if (file.isSharedViaLink()) { + ((FileActivity) getActivity()).getFileOperationsHelper().setUploadPermissionsToShare(file, shareByLinkAllowEditing.isChecked()); + } + } + + @OnClick(R.id.overflow_menu_share_link) + public void showLinkOverflowMenu() { + PopupMenu popup = new PopupMenu(getActivity(), overflowMenuShareLink); + popup.inflate(R.menu.file_detail_sharing_menu); + prepareOptionsMenu(popup.getMenu()); + popup.setOnMenuItemClickListener(this::optionsItemSelected); + popup.show(); + } + + private void prepareOptionsMenu(Menu menu) { + // TODO implement setting the correct menu item titles based on the share/permissions info + Resources res = getResources(); + setupExpirationDateMenuItem(menu.findItem(R.id.action_share_link_expiration_date), res); + } + + private void setupExpirationDateMenuItem(MenuItem expirationDate, Resources res) { + long expirationDateValue = publicShare.getExpirationDate(); + if (expirationDateValue > 0) { + String formattedDate = + SimpleDateFormat.getDateInstance().format( + new Date(expirationDateValue) + ); + expirationDate.setTitle(res.getString( + R.string.share_via_link_menu_expiration_date_label, + formattedDate + )); + } else { + expirationDate.setTitle(res.getString( + R.string.share_via_link_menu_expiration_date_label, + res.getString(R.string.share_via_link_menu_expiration_date_never) + )); + } + } + + private boolean optionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_share_link_permissions: { + return true; + } + case R.id.action_share_link_password: { + requestPasswordForShareViaLink(false); + return true; + } + case R.id.action_share_link_expiration_date: { + // TODO extend date picker to allow for clearing/unsetting a date + ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment.newInstance(file, -1); + dialog.show( + getActivity().getSupportFragmentManager(), + ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG + ); + return true; + } + default: + return super.onOptionsItemSelected(item); + } + } + @Override public void unshareWith(OCShare share) { ((FileActivity)getActivity()).getFileOperationsHelper() @@ -271,4 +398,83 @@ public int updatePermissionsToShare(OCShare share, boolean canReshare, boolean c return permissions; } + + /** + * Starts a dialog that requests a password to the user to protect a share link. + * + * @param createShare When 'true', the request for password will be followed by the creation of a new + * public link; when 'false', a public share is assumed to exist, and the password + * is bound to it. + */ + public void requestPasswordForShareViaLink(boolean createShare) { + SharePasswordDialogFragment dialog = SharePasswordDialogFragment.newInstance(file, createShare); + dialog.show(getFragmentManager(), SharePasswordDialogFragment.PASSWORD_FRAGMENT); + } + + /** + * Get known server capabilities from DB + *

+ * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} + * instance ready to use. If not ready, does nothing. + */ + public void refreshCapabilitiesFromDB() { + if (((FileActivity) getActivity()).getStorageManager() != null) { + capabilities = ((FileActivity) getActivity()).getStorageManager().getCapability(account.name); + } + } + + /** + * Get public link from the DB to fill in the "Share link" section in the UI. + * + * Takes into account server capabilities before reading database. + * + * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} + * instance ready to use. If not ready, does nothing. + */ + public void refreshPublicShareFromDB() { + if (isPublicShareDisabled()) { + hidePublicShare(); + } else if (((FileActivity) getActivity()).getStorageManager() != null) { + // Get public share + publicShare = ((FileActivity) getActivity()).getStorageManager().getFirstShareByPathAndType( + file.getRemotePath(), + ShareType.PUBLIC_LINK, + "" + ); + + // Update public share section + updatePublicShareSection(); + } + } + + /** + * Updates in the UI the section about public share with the information in the current public share bound to + * mFile, if any. + */ + private void updatePublicShareSection() { + if (publicShare != null && ShareType.PUBLIC_LINK.equals(publicShare.getShareType())) { + shareByLink.setChecked(true); + + if (publicShare.getPermissions() > OCShare.READ_PERMISSION_FLAG) { + shareByLinkAllowEditing.setChecked(true); + } else { + shareByLinkAllowEditing.setChecked(false); + } + } + } + + /** + * @return 'True' when public share is disabled in the server. + */ + private boolean isPublicShareDisabled() { + return (capabilities != null && capabilities.getFilesSharingPublicEnabled().isFalse() + ); + } + + /** + * Hides all the UI elements related to public share. + */ + private void hidePublicShare() { + shareByLinkContainer.setVisibility(View.GONE); + } } diff --git a/src/main/res/layout/file_details_sharing_fragment.xml b/src/main/res/layout/file_details_sharing_fragment.xml index db6e66ae40e8..5e9dc621613a 100644 --- a/src/main/res/layout/file_details_sharing_fragment.xml +++ b/src/main/res/layout/file_details_sharing_fragment.xml @@ -28,65 +28,64 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingTop="@dimen/standard_padding"> + android:paddingTop="@dimen/standard_half_padding"> - + android:id="@+id/searchView" + android:hint="@string/share_search" + style="@style/ownCloud.SearchView"/> + android:paddingTop="@dimen/standard_half_padding"> - + - + android:layout_gravity="start|center" + android:textColor="@color/black" + android:layout_weight="1" + android:textSize="@dimen/two_line_secondary_text_size" + android:paddingTop="@dimen/standard_half_padding" + android:paddingBottom="@dimen/standard_half_padding" + android:text="@string/share_via_link_section_title" /> - + - + + + +

+ + + + + + diff --git a/src/main/res/menu/file_detail_sharing_menu.xml b/src/main/res/menu/file_detail_sharing_menu.xml index 647a8330f977..ca200e296947 100644 --- a/src/main/res/menu/file_detail_sharing_menu.xml +++ b/src/main/res/menu/file_detail_sharing_menu.xml @@ -25,28 +25,24 @@ diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index ccf60123dac8..60764ef536a4 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -480,11 +480,15 @@ Share with users and groups No data shared with users yet Add user or group + Permissions (%1$s) + Password protect (%1$s) + Set expiration date (%1$s) + never Share link Set expiration date Protect with password Secured - Allow edits + Allow editing Hide file listing Get link Share with… From 362061bef3b3adb2cc4d42b121b8840a4c06f93f Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 16 May 2018 14:58:10 +0200 Subject: [PATCH 21/75] fixed 2 NPEs --- .../android/ui/fragment/FileDetailSharingFragment.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 029319a0d8b5..209a5bb68699 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -118,10 +118,9 @@ public static FileDetailSharingFragment newInstance(OCFile file, Account account @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Log_OC.d(TAG, "onActivityCreated"); - // Load known capabilities of the server from DB refreshCapabilitiesFromDB(); + refreshPublicShareFromDB(); } @Override @@ -314,7 +313,7 @@ public void toggleShareLinkAllowEditing() { @OnClick(R.id.overflow_menu_share_link) public void showLinkOverflowMenu() { PopupMenu popup = new PopupMenu(getActivity(), overflowMenuShareLink); - popup.inflate(R.menu.file_detail_sharing_menu); + popup.inflate(R.menu.file_detail_sharing_link_menu); prepareOptionsMenu(popup.getMenu()); popup.setOnMenuItemClickListener(this::optionsItemSelected); popup.show(); @@ -351,11 +350,14 @@ private boolean optionsItemSelected(MenuItem item) { return true; } case R.id.action_share_link_password: { + // TODO extend password dialog to allow for clearing/unsetting a password + // TODO refresh share object after password has been set requestPasswordForShareViaLink(false); return true; } case R.id.action_share_link_expiration_date: { // TODO extend date picker to allow for clearing/unsetting a date + // TODO refresh share object after new exp. date has been set ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment.newInstance(file, -1); dialog.show( getActivity().getSupportFragmentManager(), From 391368947cbfc1195b1decfaaf3658d17d557c14 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Thu, 17 May 2018 17:19:18 +0200 Subject: [PATCH 22/75] add sharee works Signed-off-by: tobiasKaminsky --- src/main/AndroidManifest.xml | 1 + .../ui/activity/FileDisplayActivity.java | 29 +++++++++++-------- .../fragment/FileDetailSharingFragment.java | 20 ++++--------- src/main/res/values/strings.xml | 1 + 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index f7007b20245a..a197673da878 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -80,6 +80,7 @@ tools:ignore="UnusedAttribute"> diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 015af8112c93..a4380dc2a856 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -87,6 +87,7 @@ import com.owncloud.android.operations.CopyFileOperation; import com.owncloud.android.operations.CreateFolderOperation; import com.owncloud.android.operations.CreateShareViaLinkOperation; +import com.owncloud.android.operations.CreateShareWithShareeOperation; import com.owncloud.android.operations.MoveFileOperation; import com.owncloud.android.operations.RefreshFolderOperation; import com.owncloud.android.operations.RemoveFileOperation; @@ -102,10 +103,8 @@ import com.owncloud.android.ui.events.TokenPushEvent; import com.owncloud.android.ui.fragment.ExtendedListFragment; import com.owncloud.android.ui.fragment.FileDetailFragment; -import com.owncloud.android.ui.fragment.FileDetailSharingFragment; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.ui.fragment.OCFileListFragment; -import com.owncloud.android.ui.fragment.ShareFileFragment; import com.owncloud.android.ui.fragment.TaskRetainerFragment; import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; @@ -515,11 +514,7 @@ protected void onNewIntent(Intent intent) { } if (!shareeNames.contains(shareWith)) { - - doShareWith( - shareWith, - data.getAuthority() - ); + doShareWith(shareWith, data.getAuthority()); } } else { @@ -1689,23 +1684,20 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe if (operation instanceof RemoveFileOperation) { onRemoveFileOperationFinish((RemoveFileOperation) operation, result); - } else if (operation instanceof RenameFileOperation) { onRenameFileOperationFinish((RenameFileOperation) operation, result); - } else if (operation instanceof SynchronizeFileOperation) { onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result); - } else if (operation instanceof CreateFolderOperation) { onCreateFolderOperationFinish((CreateFolderOperation) operation, result); - } else if (operation instanceof MoveFileOperation) { onMoveFileOperationFinish((MoveFileOperation) operation, result); - } else if (operation instanceof CopyFileOperation) { onCopyFileOperationFinish((CopyFileOperation) operation, result); } else if (operation instanceof CreateShareViaLinkOperation) { onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); + } else if (operation instanceof CreateShareWithShareeOperation) { + onCreateShareWithShareeOperationFinish(result); } } @@ -1882,6 +1874,19 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope } } + private void onCreateShareWithShareeOperationFinish(RemoteOperationResult result) { + + Fragment fileDetailFragment = getSecondFragment(); + + if (result.isSuccess()) { + if (fileDetailFragment instanceof FileDetailFragment) { + ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment().setShareWithUserInfo(); + } + } else { + Snackbar.make(fileDetailFragment.getView(), R.string.sharee_add_failed, Snackbar.LENGTH_LONG).show(); + } + } + /** * Shortcut to get access to the {@link FileDetailFragment} instance, if any * diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 209a5bb68699..3d0832132a00 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -24,13 +24,10 @@ import android.app.SearchManager; import android.content.Context; import android.content.res.Resources; -import android.graphics.PorterDuff; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; -import android.support.v7.widget.AppCompatButton; import android.support.v7.widget.SearchView; -import android.support.v7.widget.SwitchCompat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -57,7 +54,6 @@ import com.owncloud.android.ui.adapter.UserListAdapter; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; -import com.owncloud.android.utils.ThemeUtils; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -216,14 +212,12 @@ private void setLinkDetailVisible(boolean visible) { /** * Update Share With data */ - private void setShareWithUserInfo(){ + public void setShareWithUserInfo() { // Get Users and Groups if (((FileActivity) getActivity()).getStorageManager() != null) { - FileDataStorageManager fileDataStorageManager = ((FileActivity) getActivity()) - .getStorageManager(); - mShares = fileDataStorageManager.getSharesWithForAFile( - file.getRemotePath(),account.name - ); + FileDataStorageManager fileDataStorageManager = ((FileActivity) getActivity()).getStorageManager(); + + mShares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), account.name); // Update list of users/groups updateListOfUserGroups(); @@ -233,10 +227,8 @@ private void setShareWithUserInfo(){ private void updateListOfUserGroups() { // Update list of users/groups // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed - UserListAdapter mUserGroupsAdapter = new UserListAdapter( - getActivity().getApplicationContext(), - R.layout.share_user_item, mShares, account, file,this - ); + UserListAdapter mUserGroupsAdapter = new UserListAdapter(getActivity().getApplicationContext(), + R.layout.share_user_item, mShares, account, file, this); if (mShares.size() > 0) { usersList.setVisibility(View.VISIBLE); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 60764ef536a4..49d4a0d4e485 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -791,4 +791,5 @@ Create new folder Virus detected. Upload cannot be completed! Tags + Adding sharee failed From 607bc12b567cd13a730fa628f21e987729379b09 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 18 May 2018 09:38:35 +0200 Subject: [PATCH 23/75] add unset of password for share link --- .../dialog/SharePasswordDialogFragment.java | 33 +++++++++++-------- src/main/res/values/strings.xml | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java index 744649f260cb..af5f181e26d6 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java @@ -1,7 +1,10 @@ -/** +/* * ownCloud Android client application + * * @author masensio - * Copyright (C) 2015 ownCloud Inc. + * @author Andy Scherzinger + * Copyright (C) 2015 ownCloud GmbH. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -14,7 +17,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package com.owncloud.android.ui.dialog; @@ -42,7 +44,6 @@ * * Triggers the share when the password is introduced. */ - public class SharePasswordDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { @@ -60,6 +61,7 @@ public void onStart() { AlertDialog alertDialog = (AlertDialog) getDialog(); alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ThemeUtils.primaryAccentColor(getContext())); alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(ThemeUtils.primaryAccentColor(getContext())); + alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(getResources().getColor(R.color.highlight_textColor_Warning)); } /** @@ -102,13 +104,13 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { builder.setView(v) .setPositiveButton(R.string.common_ok, this) .setNegativeButton(R.string.common_cancel, this) + .setNeutralButton(R.string.common_delete, this) .setTitle(R.string.share_link_password_title); Dialog d = builder.create(); d.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); return d; } - @Override public void onClick(DialogInterface dialog, int which) { if (which == AlertDialog.BUTTON_POSITIVE) { @@ -125,16 +127,19 @@ public void onClick(DialogInterface dialog, int which) { return; } - if (mCreateShare) { - // Share the file - ((FileActivity) getActivity()).getFileOperationsHelper(). - shareFileViaLink(mFile, password); + setPassword(mCreateShare, mFile, password); + } else if (which == AlertDialog.BUTTON_NEUTRAL) { + setPassword(mCreateShare, mFile, null); + } + } - } else { - // updat existing link - ((FileActivity) getActivity()).getFileOperationsHelper(). - setPasswordToShareViaLink(mFile, password); - } + private void setPassword(boolean createShare, OCFile file, String password) { + if (createShare) { + ((FileActivity) getActivity()).getFileOperationsHelper(). + shareFileViaLink(file, password); + } else { + ((FileActivity) getActivity()).getFileOperationsHelper(). + setPasswordToShareViaLink(file, password); } } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 49d4a0d4e485..a1aff3ada9d2 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -493,6 +493,7 @@ Get link Share with… Share with %1$s + Unset Name, federated cloud ID or email address… From 072e6465528ce4c6565646d023630a3521ec7ed7 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 18 May 2018 09:41:47 +0200 Subject: [PATCH 24/75] reformatted code for line length 120 --- .../dialog/SharePasswordDialogFragment.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java index af5f181e26d6..cf0fdbe1c463 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java @@ -41,7 +41,7 @@ /** * Dialog to input the password for sharing a file/folder. - * + *

* Triggers the share when the password is introduced. */ public class SharePasswordDialogFragment extends DialogFragment @@ -51,27 +51,30 @@ public class SharePasswordDialogFragment extends DialogFragment private static final String ARG_CREATE_SHARE = "CREATE_SHARE"; public static final String PASSWORD_FRAGMENT = "PASSWORD_FRAGMENT"; - private OCFile mFile; - private boolean mCreateShare; + private OCFile file; + private boolean createShare; @Override public void onStart() { super.onStart(); AlertDialog alertDialog = (AlertDialog) getDialog(); - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ThemeUtils.primaryAccentColor(getContext())); - alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(ThemeUtils.primaryAccentColor(getContext())); - alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(getResources().getColor(R.color.highlight_textColor_Warning)); + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) + .setTextColor(ThemeUtils.primaryAccentColor(getContext())); + alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) + .setTextColor(ThemeUtils.primaryAccentColor(getContext())); + alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) + .setTextColor(getResources().getColor(R.color.highlight_textColor_Warning)); } /** * Public factory method to create new SharePasswordDialogFragment instances. * - * @param file OCFile bound to the public share that which password will be set or updated - * @param createShare When 'true', the request for password will be followed by the creation of a new - * public link; when 'false', a public share is assumed to exist, and the password - * is bound to it. - * @return Dialog ready to show. + * @param file OCFile bound to the public share that which password will be set or updated + * @param createShare When 'true', the request for password will be followed by the creation of a new + * public link; when 'false', a public share is assumed to exist, and the password + * is bound to it. + * @return Dialog ready to show. */ public static SharePasswordDialogFragment newInstance(OCFile file, boolean createShare) { SharePasswordDialogFragment frag = new SharePasswordDialogFragment(); @@ -85,8 +88,8 @@ public static SharePasswordDialogFragment newInstance(OCFile file, boolean creat @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - mFile = getArguments().getParcelable(ARG_FILE); - mCreateShare = getArguments().getBoolean(ARG_CREATE_SHARE, false); + file = getArguments().getParcelable(ARG_FILE); + createShare = getArguments().getBoolean(ARG_CREATE_SHARE, false); // Inflate the layout for the dialog LayoutInflater inflater = getActivity().getLayoutInflater(); @@ -94,7 +97,10 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { // Setup layout EditText inputText = v.findViewById(R.id.share_password); - inputText.getBackground().setColorFilter(ThemeUtils.primaryAccentColor(getContext()), PorterDuff.Mode.SRC_ATOP); + inputText.getBackground().setColorFilter( + ThemeUtils.primaryAccentColor(getContext()), + PorterDuff.Mode.SRC_ATOP + ); inputText.setText(""); inputText.requestFocus(); @@ -115,8 +121,8 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { public void onClick(DialogInterface dialog, int which) { if (which == AlertDialog.BUTTON_POSITIVE) { String password = - ((TextView)(getDialog().findViewById(R.id.share_password))) - .getText().toString(); + ((TextView) (getDialog().findViewById(R.id.share_password))) + .getText().toString(); if (password.length() <= 0) { Snackbar.make( @@ -127,9 +133,9 @@ public void onClick(DialogInterface dialog, int which) { return; } - setPassword(mCreateShare, mFile, password); + setPassword(createShare, file, password); } else if (which == AlertDialog.BUTTON_NEUTRAL) { - setPassword(mCreateShare, mFile, null); + setPassword(createShare, file, null); } } From f5d9af71b92f74d7b60e0267df6c8623038b71c8 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 18 May 2018 15:59:18 +0200 Subject: [PATCH 25/75] WiP expiration Date, permission text calculation --- .../ExpirationDatePickerDialogFragment.java | 24 +++++++---- .../fragment/FileDetailSharingFragment.java | 40 ++++++++++++++++++- .../ui/fragment/ShareFileFragment.java | 2 +- src/main/res/layout/share_file_layout.xml | 2 - src/main/res/values/strings.xml | 6 ++- 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java index 03739fdb01a5..a6e88c8489ce 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java @@ -24,10 +24,12 @@ import android.app.DatePickerDialog; import android.app.Dialog; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.text.format.DateUtils; import android.widget.DatePicker; +import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.ui.activity.FileActivity; @@ -50,14 +52,14 @@ public class ExpirationDatePickerDialogFragment private static final String ARG_CHOSEN_DATE_IN_MILLIS = "CHOSEN_DATE_IN_MILLIS"; /** File to bind an expiration date */ - private OCFile mFile; + private OCFile file; /** * Factory method to create new instances * * @param file File to bind an expiration date * @param chosenDateInMillis Date chosen when the dialog appears - * @return New dialog instance + * @return New dialog instance */ public static ExpirationDatePickerDialogFragment newInstance(OCFile file, long chosenDateInMillis) { Bundle arguments = new Bundle(); @@ -72,12 +74,13 @@ public static ExpirationDatePickerDialogFragment newInstance(OCFile file, long c /** * {@inheritDoc} * - * @return A new dialog to let the user choose an expiration date that will be bound to a share link. + * @return A new dialog to let the user choose an expiration date that will be bound to a share link. */ @Override + @NonNull public Dialog onCreateDialog(Bundle savedInstanceState) { // Load arguments - mFile = getArguments().getParcelable(ARG_FILE); + file = getArguments().getParcelable(ARG_FILE); // Chosen date received as an argument must be later than tomorrow ; default to tomorrow in other case final Calendar chosenDate = Calendar.getInstance(); @@ -97,6 +100,13 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { chosenDate.get(Calendar.MONTH), chosenDate.get(Calendar.DAY_OF_MONTH) ); + dialog.setButton( + Dialog.BUTTON_NEUTRAL, + getText(R.string.share_via_link_unset_password), + (dialog1, which) -> { + ((FileActivity) getActivity()).getFileOperationsHelper() + .setExpirationDateToShareViaLink(file, 0); + }); // Prevent days in the past may be chosen DatePicker picker = dialog.getDatePicker(); @@ -127,9 +137,7 @@ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth chosenDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); long chosenDateInMillis = chosenDate.getTimeInMillis(); - ((FileActivity)getActivity()).getFileOperationsHelper().setExpirationDateToShareViaLink( - mFile, - chosenDateInMillis - ); + ((FileActivity) getActivity()).getFileOperationsHelper() + .setExpirationDateToShareViaLink(file, chosenDateInMillis); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 3d0832132a00..647fb8805a17 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -312,11 +312,48 @@ public void showLinkOverflowMenu() { } private void prepareOptionsMenu(Menu menu) { - // TODO implement setting the correct menu item titles based on the share/permissions info Resources res = getResources(); + setupPermissionsMenuItem(menu.findItem(R.id.action_share_link_permissions), res); + setupPasswordMenuItem(menu.findItem(R.id.action_share_link_password), res); setupExpirationDateMenuItem(menu.findItem(R.id.action_share_link_expiration_date), res); } + private void setupPermissionsMenuItem(MenuItem permission, Resources res) { + if (publicShare.getPermissions() > OCShare.READ_PERMISSION_FLAG) { + boolean uploadOnly = (publicShare.getPermissions() & OCShare.READ_PERMISSION_FLAG) != 0; + if (uploadOnly) { + permission.setTitle(res.getString( + R.string.share_via_link_menu_permissions_label, + res.getString(R.string.share_privilege_can_edit), + res.getString(R.string.share_via_link_menu_permissions_upload_only_label) + )); + } else { + permission.setTitle(res.getString( + R.string.share_via_link_menu_permission_label, + res.getString(R.string.share_privilege_can_edit))); + } + } else { + // read only + permission.setTitle(res.getString( + R.string.share_via_link_menu_permission_label, + res.getString(R.string.share_via_link_menu_permissions_read_only_label))); + } + } + + private void setupPasswordMenuItem(MenuItem password, Resources res) { + if (publicShare.isPasswordProtected()) { + password.setTitle(res.getString( + R.string.share_via_link_menu_password_label, + res.getString(R.string.share_via_link_password_title) + )); + } else { + password.setTitle(res.getString( + R.string.share_via_link_menu_password_label, + res.getString(R.string.share_via_link_no_password_title) + )); + } + } + private void setupExpirationDateMenuItem(MenuItem expirationDate, Resources res) { long expirationDateValue = publicShare.getExpirationDate(); if (expirationDateValue > 0) { @@ -342,7 +379,6 @@ private boolean optionsItemSelected(MenuItem item) { return true; } case R.id.action_share_link_password: { - // TODO extend password dialog to allow for clearing/unsetting a password // TODO refresh share object after password has been set requestPasswordForShareViaLink(false); return true; diff --git a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java index 039a6db452f2..c094a2d6423a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java @@ -846,7 +846,7 @@ public void onClick(View v) { } - /// BEWARE: next methods will failed with NullPointerException if called before onCreateView() finishes + // BEWARE: next methods will failed with NullPointerException if called before onCreateView() finishes private SwitchCompat getShareViaLinkSwitch() { return (SwitchCompat) getView().findViewById(R.id.shareViaLinkSectionSwitch); diff --git a/src/main/res/layout/share_file_layout.xml b/src/main/res/layout/share_file_layout.xml index 21ad6bccfece..08a9eb3f8031 100644 --- a/src/main/res/layout/share_file_layout.xml +++ b/src/main/res/layout/share_file_layout.xml @@ -169,7 +169,6 @@ android:id="@+id/shareViaLinkEditPermissionSection" android:layout_width="match_parent" android:layout_height="wrap_content" - android:visibility="invisible" android:layout_marginBottom="@dimen/standard_half_margin" > @@ -204,7 +203,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/standard_half_margin" - android:visibility="invisible" > Share with users and groups No data shared with users yet Add user or group - Permissions (%1$s) + Permissions (%1$s) + Permissions (%1$s, %2$s) + read-only + upload-only Password protect (%1$s) Set expiration date (%1$s) never @@ -488,6 +491,7 @@ Set expiration date Protect with password Secured + none Allow editing Hide file listing Get link From 236924ccc2479843e5114a9ef2249de586daba12 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 18:22:52 +0200 Subject: [PATCH 26/75] add hide file listing --- .../dialog/SharePasswordDialogFragment.java | 23 +++-- .../fragment/FileDetailSharingFragment.java | 97 +++++++++---------- .../menu/file_detail_sharing_link_menu.xml | 5 +- 3 files changed, 62 insertions(+), 63 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java index cf0fdbe1c463..2a928a0717b9 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java @@ -25,11 +25,13 @@ import android.graphics.PorterDuff; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.widget.EditText; import android.widget.TextView; @@ -41,11 +43,10 @@ /** * Dialog to input the password for sharing a file/folder. - *

+ * * Triggers the share when the password is introduced. */ -public class SharePasswordDialogFragment extends DialogFragment - implements DialogInterface.OnClickListener { +public class SharePasswordDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String ARG_FILE = "FILE"; private static final String ARG_CREATE_SHARE = "CREATE_SHARE"; @@ -85,6 +86,12 @@ public static SharePasswordDialogFragment newInstance(OCFile file, boolean creat return frag; } + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return super.onCreateView(inflater, container, savedInstanceState); + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -120,9 +127,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { @Override public void onClick(DialogInterface dialog, int which) { if (which == AlertDialog.BUTTON_POSITIVE) { - String password = - ((TextView) (getDialog().findViewById(R.id.share_password))) - .getText().toString(); + String password = ((TextView) (getDialog().findViewById(R.id.share_password))).getText().toString(); if (password.length() <= 0) { Snackbar.make( @@ -141,11 +146,9 @@ public void onClick(DialogInterface dialog, int which) { private void setPassword(boolean createShare, OCFile file, String password) { if (createShare) { - ((FileActivity) getActivity()).getFileOperationsHelper(). - shareFileViaLink(file, password); + ((FileActivity) getActivity()).getFileOperationsHelper().shareFileViaLink(file, password); } else { - ((FileActivity) getActivity()).getFileOperationsHelper(). - setPasswordToShareViaLink(file, password); + ((FileActivity) getActivity()).getFileOperationsHelper().setPasswordToShareViaLink(file, password); } } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 647fb8805a17..5c709b8ddc5e 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -23,9 +23,12 @@ import android.accounts.Account; import android.app.SearchManager; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; +import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v7.widget.SearchView; import android.view.LayoutInflater; @@ -71,7 +74,7 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt private static final String ARG_ACCOUNT = "ACCOUNT"; // to show share with users/groups info - private ArrayList mShares; + private ArrayList shares; private OCFile file; private Account account; @@ -101,7 +104,6 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt @BindView(R.id.share_by_link_container) LinearLayout shareByLinkContainer; - public static FileDetailSharingFragment newInstance(OCFile file, Account account) { FileDetailSharingFragment fragment = new FileDetailSharingFragment(); Bundle args = new Bundle(); @@ -120,9 +122,7 @@ public void onActivityCreated(Bundle savedInstanceState) { } @Override - public View onCreateView(@NonNull LayoutInflater inflater, - ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { file = getArguments().getParcelable(ARG_FILE); account = getArguments().getParcelable(ARG_ACCOUNT); @@ -163,9 +163,7 @@ private void setupView() { private void setupSearchView() { SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); // assumes parent activity is the searchable activity - searchView.setSearchableInfo(searchManager - .getSearchableInfo(getActivity().getComponentName()) - ); + searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); // do not iconify the widget; expand it by default searchView.setIconifiedByDefault(false); @@ -217,7 +215,7 @@ public void setShareWithUserInfo() { if (((FileActivity) getActivity()).getStorageManager() != null) { FileDataStorageManager fileDataStorageManager = ((FileActivity) getActivity()).getStorageManager(); - mShares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), account.name); + shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), account.name); // Update list of users/groups updateListOfUserGroups(); @@ -225,12 +223,11 @@ public void setShareWithUserInfo() { } private void updateListOfUserGroups() { - // Update list of users/groups // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed UserListAdapter mUserGroupsAdapter = new UserListAdapter(getActivity().getApplicationContext(), - R.layout.share_user_item, mShares, account, file, this); + R.layout.share_user_item, shares, account, file, this); - if (mShares.size() > 0) { + if (shares.size() > 0) { usersList.setVisibility(View.VISIBLE); usersList.setAdapter(mUserGroupsAdapter); noList.setVisibility(View.GONE); @@ -242,7 +239,7 @@ private void updateListOfUserGroups() { } /** - * Fix scroll in listview when the parent is a ScrollView + * Fix scroll in ListView when the parent is a ScrollView */ private static void setListViewHeightBasedOnChildren(ListView listView) { ListAdapter listAdapter = listView.getAdapter(); @@ -277,10 +274,6 @@ public void toggleShareByLink() { } else { // create without password if not enforced by server or we don't know if enforced; ((FileActivity) getActivity()).getFileOperationsHelper().shareFileViaLink(file, null); - - // ShareActivity#onCreateShareViaLinkOperationFinish will take care if password - // is enforced by the server but app doesn't know, or if server version is - // older than OwnCloudVersion#MINIMUM_VERSION_CAPABILITIES_API } } else { @@ -313,33 +306,11 @@ public void showLinkOverflowMenu() { private void prepareOptionsMenu(Menu menu) { Resources res = getResources(); - setupPermissionsMenuItem(menu.findItem(R.id.action_share_link_permissions), res); + setupHideFileListingMenuItem(menu.findItem(R.id.action_share_link_hide_file_listing)); setupPasswordMenuItem(menu.findItem(R.id.action_share_link_password), res); setupExpirationDateMenuItem(menu.findItem(R.id.action_share_link_expiration_date), res); } - private void setupPermissionsMenuItem(MenuItem permission, Resources res) { - if (publicShare.getPermissions() > OCShare.READ_PERMISSION_FLAG) { - boolean uploadOnly = (publicShare.getPermissions() & OCShare.READ_PERMISSION_FLAG) != 0; - if (uploadOnly) { - permission.setTitle(res.getString( - R.string.share_via_link_menu_permissions_label, - res.getString(R.string.share_privilege_can_edit), - res.getString(R.string.share_via_link_menu_permissions_upload_only_label) - )); - } else { - permission.setTitle(res.getString( - R.string.share_via_link_menu_permission_label, - res.getString(R.string.share_privilege_can_edit))); - } - } else { - // read only - permission.setTitle(res.getString( - R.string.share_via_link_menu_permission_label, - res.getString(R.string.share_via_link_menu_permissions_read_only_label))); - } - } - private void setupPasswordMenuItem(MenuItem password, Resources res) { if (publicShare.isPasswordProtected()) { password.setTitle(res.getString( @@ -354,6 +325,15 @@ private void setupPasswordMenuItem(MenuItem password, Resources res) { } } + private void setupHideFileListingMenuItem(MenuItem fileListing) { + if (!file.isFolder()) { + fileListing.setVisible(false); + } else { + boolean readOnly = (publicShare.getPermissions() & OCShare.READ_PERMISSION_FLAG) != 0; + fileListing.setChecked(!readOnly); + } + } + private void setupExpirationDateMenuItem(MenuItem expirationDate, Resources res) { long expirationDateValue = publicShare.getExpirationDate(); if (expirationDateValue > 0) { @@ -375,7 +355,24 @@ private void setupExpirationDateMenuItem(MenuItem expirationDate, Resources res) private boolean optionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.action_share_link_permissions: { + case R.id.action_share_link_hide_file_listing: { + // TODO refresh share object after file listing has been set + if (capabilities.getFilesFileDrop().isTrue()) { + ((FileActivity) getActivity()).getFileOperationsHelper(). + setHideFileListingPermissionsToShare( + publicShare, + shareByLinkAllowEditing.isChecked() + ); + } else { + // not supported in ownCloud + Snackbar.make(getView(), R.string.files_drop_not_supported, Snackbar.LENGTH_LONG) + .setAction(R.string.learn_more, v -> { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(getString(R.string.url_server_install))); + startActivity(i); + }) + .show(); + } return true; } case R.id.action_share_link_password: { @@ -384,7 +381,6 @@ private boolean optionsItemSelected(MenuItem item) { return true; } case R.id.action_share_link_expiration_date: { - // TODO extend date picker to allow for clearing/unsetting a date // TODO refresh share object after new exp. date has been set ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment.newInstance(file, -1); dialog.show( @@ -400,14 +396,14 @@ private boolean optionsItemSelected(MenuItem item) { @Override public void unshareWith(OCShare share) { - ((FileActivity)getActivity()).getFileOperationsHelper() + ((FileActivity) getActivity()).getFileOperationsHelper() .unshareFileWithUserOrGroup(file, share.getShareType(), share.getShareWith()); } @Override public int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, - boolean canEditCreate, boolean canEditChange, - boolean canEditDelete) { + boolean canEditCreate, boolean canEditChange, + boolean canEditDelete) { SharePermissionsBuilder spb = new SharePermissionsBuilder(); spb.setSharePermission(canReshare); if (file.isFolder()) { @@ -438,12 +434,12 @@ public int updatePermissionsToShare(OCShare share, boolean canReshare, boolean c */ public void requestPasswordForShareViaLink(boolean createShare) { SharePasswordDialogFragment dialog = SharePasswordDialogFragment.newInstance(file, createShare); - dialog.show(getFragmentManager(), SharePasswordDialogFragment.PASSWORD_FRAGMENT); + dialog.show(getChildFragmentManager(), SharePasswordDialogFragment.PASSWORD_FRAGMENT); } /** * Get known server capabilities from DB - *

+ * * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} * instance ready to use. If not ready, does nothing. */ @@ -455,9 +451,9 @@ public void refreshCapabilitiesFromDB() { /** * Get public link from the DB to fill in the "Share link" section in the UI. - * + * * Takes into account server capabilities before reading database. - * + * * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} * instance ready to use. If not ready, does nothing. */ @@ -497,8 +493,7 @@ private void updatePublicShareSection() { * @return 'True' when public share is disabled in the server. */ private boolean isPublicShareDisabled() { - return (capabilities != null && capabilities.getFilesSharingPublicEnabled().isFalse() - ); + return (capabilities != null && capabilities.getFilesSharingPublicEnabled().isFalse()); } /** diff --git a/src/main/res/menu/file_detail_sharing_link_menu.xml b/src/main/res/menu/file_detail_sharing_link_menu.xml index 3358028c87c6..e83de2dbf60d 100644 --- a/src/main/res/menu/file_detail_sharing_link_menu.xml +++ b/src/main/res/menu/file_detail_sharing_link_menu.xml @@ -23,9 +23,10 @@ tools:ignore="AppCompatResource"> Date: Sun, 20 May 2018 18:24:19 +0200 Subject: [PATCH 27/75] fix flag detection --- .../owncloud/android/ui/fragment/FileDetailSharingFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 5c709b8ddc5e..6f1e84df0210 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -361,7 +361,7 @@ private boolean optionsItemSelected(MenuItem item) { ((FileActivity) getActivity()).getFileOperationsHelper(). setHideFileListingPermissionsToShare( publicShare, - shareByLinkAllowEditing.isChecked() + item.isChecked() ); } else { // not supported in ownCloud From 8397a48ddee1c8f2fc4edf827152d99cf46b6821 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 18:27:39 +0200 Subject: [PATCH 28/75] hide file listing option if share is not editable --- .../ui/fragment/FileDetailSharingFragment.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 6f1e84df0210..43a730b06bbb 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -329,8 +329,12 @@ private void setupHideFileListingMenuItem(MenuItem fileListing) { if (!file.isFolder()) { fileListing.setVisible(false); } else { - boolean readOnly = (publicShare.getPermissions() & OCShare.READ_PERMISSION_FLAG) != 0; - fileListing.setChecked(!readOnly); + if(shareByLinkAllowEditing.isChecked()) { + boolean readOnly = (publicShare.getPermissions() & OCShare.READ_PERMISSION_FLAG) != 0; + fileListing.setChecked(!readOnly); + } else { + fileListing.setVisible(false); + } } } @@ -359,10 +363,7 @@ private boolean optionsItemSelected(MenuItem item) { // TODO refresh share object after file listing has been set if (capabilities.getFilesFileDrop().isTrue()) { ((FileActivity) getActivity()).getFileOperationsHelper(). - setHideFileListingPermissionsToShare( - publicShare, - item.isChecked() - ); + setHideFileListingPermissionsToShare(publicShare, item.isChecked()); } else { // not supported in ownCloud Snackbar.make(getView(), R.string.files_drop_not_supported, Snackbar.LENGTH_LONG) From 7800d33ff5e018ec8ae26b6e79c6d5481e17ecf6 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 18:51:33 +0200 Subject: [PATCH 29/75] move stuff to helper class --- .../fragment/FileDetailSharingFragment.java | 148 ++++------------- .../util/FileDetailSharingFragmentHelper.java | 152 ++++++++++++++++++ 2 files changed, 179 insertions(+), 121 deletions(-) create mode 100644 src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 43a730b06bbb..31392f512676 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -57,6 +57,7 @@ import com.owncloud.android.ui.adapter.UserListAdapter; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; +import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -157,34 +158,11 @@ public void onAttach(Context context) { private void setupView() { setShareByLinkInfo(file.isSharedViaLink()); setShareWithUserInfo(); - setupSearchView(); - } - - private void setupSearchView() { - SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); - // assumes parent activity is the searchable activity - searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); - - // do not iconify the widget; expand it by default - searchView.setIconifiedByDefault(false); - - // avoid fullscreen with softkeyboard - searchView.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); - - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - Log_OC.v(TAG, "onQueryTextSubmit intercepted, query: " + query); - // return true to prevent the query is processed to be queried; - return true; - } - - @Override - public boolean onQueryTextChange(String newText) { - // leave it for the parent listener in the hierarchy / default behaviour - return false; - } - }); + FileDetailSharingFragmentHelper.setupSearchView( + (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE), + searchView, + getActivity().getComponentName() + ); } /** @@ -231,38 +209,13 @@ private void updateListOfUserGroups() { usersList.setVisibility(View.VISIBLE); usersList.setAdapter(mUserGroupsAdapter); noList.setVisibility(View.GONE); - setListViewHeightBasedOnChildren(usersList); + FileDetailSharingFragmentHelper.setListViewHeightBasedOnChildren(usersList); } else { usersList.setVisibility(View.GONE); noList.setVisibility(View.VISIBLE); } } - /** - * Fix scroll in ListView when the parent is a ScrollView - */ - private static void setListViewHeightBasedOnChildren(ListView listView) { - ListAdapter listAdapter = listView.getAdapter(); - if (listAdapter == null) { - return; - } - int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.AT_MOST); - int totalHeight = 0; - View view = null; - for (int i = 0; i < listAdapter.getCount(); i++) { - view = listAdapter.getView(i, view, listView); - if (i == 0) { - view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); - } - view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED); - totalHeight += view.getMeasuredHeight(); - } - ViewGroup.LayoutParams params = listView.getLayoutParams(); - params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); - listView.setLayoutParams(params); - listView.requestLayout(); - } - @OnClick(R.id.share_by_link) public void toggleShareByLink() { if (shareByLink.isChecked()) { @@ -306,55 +259,22 @@ public void showLinkOverflowMenu() { private void prepareOptionsMenu(Menu menu) { Resources res = getResources(); - setupHideFileListingMenuItem(menu.findItem(R.id.action_share_link_hide_file_listing)); - setupPasswordMenuItem(menu.findItem(R.id.action_share_link_password), res); - setupExpirationDateMenuItem(menu.findItem(R.id.action_share_link_expiration_date), res); - } - - private void setupPasswordMenuItem(MenuItem password, Resources res) { - if (publicShare.isPasswordProtected()) { - password.setTitle(res.getString( - R.string.share_via_link_menu_password_label, - res.getString(R.string.share_via_link_password_title) - )); - } else { - password.setTitle(res.getString( - R.string.share_via_link_menu_password_label, - res.getString(R.string.share_via_link_no_password_title) - )); - } - } - - private void setupHideFileListingMenuItem(MenuItem fileListing) { - if (!file.isFolder()) { - fileListing.setVisible(false); - } else { - if(shareByLinkAllowEditing.isChecked()) { - boolean readOnly = (publicShare.getPermissions() & OCShare.READ_PERMISSION_FLAG) != 0; - fileListing.setChecked(!readOnly); - } else { - fileListing.setVisible(false); - } - } - } - - private void setupExpirationDateMenuItem(MenuItem expirationDate, Resources res) { - long expirationDateValue = publicShare.getExpirationDate(); - if (expirationDateValue > 0) { - String formattedDate = - SimpleDateFormat.getDateInstance().format( - new Date(expirationDateValue) - ); - expirationDate.setTitle(res.getString( - R.string.share_via_link_menu_expiration_date_label, - formattedDate - )); - } else { - expirationDate.setTitle(res.getString( - R.string.share_via_link_menu_expiration_date_label, - res.getString(R.string.share_via_link_menu_expiration_date_never) - )); - } + FileDetailSharingFragmentHelper.setupHideFileListingMenuItem( + menu.findItem(R.id.action_share_link_hide_file_listing), + file.isFolder(), + shareByLinkAllowEditing.isChecked(), + publicShare.getPermissions() + ); + FileDetailSharingFragmentHelper.setupPasswordMenuItem( + menu.findItem(R.id.action_share_link_password), + publicShare.isPasswordProtected(), + res + ); + FileDetailSharingFragmentHelper.setupExpirationDateMenuItem( + menu.findItem(R.id.action_share_link_expiration_date), + publicShare.getExpirationDate(), + res + ); } private boolean optionsItemSelected(MenuItem item) { @@ -459,8 +379,8 @@ public void refreshCapabilitiesFromDB() { * instance ready to use. If not ready, does nothing. */ public void refreshPublicShareFromDB() { - if (isPublicShareDisabled()) { - hidePublicShare(); + if (FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities)) { + shareByLinkContainer.setVisibility(View.GONE); } else if (((FileActivity) getActivity()).getStorageManager() != null) { // Get public share publicShare = ((FileActivity) getActivity()).getStorageManager().getFirstShareByPathAndType( @@ -475,8 +395,8 @@ public void refreshPublicShareFromDB() { } /** - * Updates in the UI the section about public share with the information in the current public share bound to - * mFile, if any. + * Updates in the UI the section about public share with the information + * in the current public share bound to, if any. */ private void updatePublicShareSection() { if (publicShare != null && ShareType.PUBLIC_LINK.equals(publicShare.getShareType())) { @@ -489,18 +409,4 @@ private void updatePublicShareSection() { } } } - - /** - * @return 'True' when public share is disabled in the server. - */ - private boolean isPublicShareDisabled() { - return (capabilities != null && capabilities.getFilesSharingPublicEnabled().isFalse()); - } - - /** - * Hides all the UI elements related to public share. - */ - private void hidePublicShare() { - shareByLinkContainer.setVisibility(View.GONE); - } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java new file mode 100644 index 000000000000..7d4e381f03c8 --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -0,0 +1,152 @@ +/* + * Nextcloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2018 Andy Scherzinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + */ + +package com.owncloud.android.ui.fragment.util; + +import android.app.SearchManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.res.Resources; +import android.support.v7.widget.SearchView; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.ListAdapter; +import android.widget.ListView; + +import com.owncloud.android.R; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.lib.resources.status.OCCapability; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Helper calls for visibility logic of the sharing fragment. + */ +public class FileDetailSharingFragmentHelper { + + public static void setupHideFileListingMenuItem(MenuItem fileListing, + boolean isFolder, + boolean isEditingAllowed, + int publicSharePermissions) { + if (!isFolder) { + fileListing.setVisible(false); + } else { + if (isEditingAllowed) { + boolean readOnly = (publicSharePermissions & OCShare.READ_PERMISSION_FLAG) != 0; + fileListing.setChecked(!readOnly); + } else { + fileListing.setVisible(false); + } + } + } + + public static void setupPasswordMenuItem(MenuItem password, boolean isPasswordProtected, Resources res) { + if (isPasswordProtected) { + password.setTitle(res.getString( + R.string.share_via_link_menu_password_label, + res.getString(R.string.share_via_link_password_title) + )); + } else { + password.setTitle(res.getString( + R.string.share_via_link_menu_password_label, + res.getString(R.string.share_via_link_no_password_title) + )); + } + } + + public static void setupExpirationDateMenuItem(MenuItem expirationDate, long expirationDateValue, Resources res) { + if (expirationDateValue > 0) { + String formattedDate = + SimpleDateFormat.getDateInstance().format( + new Date(expirationDateValue) + ); + expirationDate.setTitle(res.getString( + R.string.share_via_link_menu_expiration_date_label, + formattedDate + )); + } else { + expirationDate.setTitle(res.getString( + R.string.share_via_link_menu_expiration_date_label, + res.getString(R.string.share_via_link_menu_expiration_date_never) + )); + } + } + + public static void setupSearchView(SearchManager searchManager, SearchView searchView, ComponentName componentName) { + // assumes parent activity is the searchable activity + searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName)); + + // do not iconify the widget; expand it by default + searchView.setIconifiedByDefault(false); + + // avoid fullscreen with softkeyboard + searchView.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + // return true to prevent the query is processed to be queried; + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + // leave it for the parent listener in the hierarchy / default behaviour + return false; + } + }); + } + + /** + * @return 'True' when public share is disabled in the server. + */ + public static boolean isPublicShareDisabled(OCCapability capabilities) { + return (capabilities != null && capabilities.getFilesSharingPublicEnabled().isFalse()); + } + + /** + * Fix scroll in ListView when the parent is a ScrollView + */ + public static void setListViewHeightBasedOnChildren(ListView listView) { + ListAdapter listAdapter = listView.getAdapter(); + if (listAdapter == null) { + return; + } + int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.AT_MOST); + int totalHeight = 0; + View view = null; + for (int i = 0; i < listAdapter.getCount(); i++) { + view = listAdapter.getView(i, view, listView); + if (i == 0) { + view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED); + totalHeight += view.getMeasuredHeight(); + } + ViewGroup.LayoutParams params = listView.getLayoutParams(); + params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); + listView.setLayoutParams(params); + listView.requestLayout(); + } +} From 1a32758e271da878c37197bcd0c4fe40318dec5e Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 19:31:55 +0200 Subject: [PATCH 30/75] implement changes sharing data / fixed unsetting of exp. date --- .../ui/activity/FileDisplayActivity.java | 16 ++++++ .../ExpirationDatePickerDialogFragment.java | 2 +- .../fragment/FileDetailSharingFragment.java | 49 +++++++++++++++++-- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index a4380dc2a856..0e76cc81eb79 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -93,6 +93,8 @@ import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.operations.UpdateSharePermissionsOperation; +import com.owncloud.android.operations.UpdateShareViaLinkOperation; import com.owncloud.android.operations.UploadFileOperation; import com.owncloud.android.providers.UsersAndGroupsSearchProvider; import com.owncloud.android.syncadapter.FileSyncAdapter; @@ -1698,6 +1700,9 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); } else if (operation instanceof CreateShareWithShareeOperation) { onCreateShareWithShareeOperationFinish(result); + } else if (operation instanceof UpdateSharePermissionsOperation + || operation instanceof UpdateShareViaLinkOperation) { + onUpdateSharePermissionsFinished(result); } } @@ -1887,6 +1892,17 @@ private void onCreateShareWithShareeOperationFinish(RemoteOperationResult result } } + private void onUpdateSharePermissionsFinished(RemoteOperationResult result) { + Fragment fileDetailFragment = getSecondFragment(); + + if (result.isSuccess()) { + if (fileDetailFragment instanceof FileDetailFragment) { + ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment() + .onUpdateSharePermissionsFinished(result); + } + } + } + /** * Shortcut to get access to the {@link FileDetailFragment} instance, if any * diff --git a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java index a6e88c8489ce..35d131565197 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java @@ -105,7 +105,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { getText(R.string.share_via_link_unset_password), (dialog1, which) -> { ((FileActivity) getActivity()).getFileOperationsHelper() - .setExpirationDateToShareViaLink(file, 0); + .setExpirationDateToShareViaLink(file, -1); }); // Prevent days in the past may be chosen diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 31392f512676..c7f2787e355c 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -30,7 +30,9 @@ import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; +import android.support.v7.widget.AppCompatCheckBox; import android.support.v7.widget.SearchView; +import android.support.v7.widget.SwitchCompat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -46,18 +48,22 @@ import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OCCapability; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.UserListAdapter; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; +import com.owncloud.android.utils.ThemeUtils; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -177,7 +183,11 @@ private void setShareByLinkInfo(boolean isShareByLink) { private void setLinkDetailVisible(boolean visible) { if (visible) { - shareByLinkAllowEditing.setVisibility(View.VISIBLE); + if (file.isFolder()) { + shareByLinkAllowEditing.setVisibility(View.VISIBLE); + } else { + shareByLinkAllowEditing.setVisibility(View.INVISIBLE); + } overflowMenuShareLink.setVisibility(View.VISIBLE); } else { shareByLinkAllowEditing.setVisibility(View.INVISIBLE); @@ -244,7 +254,8 @@ public void showSendLinkTo() { @OnClick(R.id.share_by_link_allow_editing) public void toggleShareLinkAllowEditing() { if (file.isSharedViaLink()) { - ((FileActivity) getActivity()).getFileOperationsHelper().setUploadPermissionsToShare(file, shareByLinkAllowEditing.isChecked()); + ((FileActivity) getActivity()).getFileOperationsHelper() + .setUploadPermissionsToShare(file, shareByLinkAllowEditing.isChecked()); } } @@ -280,7 +291,7 @@ private void prepareOptionsMenu(Menu menu) { private boolean optionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_share_link_hide_file_listing: { - // TODO refresh share object after file listing has been set + item.setChecked(!item.isChecked()); if (capabilities.getFilesFileDrop().isTrue()) { ((FileActivity) getActivity()).getFileOperationsHelper(). setHideFileListingPermissionsToShare(publicShare, item.isChecked()); @@ -297,12 +308,10 @@ private boolean optionsItemSelected(MenuItem item) { return true; } case R.id.action_share_link_password: { - // TODO refresh share object after password has been set requestPasswordForShareViaLink(false); return true; } case R.id.action_share_link_expiration_date: { - // TODO refresh share object after new exp. date has been set ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment.newInstance(file, -1); dialog.show( getActivity().getSupportFragmentManager(), @@ -315,6 +324,36 @@ private boolean optionsItemSelected(MenuItem item) { } } + /** + * Updates the UI after the result of an update operation on the edited {@link OCShare} permissions. + * + * @param result Result of an update on the edited {@link OCShare} permissions. + */ + public void onUpdateSharePermissionsFinished(RemoteOperationResult result) { + if (result.isSuccess()) { + refreshUiFromDB(); + } else { + setupView(); + } + } + + /** + * Get {@link OCShare} instance from DB and updates the UI. + * + * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} + * instance ready to use. If not ready, does nothing. + */ + private void refreshUiFromDB() { + FileDataStorageManager storageManager = ((FileActivity) getActivity()).getStorageManager(); + if (storageManager != null) { + // Get edited share + publicShare = storageManager.getShareById(publicShare.getId()); + + // Updates UI with new state + setupView(); + } + } + @Override public void unshareWith(OCShare share) { ((FileActivity) getActivity()).getFileOperationsHelper() From 7f85bffa9399f7fd68ae6775ddb8c00781cbbb88 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 19:34:34 +0200 Subject: [PATCH 31/75] fix codacy + revert to handling name-to-color exceptions --- .../com/owncloud/android/ui/TextDrawable.java | 11 ++++++--- .../ui/activity/FileDisplayActivity.java | 8 +++---- .../ui/adapter/ShareUserListAdapter.java | 23 +++++++++++++++---- .../android/ui/adapter/UserListAdapter.java | 14 +++++++++-- .../fragment/FileDetailSharingFragment.java | 11 --------- .../util/FileDetailSharingFragmentHelper.java | 2 -- 6 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/TextDrawable.java b/src/main/java/com/owncloud/android/ui/TextDrawable.java index b488783ec198..2ba8c563e298 100644 --- a/src/main/java/com/owncloud/android/ui/TextDrawable.java +++ b/src/main/java/com/owncloud/android/ui/TextDrawable.java @@ -97,7 +97,8 @@ public TextDrawable(String text, int r, int g, int b, float radius) { */ @NonNull @NextcloudServer(max = 12) - public static TextDrawable createAvatar(String accountName, float radiusInDp) { + public static TextDrawable createAvatar(String accountName, float radiusInDp) throws + UnsupportedEncodingException, NoSuchAlgorithmException { String username = AccountUtils.getAccountUsername(accountName); return createNamedAvatar(username, radiusInDp); } @@ -114,7 +115,8 @@ public static TextDrawable createAvatar(String accountName, float radiusInDp) { */ @NonNull @NextcloudServer(max = 12) - public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) { + public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) throws + UnsupportedEncodingException, NoSuchAlgorithmException { return createNamedAvatar(userId, radiusInDp); } @@ -125,9 +127,12 @@ public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) * @param name the name * @param radiusInDp the circle's radius * @return the avatar as a TextDrawable + * @throws UnsupportedEncodingException if the charset is not supported when calculating the color values + * @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values */ @NonNull - public static TextDrawable createNamedAvatar(String name, float radiusInDp) { + public static TextDrawable createNamedAvatar(String name, float radiusInDp) throws + UnsupportedEncodingException, NoSuchAlgorithmException { int[] hsl = BitmapUtils.calculateHSL(name); int[] rgb = BitmapUtils.HSLtoRGB(hsl[0], hsl[1], hsl[2], 1); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 0e76cc81eb79..7555449d22e6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1895,11 +1895,9 @@ private void onCreateShareWithShareeOperationFinish(RemoteOperationResult result private void onUpdateSharePermissionsFinished(RemoteOperationResult result) { Fragment fileDetailFragment = getSecondFragment(); - if (result.isSuccess()) { - if (fileDetailFragment instanceof FileDetailFragment) { - ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment() - .onUpdateSharePermissionsFinished(result); - } + if (result.isSuccess() && fileDetailFragment instanceof FileDetailFragment) { + ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment() + .onUpdateSharePermissionsFinished(result); } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java index 125a0c4abbe1..f8c24fe18565 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java @@ -1,4 +1,4 @@ -/** +/* * ownCloud Android client application * * @author masensio @@ -34,6 +34,8 @@ import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.ui.TextDrawable; +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** @@ -91,12 +93,25 @@ public View getView(final int position, View convertView, @NonNull ViewGroup par String name = share.getSharedWithDisplayName(); if (share.getShareType() == ShareType.GROUP) { name = getContext().getString(R.string.share_group_clarification, name); - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + try { + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { + icon.setImageResource(R.drawable.ic_group); + } } else if (share.getShareType() == ShareType.EMAIL) { name = getContext().getString(R.string.share_email_clarification, name); - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + try { + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { + icon.setImageResource(R.drawable.ic_email); + } } else { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + try { + + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); + } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { + icon.setImageResource(R.drawable.ic_user); + } } userName.setText(name); diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index c2bd3aa79ff1..812b22057335 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -45,6 +45,8 @@ import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.utils.DisplayUtils; +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** @@ -109,10 +111,18 @@ View getView(final int position, View convertView, @NonNull ViewGroup parent) { String name = share.getSharedWithDisplayName(); if (share.getShareType() == ShareType.GROUP) { name = getContext().getString(R.string.share_group_clarification, name); - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); + try { + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); + } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { + icon.setImageResource(R.drawable.ic_group); + } } else if (share.getShareType() == ShareType.EMAIL) { name = getContext().getString(R.string.share_email_clarification, name); - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); + try { + icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); + } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { + icon.setImageResource(R.drawable.ic_email); + } } else { icon.setTag(share.getShareWith()); DisplayUtils.setAvatar(account, share.getShareWith(), this, avatarRadiusDimension, diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index c7f2787e355c..814a99b89360 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -30,44 +30,34 @@ import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; -import android.support.v7.widget.AppCompatCheckBox; import android.support.v7.widget.SearchView; -import android.support.v7.widget.SwitchCompat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListAdapter; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.TextView; import com.owncloud.android.R; -import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OCCapability; -import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.UserListAdapter; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; -import com.owncloud.android.utils.ThemeUtils; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import butterknife.BindView; import butterknife.ButterKnife; @@ -75,7 +65,6 @@ import butterknife.Unbinder; public class FileDetailSharingFragment extends Fragment implements UserListAdapter.ShareeListAdapterListener { - private static final String TAG = FileDetailSharingFragment.class.getSimpleName(); private static final String ARG_FILE = "FILE"; private static final String ARG_ACCOUNT = "ACCOUNT"; diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java index 7d4e381f03c8..f10dd8363802 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -22,7 +22,6 @@ import android.app.SearchManager; import android.content.ComponentName; -import android.content.Context; import android.content.res.Resources; import android.support.v7.widget.SearchView; import android.view.MenuItem; @@ -33,7 +32,6 @@ import android.widget.ListView; import com.owncloud.android.R; -import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.status.OCCapability; From 5f64479d30466d02bfc84882721cc8bf6c90315f Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 20:50:51 +0200 Subject: [PATCH 32/75] add the ability to set expiration date on shares not just links --- .../android/ui/adapter/UserListAdapter.java | 19 ++++++++- .../ExpirationDatePickerDialogFragment.java | 41 +++++++++++++++++-- .../fragment/FileDetailSharingFragment.java | 2 +- .../util/FileDetailSharingFragmentHelper.java | 4 +- .../ui/helpers/FileOperationsHelper.java | 19 +++++++++ .../menu/file_detail_sharing_link_menu.xml | 2 +- .../res/menu/file_detail_sharing_menu.xml | 5 +++ src/main/res/values/strings.xml | 2 +- 8 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 812b22057335..06ca8392092c 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -21,9 +21,11 @@ package com.owncloud.android.ui.adapter; import android.accounts.Account; +import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; +import android.support.v4.app.FragmentManager; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -43,6 +45,8 @@ import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.TextDrawable; +import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; +import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; import com.owncloud.android.utils.DisplayUtils; import java.io.UnsupportedEncodingException; @@ -56,6 +60,7 @@ public class UserListAdapter extends ArrayAdapter implements DisplayUtils.Avatar private ShareeListAdapterListener listener; private OCCapability capabilities; + private FragmentManager fragmentManager; private Context context; private ArrayList shares; private float avatarRadiusDimension; @@ -63,10 +68,11 @@ public class UserListAdapter extends ArrayAdapter implements DisplayUtils.Avatar private OCFile file; private FileDataStorageManager storageManager; - public UserListAdapter(Context context, int resource, ArrayList shares, + public UserListAdapter(FragmentManager fragmentManager, Context context, int resource, ArrayList shares, Account account, OCFile file, ShareeListAdapterListener listener) { super(context, resource); this.context = context; + this.fragmentManager = fragmentManager; this.shares = shares; this.listener = listener; this.account = account; @@ -185,6 +191,9 @@ private void prepareOptionsMenu(Menu menu, OCShare share) { editChangeItem.setVisible(false); editDeleteItem.setVisible(false); } + + FileDetailSharingFragmentHelper.setupExpirationDateMenuItem( + menu.findItem(R.id.action_expiration_date), share.getExpirationDate(), context.getResources()); } private boolean optionsItemSelected(Menu menu, MenuItem item, OCShare share) { @@ -220,6 +229,14 @@ private boolean optionsItemSelected(Menu menu, MenuItem item, OCShare share) { notifyDataSetChanged(); return true; } + case R.id.action_expiration_date: { + ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment.newInstance(share, -1); + dialog.show( + fragmentManager, + ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG + ); + return true; + } default: return true; } diff --git a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java index 35d131565197..54d5a678b793 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java @@ -31,6 +31,7 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.ui.activity.FileActivity; import java.util.Calendar; @@ -48,12 +49,18 @@ public class ExpirationDatePickerDialogFragment /** Parameter constant for {@link OCFile} instance to set the expiration date */ private static final String ARG_FILE = "FILE"; + /** Parameter constant for {@link OCShare} instance to set the expiration date */ + private static final String ARG_SHARE = "SHARE"; + /** Parameter constant for date chosen initially */ private static final String ARG_CHOSEN_DATE_IN_MILLIS = "CHOSEN_DATE_IN_MILLIS"; /** File to bind an expiration date */ private OCFile file; + /** Share to bind an expiration date */ + private OCShare share; + /** * Factory method to create new instances * @@ -71,6 +78,23 @@ public static ExpirationDatePickerDialogFragment newInstance(OCFile file, long c return dialog; } + /** + * Factory method to create new instances + * + * @param share share to bind an expiration date + * @param chosenDateInMillis Date chosen when the dialog appears + * @return New dialog instance + */ + public static ExpirationDatePickerDialogFragment newInstance(OCShare share, long chosenDateInMillis) { + Bundle arguments = new Bundle(); + arguments.putParcelable(ARG_SHARE, share); + arguments.putLong(ARG_CHOSEN_DATE_IN_MILLIS, chosenDateInMillis); + + ExpirationDatePickerDialogFragment dialog = new ExpirationDatePickerDialogFragment(); + dialog.setArguments(arguments); + return dialog; + } + /** * {@inheritDoc} * @@ -81,6 +105,7 @@ public static ExpirationDatePickerDialogFragment newInstance(OCFile file, long c public Dialog onCreateDialog(Bundle savedInstanceState) { // Load arguments file = getArguments().getParcelable(ARG_FILE); + share = getArguments().getParcelable(ARG_SHARE); // Chosen date received as an argument must be later than tomorrow ; default to tomorrow in other case final Calendar chosenDate = Calendar.getInstance(); @@ -104,8 +129,12 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog.BUTTON_NEUTRAL, getText(R.string.share_via_link_unset_password), (dialog1, which) -> { - ((FileActivity) getActivity()).getFileOperationsHelper() - .setExpirationDateToShareViaLink(file, -1); + if (file != null) { + ((FileActivity) getActivity()).getFileOperationsHelper() + .setExpirationDateToShareViaLink(file, -1); + } else if (share != null) { + ((FileActivity) getActivity()).getFileOperationsHelper().setExpirationDateToShare(share,-1); + } }); // Prevent days in the past may be chosen @@ -137,7 +166,11 @@ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth chosenDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); long chosenDateInMillis = chosenDate.getTimeInMillis(); - ((FileActivity) getActivity()).getFileOperationsHelper() - .setExpirationDateToShareViaLink(file, chosenDateInMillis); + if (file != null) { + ((FileActivity) getActivity()).getFileOperationsHelper() + .setExpirationDateToShareViaLink(file, chosenDateInMillis); + } else if (share != null) { + ((FileActivity) getActivity()).getFileOperationsHelper().setExpirationDateToShare(share,chosenDateInMillis); + } } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 814a99b89360..912f3fb2df1a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -201,7 +201,7 @@ public void setShareWithUserInfo() { private void updateListOfUserGroups() { // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed - UserListAdapter mUserGroupsAdapter = new UserListAdapter(getActivity().getApplicationContext(), + UserListAdapter mUserGroupsAdapter = new UserListAdapter(getActivity().getSupportFragmentManager(),getActivity().getApplicationContext(), R.layout.share_user_item, shares, account, file, this); if (shares.size() > 0) { diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java index f10dd8363802..c53188214ba1 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -80,12 +80,12 @@ public static void setupExpirationDateMenuItem(MenuItem expirationDate, long exp new Date(expirationDateValue) ); expirationDate.setTitle(res.getString( - R.string.share_via_link_menu_expiration_date_label, + R.string.share_expiration_date_label, formattedDate )); } else { expirationDate.setTitle(res.getString( - R.string.share_via_link_menu_expiration_date_label, + R.string.share_expiration_date_label, res.getString(R.string.share_via_link_menu_expiration_date_never) )); } diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 65217d76107c..34f0616742bc 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -537,6 +537,25 @@ public void setExpirationDateToShareViaLink(OCFile file, long expirationTimeInMi queueShareIntent(updateShareIntent); } + /** + * Updates a public share on a file to set its expiration date. + * Starts a request to do it in {@link OperationsService} + * + * @param share {@link OCShare} instance which permissions will be updated. + * @param expirationTimeInMillis Expiration date to set. A negative value clears the current expiration + * date, leaving the link unrestricted. Zero makes no change. + */ + public void setExpirationDateToShare(OCShare share, long expirationTimeInMillis) { + Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class); + updateShareIntent.setAction(OperationsService.ACTION_UPDATE_SHARE); + updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId()); + updateShareIntent.putExtra( + OperationsService.EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, + expirationTimeInMillis + ); + queueShareIntent(updateShareIntent); + } /** * Updates a share on a file to set its access permissions. diff --git a/src/main/res/menu/file_detail_sharing_link_menu.xml b/src/main/res/menu/file_detail_sharing_link_menu.xml index e83de2dbf60d..5d41d9d4ac10 100644 --- a/src/main/res/menu/file_detail_sharing_link_menu.xml +++ b/src/main/res/menu/file_detail_sharing_link_menu.xml @@ -36,7 +36,7 @@

diff --git a/src/main/res/menu/file_detail_sharing_menu.xml b/src/main/res/menu/file_detail_sharing_menu.xml index ca200e296947..1ac0287708e1 100644 --- a/src/main/res/menu/file_detail_sharing_menu.xml +++ b/src/main/res/menu/file_detail_sharing_menu.xml @@ -52,6 +52,11 @@ android:showAsAction="never" android:title="@string/share_privilege_can_share" app:showAsAction="never" /> + read-only upload-only Password protect (%1$s) - Set expiration date (%1$s) + Set expiration date (%1$s) never Share link Set expiration date From 5ea05288eb5a93baa8c1de48a09c1326b31cd110 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 21:02:44 +0200 Subject: [PATCH 33/75] cleanup + fix lint issues --- .../android/ui/adapter/UserListAdapter.java | 16 ++++++++-------- .../android/ui/helpers/FileOperationsHelper.java | 2 ++ .../com/owncloud/android/utils/BitmapUtils.java | 6 +++++- src/main/res/layout/share_file_layout.xml | 2 ++ src/main/res/values/dims.xml | 2 -- src/main/res/values/strings.xml | 8 -------- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 06ca8392092c..03ce505a7f66 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -1,27 +1,27 @@ /* * Nextcloud Android client application * + * @author masensio * @author Andy Scherzinger + * Copyright (C) 2015 ownCloud GmbH * Copyright (C) 2018 Andy Scherzinger * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. * * 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. + * GNU 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 . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.owncloud.android.ui.adapter; import android.accounts.Account; -import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 34f0616742bc..4ba387308688 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -4,7 +4,9 @@ * @author masensio * @author David A. Velasco * @author Juan Carlos González Cabrero + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, diff --git a/src/main/java/com/owncloud/android/utils/BitmapUtils.java b/src/main/java/com/owncloud/android/utils/BitmapUtils.java index 733a20ea4f87..80d8081bd109 100644 --- a/src/main/java/com/owncloud/android/utils/BitmapUtils.java +++ b/src/main/java/com/owncloud/android/utils/BitmapUtils.java @@ -34,7 +34,9 @@ import org.apache.commons.codec.binary.Hex; +import java.io.UnsupportedEncodingException; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Locale; /** @@ -267,8 +269,10 @@ private static float HueToRGB(float p, float q, float h) { * * @param name The name * @return corresponding RGB color + * @throws UnsupportedEncodingException if the charset is not supported + * @throws NoSuchAlgorithmException if the specified algorithm is not available */ - public static int[] calculateHSL(String name) { + public static int[] calculateHSL(String name) throws UnsupportedEncodingException, NoSuchAlgorithmException { // using adapted algorithm from https://github.com/nextcloud/server/blob/master/core/js/placeholder.js#L126 String[] result = new String[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}; diff --git a/src/main/res/layout/share_file_layout.xml b/src/main/res/layout/share_file_layout.xml index 08a9eb3f8031..c4ac306a6af9 100644 --- a/src/main/res/layout/share_file_layout.xml +++ b/src/main/res/layout/share_file_layout.xml @@ -169,6 +169,7 @@ android:id="@+id/shareViaLinkEditPermissionSection" android:layout_width="match_parent" android:layout_height="wrap_content" + android:visibility="gone" android:layout_marginBottom="@dimen/standard_half_margin" > @@ -202,6 +203,7 @@ android:id="@+id/shareViaLinkHideFileListingPermissionSection" android:layout_width="match_parent" android:layout_height="wrap_content" + android:visibility="gone" android:layout_marginBottom="@dimen/standard_half_margin" > diff --git a/src/main/res/values/dims.xml b/src/main/res/values/dims.xml index f2700b298106..46bcf4dd2a83 100644 --- a/src/main/res/values/dims.xml +++ b/src/main/res/values/dims.xml @@ -101,8 +101,6 @@ 72dp 72dp 72dp - 14dp - 14dp 14sp 32dp 32dp diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 8ee1378060cb..00a1922cf7fd 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -132,7 +132,6 @@ Unknown error Pending Delete - Share Send About Remove account @@ -480,10 +479,6 @@ Share with users and groups No data shared with users yet Add user or group - Permissions (%1$s) - Permissions (%1$s, %2$s) - read-only - upload-only Password protect (%1$s) Set expiration date (%1$s) never @@ -508,9 +503,6 @@ %1$s ( at %2$s ) Upgrade the server version to allow sharing between users from within their clients.\nPlease contact your admin - Share by link enabled - Not shared by link - Users and groups with access Unshare can share can edit From fe234cd9423d51a76d8b06509ee1b0e24916027f Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 20 May 2018 21:48:00 +0200 Subject: [PATCH 34/75] open file details with sharing tab active for folders --- .../activity/ContactsPreferenceActivity.java | 6 +++- .../ui/activity/FileDisplayActivity.java | 13 +++++++- .../ui/activity/FolderPickerActivity.java | 11 +++---- .../ui/fragment/FileDetailFragment.java | 31 +++++++++++++++---- .../android/ui/fragment/FileFragment.java | 8 +++++ .../ui/fragment/OCFileListFragment.java | 6 +++- .../ui/preview/PreviewImageActivity.java | 5 +++ 7 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java b/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java index e11ab3252cf7..0959ad828735 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java @@ -51,7 +51,6 @@ /** * This activity shows all settings for contact backup/restore */ - public class ContactsPreferenceActivity extends FileActivity implements FileFragment.ContainerActivity { public static final String TAG = ContactsPreferenceActivity.class.getSimpleName(); @@ -171,6 +170,11 @@ public void showDetails(OCFile file) { // not needed } + @Override + public void showDetails(OCFile file, int activeTab) { + // not needed + } + @Override public void onBrowsedDownTo(OCFile folder) { // not needed diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 7555449d22e6..b2f3fa9ab498 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1560,7 +1560,18 @@ public void onBrowsedDownTo(OCFile directory) { */ @Override public void showDetails(OCFile file) { - Fragment detailFragment = FileDetailFragment.newInstance(file, getAccount()); + showDetails(file, 0); + } + + /** + * Shows the information of the {@link OCFile} received as a + * parameter in the second fragment. + * + * @param file {@link OCFile} whose details will be shown + * @param activeTab the active tab in the details view + */ + public void showDetails(OCFile file, int activeTab) { + Fragment detailFragment = FileDetailFragment.newInstance(file, getAccount(), activeTab); setSecondFragment(detailFragment); updateFragmentsVisibility(true); updateActionBarTitleAndHomeButton(file); diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java index f7b5d7e7d2fb..4f91873e6351 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java @@ -532,17 +532,16 @@ public void onReceive(Context context, Intent intent) { } } - /** - * Shows the information of the {@link OCFile} received as a - * parameter in the second fragment. - * - * @param file {@link OCFile} whose details will be shown - */ @Override public void showDetails(OCFile file) { // not used at the moment } + @Override + public void showDetails(OCFile file, int activeTab) { + // not used at the moment + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 26e0ce4a0a3a..9fc8690e8227 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -77,6 +77,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener private static final String ARG_FILE = "FILE"; private static final String ARG_ACCOUNT = "ACCOUNT"; + private static final String ARG_ACTIVE_TAB = "TAB"; @Nullable @BindView(R.id.fdProgressBlock) View downloadProgressContainer; @@ -131,6 +132,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener public ProgressListener progressListener; private ToolbarActivity activity; + private int activeTab; /** * Public factory method to create new FileDetailFragment instances. @@ -150,6 +152,26 @@ public static FileDetailFragment newInstance(OCFile fileToDetail, Account accoun return frag; } + /** + * Public factory method to create new FileDetailFragment instances. + * + * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before). + * + * @param fileToDetail An {@link OCFile} to show in the fragment + * @param account An ownCloud account; needed to start downloads + * @param activeTab to be active tab + * @return New fragment with arguments set + */ + public static FileDetailFragment newInstance(OCFile fileToDetail, Account account, int activeTab) { + FileDetailFragment frag = new FileDetailFragment(); + Bundle args = new Bundle(); + args.putParcelable(ARG_FILE, fileToDetail); + args.putParcelable(ARG_ACCOUNT, account); + args.putInt(ARG_ACTIVE_TAB, activeTab); + frag.setArguments(args); + return frag; + } + /** * Creates an empty details fragment. * @@ -205,6 +227,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, setFile(getArguments().getParcelable(ARG_FILE)); account = getArguments().getParcelable(ARG_ACCOUNT); + activeTab = getArguments().getInt(ARG_ACTIVE_TAB, 0); if (savedInstanceState != null) { setFile(savedInstanceState.getParcelable(FileActivity.EXTRA_FILE)); @@ -229,12 +252,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat cancelButton.setOnClickListener(this); favoriteIcon.setOnClickListener(this); overflowMenu.setOnClickListener(this); - // TODO use whenever we switch to use glide for preview images - /* - if (MimeTypeUtil.isImage(getFile())) { - setHeaderImage(); - } - */ updateFileDetails(false, false); } else { @@ -285,6 +302,8 @@ public void onTabReselected(TabLayout.Tab tab) { // unused at the moment } }); + + tabLayout.getTabAt(activeTab).select(); } @Override diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java index 88404a10e842..a1cb10d488be 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java @@ -131,6 +131,14 @@ public interface ContainerActivity extends ComponentsGetter { */ void showDetails(OCFile file); + /** + * Request the parent activity to show the details of an {@link OCFile}. + * + * @param file File to show details + * @param activeTab the active tab + */ + void showDetails(OCFile file, int activeTab); + ///// TO UNIFY IN A SINGLE CALLBACK METHOD - EVENT NOTIFICATIONs -> something happened // inside the fragment, MAYBE activity is interested --> unify in notification method diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index c4484be437fc..21bf2f787a03 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -393,7 +393,11 @@ public void uploadFiles() { @Override public void onShareIconClick(OCFile file) { - mContainerActivity.getFileOperationsHelper().sendShareFile(file); + if (file.isFolder()) { + mContainerActivity.showDetails(file, 1); + } else { + mContainerActivity.getFileOperationsHelper().sendShareFile(file); + } } @Override diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java index ec122ddb10be..587efba70a67 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -312,6 +312,11 @@ public void showDetails(OCFile file) { finish(); } + @Override + public void showDetails(OCFile file, int activeTab) { + showDetails(file); + } + public void requestForDownload(OCFile file) { if (mDownloaderBinder == null) { Log_OC.d(TAG, "requestForDownload called without binder to download service"); From 431549e66e5dc13b3642d3055ec09e62dfb2ce59 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 21 May 2018 10:57:06 +0200 Subject: [PATCH 35/75] slice filter menu method --- .../android/files/FileMenuFilter.java | 221 ++++++++++-------- 1 file changed, 128 insertions(+), 93 deletions(-) diff --git a/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 67e1dbb5be26..934966d169c1 100644 --- a/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -156,97 +156,35 @@ public static void hideMenuItems(MenuItem... items) { */ private void filter(List toShow, List toHide, boolean inSingleFileFragment) { boolean synchronizing = anyFileSynchronizing(); + OCCapability capability = mComponentsGetter.getStorageManager().getCapability(mAccount.name); + boolean endToEndEncryptionEnabled = capability != null && capability.getEndToEndEncryption().isTrue(); - /// decision is taken for each possible action on a file in the menu - - // DOWNLOAD - if (mFiles.isEmpty() || containsFolder() || anyFileDown() || synchronizing) { - toHide.add(R.id.action_download_file); - - } else { - toShow.add(R.id.action_download_file); - } - - // RENAME - if (!isSingleSelection() || synchronizing || containsEncryptedFile() || containsEncryptedFolder()) { - toHide.add(R.id.action_rename_file); - - } else { - toShow.add(R.id.action_rename_file); - } - - // MOVE & COPY - if (mFiles.isEmpty() || synchronizing || containsEncryptedFile() || containsEncryptedFolder()) { - toHide.add(R.id.action_move); - toHide.add(R.id.action_copy); - } else { - toShow.add(R.id.action_move); - toShow.add(R.id.action_copy); - } - - // REMOVE - if (mFiles.isEmpty() || synchronizing || containsEncryptedFolder()) { - toHide.add(R.id.action_remove_file); - } else { - toShow.add(R.id.action_remove_file); - } - - // SELECT ALL - if (!inSingleFileFragment) { - // Show only if at least one item isn't selected. - if (mFiles.size() >= mNumberOfAllFiles || mOverflowMenu) { - toHide.add(R.id.action_select_all_action_menu); - } else { - toShow.add(R.id.action_select_all_action_menu); - } - } else { - // Always hide in single file fragments - toHide.add(R.id.action_select_all_action_menu); - } - - // DESELECT ALL - if (!inSingleFileFragment) { - // Show only if at least one item is selected. - if (mFiles.isEmpty() || mOverflowMenu) { - toHide.add(R.id.action_deselect_all_action_menu); - } else { - toShow.add(R.id.action_deselect_all_action_menu); - } - }else { - // Always hide in single file fragments - toHide.add(R.id.action_deselect_all_action_menu); - } - - // OPEN WITH (different to preview!) - if (!isSingleFile() || !anyFileDown() || synchronizing) { - toHide.add(R.id.action_open_file_with); - } else { - toShow.add(R.id.action_open_file_with); - } - - // CANCEL SYNCHRONIZATION - if (mFiles.isEmpty() || !synchronizing) { - toHide.add(R.id.action_cancel_sync); - - } else { - toShow.add(R.id.action_cancel_sync); - } - - // SYNC CONTENTS (BOTH FILE AND FOLDER) - if (mFiles.isEmpty() || (!anyFileDown() && !containsFolder()) || synchronizing) { - toHide.add(R.id.action_sync_file); - - } else { - toShow.add(R.id.action_sync_file); - } + filterDownload(toShow, toHide, synchronizing); + filterRename(toShow, toHide, synchronizing); + filterMoveCopy(toShow, toHide, synchronizing); + filterRemove(toShow, toHide, synchronizing); + filterSelectAll(toShow, toHide, inSingleFileFragment); + filterDeselectAll(toShow, toHide, inSingleFileFragment); + filterOpenWith(toShow, toHide, synchronizing); + filterCancelSync(toShow, toHide, synchronizing); + filterSync(toShow, toHide, synchronizing); + filterShareFile(toShow, toHide, capability); + filterDetails(toShow, toHide); + filterKeepAvailableOffline(toShow, toHide, synchronizing); + filterDontKeepAvalableOffline(toShow, toHide, synchronizing); + filterFavorite(toShow, toHide, synchronizing); + filterUnfavorite(toShow, toHide, synchronizing); + filterEncrypt(toShow, toHide, endToEndEncryptionEnabled); + filterUnsetEncrypted(toShow, toHide, endToEndEncryptionEnabled); + filterSetPictureAs(toShow, toHide); + } - // SHARE FILE + private void filterShareFile(List toShow, List toHide, OCCapability capability) { boolean shareViaLinkAllowed = (mContext != null && mContext.getResources().getBoolean(R.bool.share_via_link_feature)); boolean shareWithUsersAllowed = (mContext != null && mContext.getResources().getBoolean(R.bool.share_with_users_feature)); - OCCapability capability = mComponentsGetter.getStorageManager().getCapability(mAccount.name); boolean shareApiEnabled = capability != null && (capability.getFilesSharingApiEnabled().isTrue() || capability.getFilesSharingApiEnabled().isUnknown() @@ -258,60 +196,67 @@ private void filter(List toShow, List toHide, boolean inSingle } else { toShow.add(R.id.action_send_share_file); } + } - // SEE DETAILS + private void filterDetails(List toShow, List toHide) { if (!isSingleSelection()) { toHide.add(R.id.action_see_details); } else { toShow.add(R.id.action_see_details); } + } - // Kept available offline + private void filterKeepAvailableOffline(List toShow, List toHide, boolean synchronizing) { if (!allFiles() || synchronizing || allKeptAvailableOffline()) { toHide.add(R.id.action_keep_files_offline); } else { toShow.add(R.id.action_keep_files_offline); } + } - // Not kept available offline + private void filterDontKeepAvalableOffline(List toShow, List toHide, boolean synchronizing) { if (!allFiles() || synchronizing || allNotKeptAvailableOffline()) { toHide.add(R.id.action_unset_keep_files_offline); } else { toShow.add(R.id.action_unset_keep_files_offline); } + } - // Favorite + private void filterFavorite(List toShow, List toHide, boolean synchronizing) { if (mFiles.isEmpty() || synchronizing || allFavorites()) { toHide.add(R.id.action_favorite); } else { toShow.add(R.id.action_favorite); } + } - // Unfavorite + private void filterUnfavorite(List toShow, List toHide, boolean synchronizing) { if (mFiles.isEmpty() || synchronizing || allNotFavorites()) { toHide.add(R.id.action_unset_favorite); } else { toShow.add(R.id.action_unset_favorite); } + } - // Encryption - boolean endToEndEncryptionEnabled = capability != null && capability.getEndToEndEncryption().isTrue(); + private void filterEncrypt(List toShow, List toHide, boolean endToEndEncryptionEnabled) { if (mFiles.isEmpty() || !isSingleSelection() || isSingleFile() || isEncryptedFolder() || !endToEndEncryptionEnabled) { toHide.add(R.id.action_encrypted); } else { toShow.add(R.id.action_encrypted); } + } - // Un-encrypt + private void filterUnsetEncrypted(List toShow, List toHide, boolean endToEndEncryptionEnabled) { if (mFiles.isEmpty() || !isSingleSelection() || isSingleFile() || !isEncryptedFolder() || !endToEndEncryptionEnabled) { toHide.add(R.id.action_unset_encrypted); } else { toShow.add(R.id.action_unset_encrypted); } + } - // SET PICTURE AS + private void filterSetPictureAs(List toShow, List toHide) { if (isSingleImage() && !MimeTypeUtil.isSVG(mFiles.iterator().next())) { toShow.add(R.id.action_set_as_wallpaper); } else { @@ -319,6 +264,96 @@ private void filter(List toShow, List toHide, boolean inSingle } } + private void filterSync(List toShow, List toHide, boolean synchronizing) { + if (mFiles.isEmpty() || (!anyFileDown() && !containsFolder()) || synchronizing) { + toHide.add(R.id.action_sync_file); + + } else { + toShow.add(R.id.action_sync_file); + } + } + + private void filterCancelSync(List toShow, List toHide, boolean synchronizing) { + if (mFiles.isEmpty() || !synchronizing) { + toHide.add(R.id.action_cancel_sync); + + } else { + toShow.add(R.id.action_cancel_sync); + } + } + + private void filterOpenWith(List toShow, List toHide, boolean synchronizing) { + if (!isSingleFile() || !anyFileDown() || synchronizing) { + toHide.add(R.id.action_open_file_with); + } else { + toShow.add(R.id.action_open_file_with); + } + } + + private void filterDeselectAll(List toShow, List toHide, boolean inSingleFileFragment) { + if (!inSingleFileFragment) { + // Show only if at least one item is selected. + if (mFiles.isEmpty() || mOverflowMenu) { + toHide.add(R.id.action_deselect_all_action_menu); + } else { + toShow.add(R.id.action_deselect_all_action_menu); + } + }else { + // Always hide in single file fragments + toHide.add(R.id.action_deselect_all_action_menu); + } + } + + private void filterSelectAll(List toShow, List toHide, boolean inSingleFileFragment) { + if (!inSingleFileFragment) { + // Show only if at least one item isn't selected. + if (mFiles.size() >= mNumberOfAllFiles || mOverflowMenu) { + toHide.add(R.id.action_select_all_action_menu); + } else { + toShow.add(R.id.action_select_all_action_menu); + } + } else { + // Always hide in single file fragments + toHide.add(R.id.action_select_all_action_menu); + } + } + + private void filterRemove(List toShow, List toHide, boolean synchronizing) { + if (mFiles.isEmpty() || synchronizing || containsEncryptedFolder()) { + toHide.add(R.id.action_remove_file); + } else { + toShow.add(R.id.action_remove_file); + } + } + + private void filterMoveCopy(List toShow, List toHide, boolean synchronizing) { + if (mFiles.isEmpty() || synchronizing || containsEncryptedFile() || containsEncryptedFolder()) { + toHide.add(R.id.action_move); + toHide.add(R.id.action_copy); + } else { + toShow.add(R.id.action_move); + toShow.add(R.id.action_copy); + } + } + + private void filterRename(List toShow, List toHide, boolean synchronizing) { + if (!isSingleSelection() || synchronizing || containsEncryptedFile() || containsEncryptedFolder()) { + toHide.add(R.id.action_rename_file); + + } else { + toShow.add(R.id.action_rename_file); + } + } + + private void filterDownload(List toShow, List toHide, boolean synchronizing) { + if (mFiles.isEmpty() || containsFolder() || anyFileDown() || synchronizing) { + toHide.add(R.id.action_download_file); + + } else { + toShow.add(R.id.action_download_file); + } + } + private boolean anyFileSynchronizing() { boolean synchronizing = false; if (mComponentsGetter != null && !mFiles.isEmpty() && mAccount != null) { From de51ba1acacc123b98629331abdc2bed05c41444 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 21 May 2018 11:08:24 +0200 Subject: [PATCH 36/75] remove legacy code after refactoring to a helper class --- .../ui/fragment/ShareFileFragment.java | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java index c094a2d6423a..cc04ace26a89 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java @@ -4,17 +4,19 @@ * @author masensio * @author David A. Velasco * @author Juan Carlos González Cabrero + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. - *

+ * Copyright (C) 2018 Andy Scherzinger + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. - *

+ * * 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 General Public License for more details. - *

+ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -40,7 +42,6 @@ import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ScrollView; import android.widget.TextView; @@ -57,6 +58,7 @@ import com.owncloud.android.ui.adapter.ShareUserListAdapter; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; +import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; @@ -651,7 +653,7 @@ private void updateListOfUserGroups() { noShares.setVisibility(View.GONE); usersList.setVisibility(View.VISIBLE); usersList.setAdapter(mUserGroupsAdapter); - setListViewHeightBasedOnChildren(usersList); + FileDetailSharingFragmentHelper.setListViewHeightBasedOnChildren(usersList); } else { noShares.setVisibility(View.VISIBLE); usersList.setVisibility(View.GONE); @@ -908,29 +910,6 @@ private void hidePublicShare() { getHideFileListingPermissionSection().setVisibility(View.GONE); } - public static void setListViewHeightBasedOnChildren(ListView listView) { - ListAdapter listAdapter = listView.getAdapter(); - if (listAdapter == null) { - return; - } - int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.AT_MOST); - int totalHeight = 0; - View view = null; - for (int i = 0; i < listAdapter.getCount(); i++) { - view = listAdapter.getView(i, view, listView); - if (i == 0) { - view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); - } - view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED); - totalHeight += view.getMeasuredHeight(); - } - ViewGroup.LayoutParams params = listView.getLayoutParams(); - params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); - listView.setLayoutParams(params); - listView.requestLayout(); - } - - /** * Starts a dialog that requests a password to the user to protect a share link. * From 0a545dd9a669f8233a7cbb119d9a5cb2a0e79fd9 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 22 May 2018 23:24:10 +0200 Subject: [PATCH 37/75] fix NPEs in calling UI thread and within UI threads --- .../FileDetailActivitiesFragment.java | 27 +++++++++++-------- .../ui/fragment/FileDetailFragment.java | 22 +-------------- .../ui/helpers/FileOperationsHelper.java | 1 - 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index d90b5303a390..43c39f9ccc14 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -50,6 +51,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.activities.GetRemoteActivitiesOperation; +import com.owncloud.android.lib.resources.activities.models.Activity; import com.owncloud.android.lib.resources.activities.models.RichObject; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.ActivityListAdapter; @@ -210,6 +212,11 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { private void fetchAndSetData(String pageUrl) { final Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext()); final Context context = MainApp.getAppContext(); + final FragmentActivity activity = getActivity(); + + final SwipeRefreshLayout empty = swipeEmptyListRefreshLayout; + final SwipeRefreshLayout list = swipeListRefreshLayout; + Thread t = new Thread(() -> { OwnCloudAccount ocAccount; @@ -235,15 +242,15 @@ private void fetchAndSetData(String pageUrl) { final ArrayList activities = (ArrayList) data.get(0); nextPageUrl = (String) data.get(1); - getActivity().runOnUiThread(() -> { + activity.runOnUiThread(() -> { populateList(activities, ownCloudClient, pageUrl == null); if (activities.size() > 0) { - swipeEmptyListRefreshLayout.setVisibility(View.GONE); - swipeListRefreshLayout.setVisibility(View.VISIBLE); + empty.setVisibility(View.GONE); + list.setVisibility(View.VISIBLE); } else { setEmptyContent(noResultsHeadline, noResultsMessage); - swipeListRefreshLayout.setVisibility(View.GONE); - swipeEmptyListRefreshLayout.setVisibility(View.VISIBLE); + list.setVisibility(View.GONE); + empty.setVisibility(View.VISIBLE); } isLoadingActivities = false; }); @@ -255,14 +262,13 @@ private void fetchAndSetData(String pageUrl) { logMessage = noResultsMessage; } final String finalLogMessage = logMessage; - getActivity().runOnUiThread(() -> { + activity.runOnUiThread(() -> { setEmptyContent(noResultsHeadline, finalLogMessage); isLoadingActivities = false; - //setIndeterminate(isLoadingActivities); }); } - hideRefreshLayoutLoader(); + hideRefreshLayoutLoader(activity); } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { Log_OC.e(TAG, "Account not found", e); } catch (IOException e) { @@ -292,8 +298,8 @@ private void setEmptyContent(String headline, String message) { } } - private void hideRefreshLayoutLoader() { - getActivity().runOnUiThread(() -> { + private void hideRefreshLayoutLoader(FragmentActivity activity) { + activity.runOnUiThread(() -> { if (swipeListRefreshLayout != null) { swipeListRefreshLayout.setRefreshing(false); } @@ -301,7 +307,6 @@ private void hideRefreshLayoutLoader() { swipeEmptyListRefreshLayout.setRefreshing(false); } isLoadingActivities = false; - //setIndeterminate(isLoadingActivities); }); } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 9fc8690e8227..4b9db4d24efe 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -112,18 +112,6 @@ public class FileDetailFragment extends FileFragment implements OnClickListener @Nullable @BindView(R.id.pager) ViewPager viewPager; - @Nullable @BindView(R.id.empty_list_view_text) - protected TextView emptyContentMessage; - - @Nullable @BindView(R.id.empty_list_view_headline) - protected TextView emptyContentHeadline; - - @Nullable @BindView(R.id.empty_list_icon) - protected ImageView emptyContentIcon; - - @Nullable @BindView(R.id.empty_list_progress) - protected ProgressBar emptyProgressBar; - private int layout; private View view; private boolean previewLoaded; @@ -222,8 +210,7 @@ public void onActivityCreated(Bundle savedInstanceState) { } @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setFile(getArguments().getParcelable(ARG_FILE)); account = getArguments().getParcelable(ARG_ACCOUNT); @@ -254,13 +241,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat overflowMenu.setOnClickListener(this); updateFileDetails(false, false); - } else { - emptyContentMessage.setText(R.string.filedetails_select_file); - emptyContentMessage.setVisibility(View.VISIBLE); - emptyContentHeadline.setText(R.string.common_error); - emptyContentIcon.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_alert_octagon)); - emptyContentIcon.setVisibility(View.VISIBLE); - emptyProgressBar.setVisibility(View.GONE); } } diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 4ba387308688..b0622d499212 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -734,7 +734,6 @@ public void syncFile(OCFile file) { intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); mFileActivity.startService(intent); - } } From d0dbc9e36ab7a9bd5ae709f4a1d3b0377b783dbe Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 22 May 2018 23:37:03 +0200 Subject: [PATCH 38/75] remove unused import --- .../android/ui/fragment/FileDetailActivitiesFragment.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index 43c39f9ccc14..e1960c4aa70c 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -51,7 +51,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.activities.GetRemoteActivitiesOperation; -import com.owncloud.android.lib.resources.activities.models.Activity; import com.owncloud.android.lib.resources.activities.models.RichObject; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.ActivityListAdapter; From b3f980c99d6989f330dc18f8632b65f0a561d1b3 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 22 May 2018 23:47:46 +0200 Subject: [PATCH 39/75] show proper error view if activities failed to load --- .../fragment/FileDetailActivitiesFragment.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index e1960c4aa70c..8677e0bb2d36 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -235,7 +235,7 @@ private void fetchAndSetData(String pageUrl) { Log_OC.d(TAG, "BEFORE getRemoteActivitiesOperation.execute"); final RemoteOperationResult result = getRemoteNotificationOperation.execute(ownCloudClient); - + if (result.isSuccess() && result.getData() != null) { final ArrayList data = result.getData(); final ArrayList activities = (ArrayList) data.get(0); @@ -262,7 +262,7 @@ private void fetchAndSetData(String pageUrl) { } final String finalLogMessage = logMessage; activity.runOnUiThread(() -> { - setEmptyContent(noResultsHeadline, finalLogMessage); + setErrorContent(finalLogMessage); isLoadingActivities = false; }); } @@ -289,9 +289,23 @@ private void populateList(List activities, OwnCloudClient mClient, boole private void setEmptyContent(String headline, String message) { if (emptyContentContainer != null && emptyContentMessage != null) { + emptyContentIcon.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_activity_light_grey)); emptyContentHeadline.setText(headline); emptyContentMessage.setText(message); + emptyContentMessage.setVisibility(View.VISIBLE); + emptyContentProgressBar.setVisibility(View.GONE); + emptyContentIcon.setVisibility(View.VISIBLE); + } + } + + private void setErrorContent(String message) { + if (emptyContentContainer != null && emptyContentMessage != null) { + emptyContentHeadline.setText(R.string.common_error); + emptyContentIcon.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_alert_octagon)); + emptyContentMessage.setText(message); + + emptyContentMessage.setVisibility(View.VISIBLE); emptyContentProgressBar.setVisibility(View.GONE); emptyContentIcon.setVisibility(View.VISIBLE); } From dba10423698de87c862cec0615e9e5f053ea0e12 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 22 May 2018 23:52:22 +0200 Subject: [PATCH 40/75] remove unused string --- .../android/ui/fragment/FileDetailActivitiesFragment.java | 2 +- src/main/res/values/strings.xml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index 8677e0bb2d36..f20d8a01246d 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -235,7 +235,7 @@ private void fetchAndSetData(String pageUrl) { Log_OC.d(TAG, "BEFORE getRemoteActivitiesOperation.execute"); final RemoteOperationResult result = getRemoteNotificationOperation.execute(ownCloudClient); - + if (result.isSuccess() && result.getData() != null) { final ArrayList data = result.getData(); final ArrayList activities = (ArrayList) data.get(0); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 00a1922cf7fd..d9882aaa57e7 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -113,7 +113,6 @@ No uploads available Upload some content or activate auto upload. folder - Tap on a file to display additional information. Download Sync File renamed %1$s during upload From cc8914ed0f960cd4898a95923f9fb20da5e6e9e5 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 23 May 2018 10:10:34 +0200 Subject: [PATCH 41/75] type, code formatting Signed-off-by: tobiasKaminsky --- .../com/owncloud/android/files/FileMenuFilter.java | 11 +++-------- .../android/ui/adapter/ShareUserListAdapter.java | 2 -- .../ui/dialog/SharePasswordDialogFragment.java | 6 ++---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 934966d169c1..7f23d3f60a36 100644 --- a/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -98,7 +98,6 @@ public FileMenuFilter(OCFile targetFile, Account account, ComponentsGetter cg, C public void filter(Menu menu, boolean inSingleFileFragment) { if (mFiles == null || mFiles.size() <= 0) { hideAll(menu); - } else { List toShow = new ArrayList<>(); List toHide = new ArrayList<>(); @@ -171,7 +170,7 @@ private void filter(List toShow, List toHide, boolean inSingle filterShareFile(toShow, toHide, capability); filterDetails(toShow, toHide); filterKeepAvailableOffline(toShow, toHide, synchronizing); - filterDontKeepAvalableOffline(toShow, toHide, synchronizing); + filterDontKeepAvailableOffline(toShow, toHide, synchronizing); filterFavorite(toShow, toHide, synchronizing); filterUnfavorite(toShow, toHide, synchronizing); filterEncrypt(toShow, toHide, endToEndEncryptionEnabled); @@ -214,7 +213,7 @@ private void filterKeepAvailableOffline(List toShow, List toHi } } - private void filterDontKeepAvalableOffline(List toShow, List toHide, boolean synchronizing) { + private void filterDontKeepAvailableOffline(List toShow, List toHide, boolean synchronizing) { if (!allFiles() || synchronizing || allNotKeptAvailableOffline()) { toHide.add(R.id.action_unset_keep_files_offline); } else { @@ -267,7 +266,6 @@ private void filterSetPictureAs(List toShow, List toHide) { private void filterSync(List toShow, List toHide, boolean synchronizing) { if (mFiles.isEmpty() || (!anyFileDown() && !containsFolder()) || synchronizing) { toHide.add(R.id.action_sync_file); - } else { toShow.add(R.id.action_sync_file); } @@ -276,7 +274,6 @@ private void filterSync(List toShow, List toHide, boolean sync private void filterCancelSync(List toShow, List toHide, boolean synchronizing) { if (mFiles.isEmpty() || !synchronizing) { toHide.add(R.id.action_cancel_sync); - } else { toShow.add(R.id.action_cancel_sync); } @@ -298,7 +295,7 @@ private void filterDeselectAll(List toShow, List toHide, boole } else { toShow.add(R.id.action_deselect_all_action_menu); } - }else { + } else { // Always hide in single file fragments toHide.add(R.id.action_deselect_all_action_menu); } @@ -339,7 +336,6 @@ private void filterMoveCopy(List toShow, List toHide, boolean private void filterRename(List toShow, List toHide, boolean synchronizing) { if (!isSingleSelection() || synchronizing || containsEncryptedFile() || containsEncryptedFolder()) { toHide.add(R.id.action_rename_file); - } else { toShow.add(R.id.action_rename_file); } @@ -348,7 +344,6 @@ private void filterRename(List toShow, List toHide, boolean sy private void filterDownload(List toShow, List toHide, boolean synchronizing) { if (mFiles.isEmpty() || containsFolder() || anyFileDown() || synchronizing) { toHide.add(R.id.action_download_file); - } else { toShow.add(R.id.action_download_file); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java index f8c24fe18565..e637e72ef99f 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareUserListAdapter.java @@ -107,7 +107,6 @@ public View getView(final int position, View convertView, @NonNull ViewGroup par } } else { try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, mAvatarRadiusDimension)); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { icon.setImageResource(R.drawable.ic_user); @@ -120,7 +119,6 @@ public View getView(final int position, View convertView, @NonNull ViewGroup par /// bind listener to unshare unshareButton.setOnClickListener(v -> mListener.unshareButtonPressed(mShares.get(position))); - } return view; } diff --git a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java index 2a928a0717b9..55aa75fb3a79 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java @@ -60,10 +60,8 @@ public void onStart() { super.onStart(); AlertDialog alertDialog = (AlertDialog) getDialog(); - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) - .setTextColor(ThemeUtils.primaryAccentColor(getContext())); - alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) - .setTextColor(ThemeUtils.primaryAccentColor(getContext())); + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ThemeUtils.primaryAccentColor(getContext())); + alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(ThemeUtils.primaryAccentColor(getContext())); alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) .setTextColor(getResources().getColor(R.color.highlight_textColor_Warning)); } From 0e41ed78897c071e54fd8e50264ef86eff3bd13d Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 23 May 2018 13:21:45 +0200 Subject: [PATCH 42/75] Use AppCompat checkbox --- .../android/ui/fragment/FileDetailSharingFragment.java | 9 ++++++--- src/main/res/layout/file_details_sharing_fragment.xml | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 912f3fb2df1a..fd6c18748321 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -30,13 +30,13 @@ import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; +import android.support.v7.widget.AppCompatCheckBox; import android.support.v7.widget.SearchView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; @@ -56,6 +56,7 @@ import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; +import com.owncloud.android.utils.ThemeUtils; import java.util.ArrayList; @@ -89,13 +90,13 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt TextView noList; @BindView(R.id.share_by_link) - CheckBox shareByLink; + AppCompatCheckBox shareByLink; @BindView(R.id.overflow_menu_share_link) ImageView overflowMenuShareLink; @BindView(R.id.share_by_link_allow_editing) - CheckBox shareByLinkAllowEditing; + AppCompatCheckBox shareByLinkAllowEditing; @BindView(R.id.share_by_link_container) LinearLayout shareByLinkContainer; @@ -167,6 +168,8 @@ private void setupView() { */ private void setShareByLinkInfo(boolean isShareByLink) { shareByLink.setChecked(isShareByLink); + ThemeUtils.tintCheckbox(shareByLink, ThemeUtils.primaryAccentColor(getContext())); + ThemeUtils.tintCheckbox(shareByLinkAllowEditing, ThemeUtils.primaryAccentColor(getContext())); setLinkDetailVisible(isShareByLink); } diff --git a/src/main/res/layout/file_details_sharing_fragment.xml b/src/main/res/layout/file_details_sharing_fragment.xml index 5e9dc621613a..875882d13265 100644 --- a/src/main/res/layout/file_details_sharing_fragment.xml +++ b/src/main/res/layout/file_details_sharing_fragment.xml @@ -47,7 +47,7 @@ android:paddingRight="@dimen/standard_padding" android:paddingTop="@dimen/standard_half_padding"> - - Date: Thu, 24 May 2018 12:56:15 +0200 Subject: [PATCH 43/75] sort menu items according to webUI --- .../res/menu/file_details_actions_menu.xml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/res/menu/file_details_actions_menu.xml b/src/main/res/menu/file_details_actions_menu.xml index 8ed5110b01c9..6908ea453d5e 100644 --- a/src/main/res/menu/file_details_actions_menu.xml +++ b/src/main/res/menu/file_details_actions_menu.xml @@ -22,6 +22,18 @@ xmlns:tools="http://schemas.android.com/tools" tools:ignore="AppCompatResource"> + + - - Date: Thu, 24 May 2018 16:38:23 +0200 Subject: [PATCH 44/75] account switch on drawer without "singleTop" works again Signed-off-by: tobiasKaminsky --- src/main/java/com/owncloud/android/utils/DisplayUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 72e2c858b1f8..1a690248dc25 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -467,7 +467,7 @@ public static void setAvatar(@NonNull Account account, @NonNull String userId, A float avatarRadius, Resources resources, FileDataStorageManager storageManager, Object callContext, Context context) { if (callContext instanceof View) { - ((View) callContext).setContentDescription(userId); + ((View) callContext).setContentDescription(account.name); } ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); From cab655570369e02125bb74b5582ac5e58770770c Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Thu, 24 May 2018 17:20:06 +0200 Subject: [PATCH 45/75] app restarts now after account switching Signed-off-by: tobiasKaminsky --- .../java/com/owncloud/android/ui/activity/FileActivity.java | 1 + .../owncloud/android/ui/activity/FileDisplayActivity.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 4495bc201e09..d150f08e1260 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -553,6 +553,7 @@ public FileUploaderBinder getFileUploaderBinder() { public void restart() { Intent i = new Intent(this, FileDisplayActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + i.setAction(FileDisplayActivity.RESTART); startActivity(i); fetchExternalLinks(false); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index b2f3fa9ab498..fb9dfa45736d 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -144,6 +144,8 @@ public class FileDisplayActivity extends HookActivity implements FileFragment.ContainerActivity, OnEnforceableRefreshListener, SortingOrderDialogFragment.OnSortingOrderListener, SendShareDialog.SendShareDialogDownloader { + + public static final String RESTART = "RESTART"; private SyncBroadcastReceiver mSyncBroadcastReceiver; private UploadFinishReceiver mUploadFinishReceiver; @@ -500,6 +502,9 @@ protected void onNewIntent(Intent intent) { if (intent.getAction() != null && intent.getAction().equalsIgnoreCase(ACTION_DETAILS)) { setIntent(intent); setFile(intent.getParcelableExtra(EXTRA_FILE)); + } else if (RESTART.equals(intent.getAction())) { + finish(); + startActivity(intent); } else // Verify the action and get the query if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); From f2dd5ea2a6750a7a689d9ae2d8f1cf7461a78918 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 25 May 2018 17:32:39 +0200 Subject: [PATCH 46/75] proper error snackbars for sharing errors, move feed back results to the sharing fragment+refresh --- .../android/operations/UnshareOperation.java | 4 +++ .../ui/activity/FileDisplayActivity.java | 31 +++++++++---------- .../fragment/FileDetailSharingFragment.java | 22 +++++++++++-- src/main/res/values/strings.xml | 2 ++ 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/owncloud/android/operations/UnshareOperation.java b/src/main/java/com/owncloud/android/operations/UnshareOperation.java index 585ff6e86578..559191e969f2 100644 --- a/src/main/java/com/owncloud/android/operations/UnshareOperation.java +++ b/src/main/java/com/owncloud/android/operations/UnshareOperation.java @@ -109,4 +109,8 @@ private boolean existsFile(OwnCloudClient client, String remotePath){ return result.isSuccess(); } + public ShareType getShareType() { + return mShareType; + } + } diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index fb9dfa45736d..0308df625641 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -45,6 +45,7 @@ import android.os.IBinder; import android.os.Parcelable; import android.support.annotation.NonNull; +import android.support.annotation.StringRes; import android.support.design.widget.BottomNavigationView; import android.support.design.widget.Snackbar; import android.support.v4.app.DialogFragment; @@ -93,6 +94,7 @@ import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.operations.UnshareOperation; import com.owncloud.android.operations.UpdateSharePermissionsOperation; import com.owncloud.android.operations.UpdateShareViaLinkOperation; import com.owncloud.android.operations.UploadFileOperation; @@ -1715,10 +1717,12 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe } else if (operation instanceof CreateShareViaLinkOperation) { onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); } else if (operation instanceof CreateShareWithShareeOperation) { - onCreateShareWithShareeOperationFinish(result); + onUpdateShareInformation(result, R.string.sharee_add_failed); } else if (operation instanceof UpdateSharePermissionsOperation || operation instanceof UpdateShareViaLinkOperation) { - onUpdateSharePermissionsFinished(result); + onUpdateShareInformation(result, R.string.updating_share_failed); + } else if (operation instanceof UnshareOperation) { + onUpdateShareInformation(result, R.string.unsharing_failed); } } @@ -1814,6 +1818,7 @@ private void onMoveFileOperationFinish(MoveFileOperation operation, private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation, RemoteOperationResult result) { + FileDetailFragment fileDetailFragment = getShareFileFragment(); if (result.isSuccess()) { updateFileFromDB(); @@ -1869,6 +1874,7 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intentToShareLink, packagesToExclude); chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); + fileDetailFragment.getFileDetailSharingFragment().refreshPublicShareFromDB(); } else { // Detect Failure (403) --> maybe needs password String password = operation.getPassword(); @@ -1878,7 +1884,6 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope // Was tried without password, but not sure that it's optional. // Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener - FileDetailFragment fileDetailFragment = getShareFileFragment(); if (fileDetailFragment != null && fileDetailFragment.isAdded()) { // only if added to the view hierarchy!! @@ -1886,6 +1891,7 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope } } else { + fileDetailFragment.getFileDetailSharingFragment().refreshPublicShareFromDB(); Snackbar.make( findViewById(android.R.id.content), ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()), @@ -1895,25 +1901,18 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope } } - private void onCreateShareWithShareeOperationFinish(RemoteOperationResult result) { - + private void onUpdateShareInformation(RemoteOperationResult result, @StringRes int errorString) { Fragment fileDetailFragment = getSecondFragment(); if (result.isSuccess()) { - if (fileDetailFragment instanceof FileDetailFragment) { - ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment().setShareWithUserInfo(); - } - } else { - Snackbar.make(fileDetailFragment.getView(), R.string.sharee_add_failed, Snackbar.LENGTH_LONG).show(); + updateFileFromDB(); + } else if (fileDetailFragment.getView() != null){ + Snackbar.make(fileDetailFragment.getView(), errorString, Snackbar.LENGTH_LONG).show(); } - } - - private void onUpdateSharePermissionsFinished(RemoteOperationResult result) { - Fragment fileDetailFragment = getSecondFragment(); - if (result.isSuccess() && fileDetailFragment instanceof FileDetailFragment) { + if (fileDetailFragment!=null && fileDetailFragment instanceof FileDetailFragment) { ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment() - .onUpdateSharePermissionsFinished(result); + .onUpdateShareInformations(result, getFile()); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index fd6c18748321..6989ba7ff71f 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -166,7 +166,7 @@ private void setupView() { * * @param isShareByLink flag is share by link is enable */ - private void setShareByLinkInfo(boolean isShareByLink) { + public void setShareByLinkInfo(boolean isShareByLink) { shareByLink.setChecked(isShareByLink); ThemeUtils.tintCheckbox(shareByLink, ThemeUtils.primaryAccentColor(getContext())); ThemeUtils.tintCheckbox(shareByLinkAllowEditing, ThemeUtils.primaryAccentColor(getContext())); @@ -329,6 +329,16 @@ public void onUpdateSharePermissionsFinished(RemoteOperationResult result) { } } + public void onUpdateShareInformations(RemoteOperationResult result, OCFile file) { + this.file = file; + + if (result.isSuccess()) { + refreshUiFromDB(); + } else { + setupView(); + } + } + /** * Get {@link OCShare} instance from DB and updates the UI. * @@ -338,8 +348,10 @@ public void onUpdateSharePermissionsFinished(RemoteOperationResult result) { private void refreshUiFromDB() { FileDataStorageManager storageManager = ((FileActivity) getActivity()).getStorageManager(); if (storageManager != null) { - // Get edited share - publicShare = storageManager.getShareById(publicShare.getId()); + if (publicShare != null) { + // Get edited shared by link + publicShare = storageManager.getShareById(publicShare.getId()); + } // Updates UI with new state setupView(); @@ -438,6 +450,10 @@ private void updatePublicShareSection() { } else { shareByLinkAllowEditing.setChecked(false); } + + setShareByLinkInfo(true); + } else { + setShareByLinkInfo(false); } } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index d9882aaa57e7..bb5efea246f2 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -788,4 +788,6 @@ Virus detected. Upload cannot be completed! Tags Adding sharee failed + Unsharing failed + Updateing share failed From d08a944b10f35a9e543bf1e965ff0c8db7d34055 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 25 May 2018 17:51:07 +0200 Subject: [PATCH 47/75] hide sync for folders --- .../com/owncloud/android/ui/fragment/FileDetailFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 4b9db4d24efe..df251cedb63c 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -354,6 +354,7 @@ private void prepareOptionsMenu(Menu menu) { if (getFile().isFolder()) { FileMenuFilter.hideMenuItems(menu.findItem(R.id.action_send_file)); + FileMenuFilter.hideMenuItems(menu.findItem(R.id.action_sync_file)); } // dual pane restrictions From bb7c090d6e13abebf30848de9d8f0337ae85f373 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 25 May 2018 17:51:22 +0200 Subject: [PATCH 48/75] remove unused legacy method --- .../android/ui/fragment/FileDetailSharingFragment.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 6989ba7ff71f..ebb7d658f7a3 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -321,14 +321,6 @@ private boolean optionsItemSelected(MenuItem item) { * * @param result Result of an update on the edited {@link OCShare} permissions. */ - public void onUpdateSharePermissionsFinished(RemoteOperationResult result) { - if (result.isSuccess()) { - refreshUiFromDB(); - } else { - setupView(); - } - } - public void onUpdateShareInformations(RemoteOperationResult result, OCFile file) { this.file = file; From 2d55824cf688d7781c23f89cb7a7479d4be19607 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 25 May 2018 17:53:53 +0200 Subject: [PATCH 49/75] fix name and javaDoc of updateMethod --- .../owncloud/android/ui/activity/FileDisplayActivity.java | 2 +- .../android/ui/fragment/FileDetailSharingFragment.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 0308df625641..a9a98b3ba664 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1912,7 +1912,7 @@ private void onUpdateShareInformation(RemoteOperationResult result, @StringRes i if (fileDetailFragment!=null && fileDetailFragment instanceof FileDetailFragment) { ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment() - .onUpdateShareInformations(result, getFile()); + .onUpdateShareInformation(result, getFile()); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index ebb7d658f7a3..e4e10116b281 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -317,11 +317,12 @@ private boolean optionsItemSelected(MenuItem item) { } /** - * Updates the UI after the result of an update operation on the edited {@link OCShare} permissions. + * Updates the UI after the result of an update operation on the edited {@link OCFile}. * - * @param result Result of an update on the edited {@link OCShare} permissions. + * @param result {@link RemoteOperationResult} of an update on the edited {@link OCFile} sharing information. + * @param file the edited {@link OCFile} */ - public void onUpdateShareInformations(RemoteOperationResult result, OCFile file) { + public void onUpdateShareInformation(RemoteOperationResult result, OCFile file) { this.file = file; if (result.isSuccess()) { From 42a0e28ee70d0baa66de82d7e5f0ef358d5284a1 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 25 May 2018 18:47:38 +0200 Subject: [PATCH 50/75] add ability to set expiration date for user shares --- .../UpdateSharePermissionsOperation.java | 33 ++++++++++++------- .../android/services/OperationsService.java | 11 +++++-- .../ui/activity/FileDisplayActivity.java | 7 ++-- .../ui/helpers/FileOperationsHelper.java | 4 +++ 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java b/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java index c798d6d54db6..a1f3b0a52948 100644 --- a/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java +++ b/src/main/java/com/owncloud/android/operations/UpdateSharePermissionsOperation.java @@ -1,8 +1,10 @@ -/** +/* * ownCloud Android client application * * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -15,7 +17,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package com.owncloud.android.operations; @@ -31,26 +32,26 @@ /** - * Updates an existing private share for a given file + * Updates an existing private share for a given file. */ - public class UpdateSharePermissionsOperation extends SyncOperation { private long mShareId; private int mPermissions; + private long mExpirationDateInMillis; private String mPath; /** * Constructor * - * @param shareId Private {@link OCShare} to update. Mandatory argument + * @param shareId Private {@link OCShare} to update. Mandatory argument */ public UpdateSharePermissionsOperation(long shareId) { mShareId = shareId; mPermissions = -1; + mExpirationDateInMillis = 0L; } - /** * Set permissions to update in private share. * @@ -61,6 +62,17 @@ public void setPermissions(int permissions) { mPermissions = permissions; } + /** + * Set expiration date to update private share. + * + * @param expirationDateInMillis Expiration date to set to the public link. + * A negative value clears the current expiration date. + * Zero value (start-of-epoch) results in no update done on + * the expiration date. + */ + public void setExpirationDate(long expirationDateInMillis) { + mExpirationDateInMillis = expirationDateInMillis; + } @Override protected RemoteOperationResult run(OwnCloudClient client) { @@ -69,18 +81,15 @@ protected RemoteOperationResult run(OwnCloudClient client) { if (share == null) { // TODO try to get remote share before failing? - return new RemoteOperationResult( - RemoteOperationResult.ResultCode.SHARE_NOT_FOUND - ); + return new RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND); } mPath = share.getPath(); // Update remote share with password - UpdateRemoteShareOperation updateOp = new UpdateRemoteShareOperation( - share.getRemoteId() - ); + UpdateRemoteShareOperation updateOp = new UpdateRemoteShareOperation(share.getRemoteId()); updateOp.setPermissions(mPermissions); + updateOp.setExpirationDate(mExpirationDateInMillis); RemoteOperationResult result = updateOp.execute(client); if (result.isSuccess()) { diff --git a/src/main/java/com/owncloud/android/services/OperationsService.java b/src/main/java/com/owncloud/android/services/OperationsService.java index e4fefed7562a..cbe3414a86ac 100644 --- a/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/src/main/java/com/owncloud/android/services/OperationsService.java @@ -1,7 +1,11 @@ -/** +/* * ownCloud Android client application * + * @author masensio + * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -14,7 +18,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package com.owncloud.android.services; @@ -583,8 +586,10 @@ private void nextOperation() { } else if (shareId > 0) { operation = new UpdateSharePermissionsOperation(shareId); - int permissions = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, 1); + int permissions = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, -1); ((UpdateSharePermissionsOperation)operation).setPermissions(permissions); + long expirationDateInMillis = operationIntent.getLongExtra(EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, 0L); + ((UpdateSharePermissionsOperation)operation).setExpirationDate(expirationDateInMillis); } } else if (action.equals(ACTION_CREATE_SHARE_WITH_SHAREE)) { diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index a9a98b3ba664..bc364b5a19f6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -3,8 +3,10 @@ * * @author Bartek Przybylski * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2011 Bartek Przybylski * Copyright (C) 2016 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -1718,8 +1720,9 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); } else if (operation instanceof CreateShareWithShareeOperation) { onUpdateShareInformation(result, R.string.sharee_add_failed); - } else if (operation instanceof UpdateSharePermissionsOperation - || operation instanceof UpdateShareViaLinkOperation) { + } else if (operation instanceof UpdateShareViaLinkOperation) { + onUpdateShareInformation(result, R.string.updating_share_failed); + } else if (operation instanceof UpdateSharePermissionsOperation) { onUpdateShareInformation(result, R.string.updating_share_failed); } else if (operation instanceof UnshareOperation) { onUpdateShareInformation(result, R.string.unsharing_failed); diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index b0622d499212..ef8cd713d25f 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -556,6 +556,10 @@ public void setExpirationDateToShare(OCShare share, long expirationTimeInMillis) OperationsService.EXTRA_SHARE_EXPIRATION_DATE_IN_MILLIS, expirationTimeInMillis ); + updateShareIntent.putExtra( + OperationsService.EXTRA_SHARE_PERMISSIONS, + 0 + ); queueShareIntent(updateShareIntent); } From a00b89f972361cce09d9dd654dc0b939e89ab71e Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 25 May 2018 19:02:27 +0200 Subject: [PATCH 51/75] add javaDoc comment --- .../com/owncloud/android/ui/helpers/FileOperationsHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index ef8cd713d25f..2885c335ce2b 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -85,7 +85,7 @@ import java.util.regex.Pattern; /** - * + * Helper implementation for file operations localy and remote. */ public class FileOperationsHelper { From 9e0e83872b54b23c96f2764cf134623f1d56f1c4 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 25 May 2018 19:18:06 +0200 Subject: [PATCH 52/75] update license headers --- .../java/com/owncloud/android/files/FileMenuFilter.java | 2 ++ .../owncloud/android/operations/UnshareOperation.java | 5 +++-- src/main/java/com/owncloud/android/ui/TextDrawable.java | 9 +++++---- .../ui/dialog/ExpirationDatePickerDialogFragment.java | 5 +++-- .../com/owncloud/android/ui/dialog/SendShareDialog.java | 2 ++ .../owncloud/android/ui/fragment/FileDetailFragment.java | 3 ++- .../com/owncloud/android/ui/fragment/FileFragment.java | 5 +++-- .../owncloud/android/ui/fragment/OCFileListFragment.java | 2 ++ .../android/ui/helpers/FileOperationsHelper.java | 2 +- src/main/java/com/owncloud/android/utils/ThemeUtils.java | 4 ++-- src/main/res/layout/file_details_fragment.xml | 1 + src/main/res/layout/file_details_share_user_item.xml | 2 ++ 12 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 7f23d3f60a36..b46c04f0b3bb 100644 --- a/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, diff --git a/src/main/java/com/owncloud/android/operations/UnshareOperation.java b/src/main/java/com/owncloud/android/operations/UnshareOperation.java index 559191e969f2..a30e650f8b73 100644 --- a/src/main/java/com/owncloud/android/operations/UnshareOperation.java +++ b/src/main/java/com/owncloud/android/operations/UnshareOperation.java @@ -1,8 +1,10 @@ -/** +/* * ownCloud Android client application * * @author masensio + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -15,7 +17,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package com.owncloud.android.operations; diff --git a/src/main/java/com/owncloud/android/ui/TextDrawable.java b/src/main/java/com/owncloud/android/ui/TextDrawable.java index 2ba8c563e298..0b779e2c136f 100644 --- a/src/main/java/com/owncloud/android/ui/TextDrawable.java +++ b/src/main/java/com/owncloud/android/ui/TextDrawable.java @@ -1,19 +1,20 @@ -/** +/* * ownCloud Android client application * * @author Andy Scherzinger * @author Tobias Kaminsiky * Copyright (C) 2016 ownCloud Inc. - *

+ * Copyright (C) 2018 Andy Scherzinger + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. - *

+ * * 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 General Public License for more details. - *

+ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ diff --git a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java index 54d5a678b793..884956764ccc 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java @@ -1,8 +1,10 @@ -/** +/* * ownCloud Android client application * * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -15,7 +17,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package com.owncloud.android.ui.dialog; diff --git a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java index 65ecc1882d04..6d0a8996f4ff 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java @@ -35,8 +35,10 @@ * Nextcloud Android client application * * @author Tobias Kaminsky + * @author Andy Scherzinger * Copyright (C) 2017 Tobias Kaminsky * Copyright (C) 2017 Nextcloud GmbH. + * Copyright (C) 2018 Andy Scherzinger * * 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 diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index df251cedb63c..a43c1bf8dbf1 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -3,8 +3,10 @@ * * @author Bartek Przybylski * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2011 Bartek Przybylski * Copyright (C) 2016 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -17,7 +19,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package com.owncloud.android.ui.fragment; diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java index a1cb10d488be..4eeb0d9b2d82 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java @@ -1,8 +1,10 @@ -/** +/* * ownCloud Android client application * * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2015 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -15,7 +17,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * */ package com.owncloud.android.ui.fragment; diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 21bf2f787a03..ce3ce8c4a00c 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -4,8 +4,10 @@ * @author Bartek Przybylski * @author masensio * @author David A. Velasco + * @author Andy Scherzinger * Copyright (C) 2011 Bartek Przybylski * Copyright (C) 2016 ownCloud Inc. + * Copyright (C) 2018 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 2885c335ce2b..a28671ba905f 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -85,7 +85,7 @@ import java.util.regex.Pattern; /** - * Helper implementation for file operations localy and remote. + * Helper implementation for file operations locally and remote. */ public class FileOperationsHelper { diff --git a/src/main/java/com/owncloud/android/utils/ThemeUtils.java b/src/main/java/com/owncloud/android/utils/ThemeUtils.java index 951244a9bb24..c69dcd6308c6 100644 --- a/src/main/java/com/owncloud/android/utils/ThemeUtils.java +++ b/src/main/java/com/owncloud/android/utils/ThemeUtils.java @@ -4,8 +4,8 @@ * @author Tobias Kaminsky * @author Andy Scherzinger * Copyright (C) 2017 Tobias Kaminsky - * Copyright (C) 2017 Andy Scherzinger - * Copyright (C) 2017 Nextcloud GmbH. + * Copyright (C) 2017 Nextcloud GmbH + * Copyright (C) 2018 Andy Scherzinger * * 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 diff --git a/src/main/res/layout/file_details_fragment.xml b/src/main/res/layout/file_details_fragment.xml index f41979dd410d..fc5f7c6538c4 100644 --- a/src/main/res/layout/file_details_fragment.xml +++ b/src/main/res/layout/file_details_fragment.xml @@ -4,6 +4,7 @@ Copyright (C) 2012 Bartek Przybylski Copyright (C) 2017 ownCloud GmbH. + Copyright (C) 2018 Andy Scherzinger This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2, diff --git a/src/main/res/layout/file_details_share_user_item.xml b/src/main/res/layout/file_details_share_user_item.xml index 120b8d3769bb..a4976b106c01 100644 --- a/src/main/res/layout/file_details_share_user_item.xml +++ b/src/main/res/layout/file_details_share_user_item.xml @@ -1,7 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/file_details_share_user_item.xml b/src/main/res/layout/file_details_share_user_item.xml index e108e17bfb99..9d02c4cd401d 100644 --- a/src/main/res/layout/file_details_share_user_item.xml +++ b/src/main/res/layout/file_details_share_user_item.xml @@ -23,8 +23,6 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:weightSum="1" - android:paddingTop="@dimen/standard_eigth_padding" - android:paddingBottom="@dimen/standard_eigth_padding" tools:ignore="UseCompoundDrawables"> . --> @@ -28,13 +27,17 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingTop="@dimen/standard_half_padding"> + android:paddingTop="@dimen/standard_eigth_padding"> + android:paddingTop="@dimen/standard_padding"> + android:visibility="gone" + android:divider="@drawable/divider" + android:dividerHeight="1dp"/> Date: Sun, 27 May 2018 20:05:25 +0200 Subject: [PATCH 58/75] rewrite sharee list to recycler view, show can edit in list items --- .../android/ui/adapter/UserListAdapter.java | 237 +++++++++++------- .../SimpleListItemDividerDecoration.java | 77 ++++++ .../fragment/FileDetailSharingFragment.java | 13 +- .../ui/fragment/ShareFileFragment.java | 1 - .../util/FileDetailSharingFragmentHelper.java | 29 --- .../layout/file_details_share_user_item.xml | 45 ++-- .../layout/file_details_sharing_fragment.xml | 5 +- .../res/menu/file_detail_sharing_menu.xml | 6 - 8 files changed, 264 insertions(+), 149 deletions(-) create mode 100644 src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 8260b3859277..f7b3cb3fa407 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -26,12 +26,13 @@ import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.v4.app.FragmentManager; +import android.support.v7.widget.AppCompatCheckBox; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.PopupMenu; import android.widget.TextView; @@ -43,7 +44,6 @@ import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OCCapability; -import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; @@ -52,26 +52,30 @@ import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; /** * Adapter to show a user/group/email/remote in Sharing list in file details view. */ -public class UserListAdapter extends ArrayAdapter implements DisplayUtils.AvatarGenerationListener { +public class UserListAdapter extends RecyclerView.Adapter + implements DisplayUtils.AvatarGenerationListener { private ShareeListAdapterListener listener; private OCCapability capabilities; private FragmentManager fragmentManager; private Context context; - private ArrayList shares; + private int accentColor; + private List shares; private float avatarRadiusDimension; private Account account; private OCFile file; private FileDataStorageManager storageManager; - public UserListAdapter(FragmentManager fragmentManager, Context context, int resource, ArrayList shares, - Account account, OCFile file, ShareeListAdapterListener listener) { - super(context, resource); + public UserListAdapter(FragmentManager fragmentManager, Context context, List shares, Account account, + OCFile file, ShareeListAdapterListener listener) { this.context = context; this.fragmentManager = fragmentManager; this.shares = shares; @@ -79,71 +83,88 @@ public UserListAdapter(FragmentManager fragmentManager, Context context, int res this.account = account; this.file = file; - storageManager = new FileDataStorageManager(account, getContext().getContentResolver()); + accentColor = ThemeUtils.primaryAccentColor(context); + storageManager = new FileDataStorageManager(account, context.getContentResolver()); capabilities = storageManager.getCapability(account.name); - avatarRadiusDimension = context.getResources().getDimension(R.dimen.user_icon_radius); } + @NonNull @Override - public int getCount() { - return shares.size(); + public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_details_share_user_item, parent, false); + return new UserViewHolder(v); } @Override - public Object getItem(int position) { - return shares.get(position); - } - - @Override - public long getItemId(int position) { - return 0; - } - - @Override - public @NonNull - View getView(final int position, View convertView, @NonNull ViewGroup parent) { - View view = convertView; - if (view == null) { - LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.file_details_share_user_item, parent, false); - } - + public void onBindViewHolder(@NonNull UserViewHolder holder, int position) { if (shares != null && shares.size() > position) { - OCShare share = shares.get(position); + final OCShare share = shares.get(position); - TextView userName = view.findViewById(R.id.userOrGroupName); - final ImageView editShareButton = view.findViewById(R.id.editShareButton); - ImageView icon = view.findViewById(R.id.userIcon); String name = share.getSharedWithDisplayName(); if (share.getShareType() == ShareType.GROUP) { - name = getContext().getString(R.string.share_group_clarification, name); + name = context.getString(R.string.share_group_clarification, name); try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); + holder.avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_group); + holder.avatar.setImageResource(R.drawable.ic_group); } } else if (share.getShareType() == ShareType.EMAIL) { - name = getContext().getString(R.string.share_email_clarification, name); + name = context.getString(R.string.share_email_clarification, name); try { - icon.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); + holder.avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension)); } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { - icon.setImageResource(R.drawable.ic_email); + holder.avatar.setImageResource(R.drawable.ic_email); } } else { - icon.setTag(share.getShareWith()); + holder.avatar.setTag(share.getShareWith()); DisplayUtils.setAvatar(account, share.getShareWith(), this, avatarRadiusDimension, - context.getResources(), storageManager, icon, context); + context.getResources(), storageManager, holder.avatar, context); } - userName.setText(name); + holder.name.setText(name); + + ThemeUtils.tintCheckbox(holder.allowEditing, accentColor); + holder.allowEditing.setChecked(canEdit(share)); + holder.allowEditing.setOnClickListener(v -> allowEditClick(holder.allowEditing, share)); - /// bind listener to edit privileges - editShareButton.setOnClickListener(v -> onOverflowIconClicked(v, shares.get(position))); + // bind listener to edit privileges + holder.editShareButton.setOnClickListener(v -> onOverflowIconClicked(v, holder.allowEditing, share)); + } + } + + @Override + public long getItemId(int position) { + return shares.get(position).getId(); + } + + @Override + public int getItemCount() { + return shares.size(); + } + + private void allowEditClick(AppCompatCheckBox checkBox, @NonNull OCShare share) { + if (!share.isFolder()) { + share.setPermissions(listener.updatePermissionsToShare( + share, + canReshare(share), + checkBox.isChecked(), + false, + false, + false + )); + } else { + share.setPermissions(listener.updatePermissionsToShare( + share, + canReshare(share), + checkBox.isChecked(), + checkBox.isChecked(), + checkBox.isChecked(), + checkBox.isChecked() + )); } - return view; } - private void onOverflowIconClicked(View view, OCShare share) { + private void onOverflowIconClicked(View view, AppCompatCheckBox allowEditsCheckBox, OCShare share) { // use grey as fallback for elements where custom theming is not available if (ThemeUtils.themingEnabled(context)) { context.getTheme().applyStyle(R.style.FallbackThemingTheme, true); @@ -153,7 +174,7 @@ private void onOverflowIconClicked(View view, OCShare share) { prepareOptionsMenu(popup.getMenu(), share); - popup.setOnMenuItemClickListener(item -> optionsItemSelected(popup.getMenu(), item, share)); + popup.setOnMenuItemClickListener(item -> optionsItemSelected(popup.getMenu(), item, allowEditsCheckBox, share)); popup.show(); } @@ -164,33 +185,21 @@ private void onOverflowIconClicked(View view, OCShare share) { * @param share the shared file */ private void prepareOptionsMenu(Menu menu, OCShare share) { - int sharePermissions = share.getPermissions(); - boolean isFederated = ShareType.FEDERATED.equals(share.getShareType()); MenuItem reshareItem = menu.findItem(R.id.action_can_reshare); - if (isFederated || - (capabilities != null && capabilities.getFilesSharingResharing().isFalse())) { + if (isReshareForbidden(share)) { reshareItem.setVisible(false); } - reshareItem.setChecked((sharePermissions & OCShare.SHARE_PERMISSION_FLAG) > 0); + reshareItem.setChecked(canReshare(share)); - MenuItem editItem = menu.findItem(R.id.action_can_edit); - int anyUpdatePermission = OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | - OCShare.DELETE_PERMISSION_FLAG; - boolean canEdit = (sharePermissions & anyUpdatePermission) > 0; - editItem.setChecked(canEdit); - - OwnCloudVersion serverVersion = AccountUtils.getServerVersion(account); - boolean isNotReshareableFederatedSupported = serverVersion.isNotReshareableFederatedSupported(); - boolean areEditOptionsAvailable = !isFederated || isNotReshareableFederatedSupported; MenuItem editCreateItem = menu.findItem(R.id.action_can_edit_create); MenuItem editChangeItem = menu.findItem(R.id.action_can_edit_change); MenuItem editDeleteItem = menu.findItem(R.id.action_can_edit_delete); - if (file.isFolder() && areEditOptionsAvailable) { + if (file.isFolder() && isEditOptionsAvailable(share)) { /// TODO change areEditOptionsAvailable in order to delete !isFederated - editCreateItem.setChecked((sharePermissions & OCShare.CREATE_PERMISSION_FLAG) > 0); - editChangeItem.setChecked((sharePermissions & OCShare.UPDATE_PERMISSION_FLAG) > 0); - editDeleteItem.setChecked((sharePermissions & OCShare.DELETE_PERMISSION_FLAG) > 0); + editCreateItem.setChecked(canCreate(share)); + editChangeItem.setChecked(canUpdate(share)); + editDeleteItem.setChecked(canDelete(share)); } else { editCreateItem.setVisible(false); editChangeItem.setVisible(false); @@ -201,31 +210,68 @@ private void prepareOptionsMenu(Menu menu, OCShare share) { menu.findItem(R.id.action_expiration_date), share.getExpirationDate(), context.getResources()); } - private boolean optionsItemSelected(Menu menu, MenuItem item, OCShare share) { + private boolean isEditOptionsAvailable(OCShare share) { + return !ShareType.FEDERATED.equals(share.getShareType()) + || AccountUtils.getServerVersion(account).isNotReshareableFederatedSupported(); + } + + private boolean isReshareForbidden(OCShare share) { + return ShareType.FEDERATED.equals(share.getShareType()) || + (capabilities != null && capabilities.getFilesSharingResharing().isFalse()); + } + + private boolean canEdit(OCShare share) { + return (share.getPermissions() & + (OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | OCShare.DELETE_PERMISSION_FLAG)) > 0; + } + + private boolean canCreate(OCShare share) { + return (share.getPermissions() & OCShare.CREATE_PERMISSION_FLAG) > 0; + } + + private boolean canUpdate(OCShare share) { + return (share.getPermissions() & OCShare.UPDATE_PERMISSION_FLAG) > 0; + } + + private boolean canDelete(OCShare share) { + return (share.getPermissions() & OCShare.DELETE_PERMISSION_FLAG) > 0; + } + + private boolean canReshare(OCShare share) { + return (share.getPermissions() & OCShare.SHARE_PERMISSION_FLAG) > 0; + } + + private boolean optionsItemSelected(Menu menu, MenuItem item, AppCompatCheckBox allowEditsCheckBox, OCShare share) { switch (item.getItemId()) { - case R.id.action_can_edit: { - item.setChecked(!item.isChecked()); - if (file.isFolder() && !item.isChecked()) { - menu.findItem(R.id.action_can_edit_create).setChecked(false); - menu.findItem(R.id.action_can_edit_change).setChecked(false); - menu.findItem(R.id.action_can_edit_delete).setChecked(false); - } - share.setPermissions(updatePermissionsToShare(share, menu)); - return true; - } case R.id.action_can_edit_create: case R.id.action_can_edit_change: case R.id.action_can_edit_delete: { item.setChecked(!item.isChecked()); - if (item.isChecked() && !menu.findItem(R.id.action_can_edit).isChecked()) { - menu.findItem(R.id.action_can_edit).setChecked(true); + if (item.isChecked() && !allowEditsCheckBox.isChecked()) { + allowEditsCheckBox.setChecked(true); } - share.setPermissions(updatePermissionsToShare(share, menu)); + share.setPermissions( + updatePermissionsToShare( + share, + menu.findItem(R.id.action_can_reshare).isChecked(), + allowEditsCheckBox.isChecked(), + menu.findItem(R.id.action_can_edit_create).isChecked(), + menu.findItem(R.id.action_can_edit_change).isChecked(), + menu.findItem(R.id.action_can_edit_delete).isChecked()) + ); return true; } case R.id.action_can_reshare: { item.setChecked(!item.isChecked()); - share.setPermissions(updatePermissionsToShare(share, menu)); + share.setPermissions( + updatePermissionsToShare( + share, + menu.findItem(R.id.action_can_reshare).isChecked(), + allowEditsCheckBox.isChecked(), + menu.findItem(R.id.action_can_edit_create).isChecked(), + menu.findItem(R.id.action_can_edit_change).isChecked(), + menu.findItem(R.id.action_can_edit_delete).isChecked()) + ); return true; } case R.id.action_unshare: { @@ -247,14 +293,15 @@ private boolean optionsItemSelected(Menu menu, MenuItem item, OCShare share) { } } - private int updatePermissionsToShare(OCShare share, Menu menu) { + private int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate, + boolean canEditChange, boolean canEditDelete) { return listener.updatePermissionsToShare( share, - menu.findItem(R.id.action_can_reshare).isChecked(), - menu.findItem(R.id.action_can_edit).isChecked(), - menu.findItem(R.id.action_can_edit_create).isChecked(), - menu.findItem(R.id.action_can_edit_change).isChecked(), - menu.findItem(R.id.action_can_edit_delete).isChecked() + canReshare, + canEdit, + canEditCreate, + canEditChange, + canEditDelete ); } @@ -275,6 +322,22 @@ public boolean shouldCallGeneratedCallback(String tag, Object callContext) { return false; } + class UserViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.userIcon) + ImageView avatar; + @BindView(R.id.userOrGroupName) + TextView name; + @BindView(R.id.allow_editing) + AppCompatCheckBox allowEditing; + @BindView(R.id.editShareButton) + ImageView editShareButton; + + UserViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + } + public interface ShareeListAdapterListener { /** * unshare with given sharee {@link OCShare}. diff --git a/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java b/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java new file mode 100644 index 000000000000..ee67eb1ed791 --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java @@ -0,0 +1,77 @@ +/* + * Nextcloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2018 Andy Scherzinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + */ + +package com.owncloud.android.ui.decoration; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.RecyclerView; +import android.util.DisplayMetrics; +import android.view.View; + +/** + * DividerItemDecoration based on {@link DividerItemDecoration} adding a 72dp left padding. + */ +public class SimpleListItemDividerDecoration extends DividerItemDecoration { + private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; + + private final Rect bounds = new Rect(); + private Drawable divider; + private int leftPadding; + + /** + * Default divider will be used + */ + public SimpleListItemDividerDecoration(Context context) { + super(context, DividerItemDecoration.VERTICAL); + final TypedArray a = context.obtainStyledAttributes(ATTRS); + divider = a.getDrawable(0); + leftPadding = Math.round(72 * (context.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT)); + } + + @Override + public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) { + canvas.save(); + final int right; + //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides. + if (parent.getClipToPadding()) { + right = parent.getWidth() - parent.getPaddingRight(); + canvas.clipRect(leftPadding, parent.getPaddingTop(), right, + parent.getHeight() - parent.getPaddingBottom()); + } else { + right = parent.getWidth(); + } + + final int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = parent.getChildAt(i); + parent.getDecoratedBoundsWithMargins(child, bounds); + final int bottom = bounds.bottom + Math.round(child.getTranslationY()); + final int top = bottom - 1; + divider.setBounds(leftPadding, top, right, bottom); + divider.draw(canvas); + } + canvas.restore(); + } +} diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index c0c1d3cfba39..6f508d403bd2 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -31,6 +31,8 @@ import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v7.widget.AppCompatCheckBox; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; import android.view.LayoutInflater; import android.view.Menu; @@ -53,6 +55,7 @@ import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.UserListAdapter; +import com.owncloud.android.ui.decoration.SimpleListItemDividerDecoration; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; @@ -84,7 +87,7 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt SearchView searchView; @BindView(R.id.fdshareUsersList) - ListView usersList; + RecyclerView usersList; @BindView(R.id.fdShareNoUsers) TextView noList; @@ -205,14 +208,14 @@ public void setShareWithUserInfo() { private void updateListOfUserGroups() { // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed - UserListAdapter mUserGroupsAdapter = new UserListAdapter(getActivity().getSupportFragmentManager(),getActivity().getApplicationContext(), - R.layout.share_user_item, shares, account, file, this); if (shares.size() > 0) { usersList.setVisibility(View.VISIBLE); - usersList.setAdapter(mUserGroupsAdapter); + usersList.setAdapter(new UserListAdapter(getActivity().getSupportFragmentManager(), + getActivity().getApplicationContext(), shares, account, file, this)); + usersList.setLayoutManager(new LinearLayoutManager(getContext())); + usersList.addItemDecoration(new SimpleListItemDividerDecoration(getContext())); noList.setVisibility(View.GONE); - FileDetailSharingFragmentHelper.setListViewHeightBasedOnChildren(usersList); } else { usersList.setVisibility(View.GONE); noList.setVisibility(View.VISIBLE); diff --git a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java index cc04ace26a89..638ce5cf7bf1 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java @@ -653,7 +653,6 @@ private void updateListOfUserGroups() { noShares.setVisibility(View.GONE); usersList.setVisibility(View.VISIBLE); usersList.setAdapter(mUserGroupsAdapter); - FileDetailSharingFragmentHelper.setListViewHeightBasedOnChildren(usersList); } else { noShares.setVisibility(View.VISIBLE); usersList.setVisibility(View.GONE); diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java index c53188214ba1..a08775a6c771 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -25,11 +25,7 @@ import android.content.res.Resources; import android.support.v7.widget.SearchView; import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; -import android.widget.ListAdapter; -import android.widget.ListView; import com.owncloud.android.R; import com.owncloud.android.lib.resources.shares.OCShare; @@ -122,29 +118,4 @@ public boolean onQueryTextChange(String newText) { public static boolean isPublicShareDisabled(OCCapability capabilities) { return (capabilities != null && capabilities.getFilesSharingPublicEnabled().isFalse()); } - - /** - * Fix scroll in ListView when the parent is a ScrollView - */ - public static void setListViewHeightBasedOnChildren(ListView listView) { - ListAdapter listAdapter = listView.getAdapter(); - if (listAdapter == null) { - return; - } - int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.AT_MOST); - int totalHeight = 0; - View view = null; - for (int i = 0; i < listAdapter.getCount(); i++) { - view = listAdapter.getView(i, view, listView); - if (i == 0) { - view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); - } - view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED); - totalHeight += view.getMeasuredHeight(); - } - ViewGroup.LayoutParams params = listView.getLayoutParams(); - params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); - listView.setLayoutParams(params); - listView.requestLayout(); - } } diff --git a/src/main/res/layout/file_details_share_user_item.xml b/src/main/res/layout/file_details_share_user_item.xml index 9d02c4cd401d..12b96599a38c 100644 --- a/src/main/res/layout/file_details_share_user_item.xml +++ b/src/main/res/layout/file_details_share_user_item.xml @@ -29,38 +29,47 @@ android:id="@+id/userIcon" android:layout_width="@dimen/user_icon_size" android:layout_height="@dimen/user_icon_size" - android:src="@drawable/ic_user" android:layout_gravity="center_vertical" - android:layout_marginTop="@dimen/standard_half_margin" android:layout_marginBottom="@dimen/standard_half_margin" - android:layout_marginLeft="@dimen/standard_margin" - android:layout_marginStart="@dimen/standard_margin" android:layout_marginEnd="@dimen/standard_margin" + android:layout_marginLeft="@dimen/standard_margin" android:layout_marginRight="@dimen/standard_margin" - android:contentDescription="@string/user_icon"/> + android:layout_marginStart="@dimen/standard_margin" + android:layout_marginTop="@dimen/standard_half_margin" + android:contentDescription="@string/user_icon" + android:src="@drawable/ic_user" /> + android:singleLine="true" + android:text="@string/username" + android:textColor="@color/black" + android:textSize="@dimen/file_details_username_text_size" /> - + + + android:contentDescription="@string/overflow_menu" + android:padding="@dimen/standard_half_padding" + android:src="@drawable/ic_dots_vertical" /> diff --git a/src/main/res/layout/file_details_sharing_fragment.xml b/src/main/res/layout/file_details_sharing_fragment.xml index 04f4e0501e92..b34ecee6c827 100644 --- a/src/main/res/layout/file_details_sharing_fragment.xml +++ b/src/main/res/layout/file_details_sharing_fragment.xml @@ -90,12 +90,11 @@ android:src="@drawable/ic_dots_vertical" /> - diff --git a/src/main/res/menu/file_detail_sharing_menu.xml b/src/main/res/menu/file_detail_sharing_menu.xml index 1ac0287708e1..7f05cc42ac7c 100644 --- a/src/main/res/menu/file_detail_sharing_menu.xml +++ b/src/main/res/menu/file_detail_sharing_menu.xml @@ -22,12 +22,6 @@ xmlns:tools="http://schemas.android.com/tools" tools:ignore="AppCompatResource"> - Date: Sun, 27 May 2018 21:48:18 +0200 Subject: [PATCH 59/75] use nicer UI element names --- .../com/owncloud/android/ui/adapter/UserListAdapter.java | 6 +++--- src/main/res/layout/file_details_share_user_item.xml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index f7b3cb3fa407..5c80c2fc61e5 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -323,11 +323,11 @@ public boolean shouldCallGeneratedCallback(String tag, Object callContext) { } class UserViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.userIcon) + @BindView(R.id.avatar) ImageView avatar; - @BindView(R.id.userOrGroupName) + @BindView(R.id.name) TextView name; - @BindView(R.id.allow_editing) + @BindView(R.id.allowEditing) AppCompatCheckBox allowEditing; @BindView(R.id.editShareButton) ImageView editShareButton; diff --git a/src/main/res/layout/file_details_share_user_item.xml b/src/main/res/layout/file_details_share_user_item.xml index 12b96599a38c..c8eb51ed6218 100644 --- a/src/main/res/layout/file_details_share_user_item.xml +++ b/src/main/res/layout/file_details_share_user_item.xml @@ -26,7 +26,7 @@ tools:ignore="UseCompoundDrawables"> Date: Sun, 27 May 2018 22:12:10 +0200 Subject: [PATCH 60/75] shorten edit label --- src/main/res/layout/file_details_share_user_item.xml | 2 +- src/main/res/layout/file_details_sharing_fragment.xml | 2 +- src/main/res/values/strings.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/res/layout/file_details_share_user_item.xml b/src/main/res/layout/file_details_share_user_item.xml index c8eb51ed6218..55601c6ba3e7 100644 --- a/src/main/res/layout/file_details_share_user_item.xml +++ b/src/main/res/layout/file_details_share_user_item.xml @@ -58,7 +58,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:text="@string/share_via_link_edit_permission_label" /> + android:text="@string/edit_permission_label" /> + android:text="@string/edit_permission_label"/> Protect with password Secured none - Allow editing + can edit Hide file listing Get link Share with… From abc2be7a23d9453f240559511ed6cb77a5c3bbd5 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 27 May 2018 22:15:01 +0200 Subject: [PATCH 61/75] further shortened the label --- src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index fc3979500ffe..457e35a1a14d 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -486,7 +486,7 @@ Protect with password Secured none - can edit + edit Hide file listing Get link Share with… From 5c4a450b110e8b279af30a742f8a55aaf4ad9f5b Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 28 May 2018 10:50:52 +0200 Subject: [PATCH 62/75] use file details sharing tab for files also via the share bottom sheet (only if FileDisplayActivity is the parent) --- .../com/owncloud/android/ui/dialog/SendShareDialog.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java index 6d0a8996f4ff..c050efa7eb39 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.java @@ -22,6 +22,7 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.adapter.SendButtonAdapter; import com.owncloud.android.ui.components.SendButtonData; import com.owncloud.android.ui.helpers.FileOperationsHelper; @@ -229,7 +230,11 @@ private Intent createSendIntent() { } private void shareFile(OCFile file) { - fileOperationsHelper.showShareFile(file); + if (getActivity() instanceof FileDisplayActivity) { + ((FileDisplayActivity) getActivity()).showDetails(file, 1); + } else { + fileOperationsHelper.showShareFile(file); + } dismiss(); } From 5154280f00b75d3b49621008cafa43dd8f1448b2 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 28 May 2018 11:00:55 +0200 Subject: [PATCH 63/75] fix refreshing OC file list after sharing info changes --- .../owncloud/android/ui/activity/FileDisplayActivity.java | 6 ++++-- .../android/ui/fragment/FileDetailSharingFragment.java | 1 - .../com/owncloud/android/ui/fragment/ShareFileFragment.java | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index bc364b5a19f6..1bd19493965e 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1878,6 +1878,7 @@ private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation ope chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); fileDetailFragment.getFileDetailSharingFragment().refreshPublicShareFromDB(); + refreshListOfFilesFragment(false); } else { // Detect Failure (403) --> maybe needs password String password = operation.getPassword(); @@ -1909,11 +1910,12 @@ private void onUpdateShareInformation(RemoteOperationResult result, @StringRes i if (result.isSuccess()) { updateFileFromDB(); - } else if (fileDetailFragment.getView() != null){ + refreshListOfFilesFragment(false); + } else if (fileDetailFragment.getView() != null) { Snackbar.make(fileDetailFragment.getView(), errorString, Snackbar.LENGTH_LONG).show(); } - if (fileDetailFragment!=null && fileDetailFragment instanceof FileDetailFragment) { + if (fileDetailFragment != null && fileDetailFragment instanceof FileDetailFragment) { ((FileDetailFragment) fileDetailFragment).getFileDetailSharingFragment() .onUpdateShareInformation(result, getFile()); } diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 6f508d403bd2..08cc75a1e30a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -41,7 +41,6 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListView; import android.widget.PopupMenu; import android.widget.TextView; diff --git a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java index 638ce5cf7bf1..1913d8995b30 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java @@ -58,7 +58,6 @@ import com.owncloud.android.ui.adapter.ShareUserListAdapter; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; -import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; From 952db5a34b2151ae22dddfd2677db982eb708258 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 28 May 2018 12:15:11 +0200 Subject: [PATCH 64/75] added javaDoc for public methods --- .../util/FileDetailSharingFragmentHelper.java | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java index a08775a6c771..089e652b0356 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -23,6 +23,7 @@ import android.app.SearchManager; import android.content.ComponentName; import android.content.res.Resources; +import android.support.annotation.StringRes; import android.support.v7.widget.SearchView; import android.view.MenuItem; import android.view.inputmethod.EditorInfo; @@ -39,6 +40,14 @@ */ public class FileDetailSharingFragmentHelper { + /** + * Sets checked/visiblity state on the given {@link MenuItem} based on the given criteria. + * + * @param fileListing the {@link MenuItem} to be setup + * @param isFolder flag if it is a folder + * @param isEditingAllowed flag if editing is allowed + * @param publicSharePermissions share permissions of the link + */ public static void setupHideFileListingMenuItem(MenuItem fileListing, boolean isFolder, boolean isEditingAllowed, @@ -55,20 +64,35 @@ public static void setupHideFileListingMenuItem(MenuItem fileListing, } } + /** + * sets up the password {@link MenuItem}'s title based on the fact if a password is present. + * + * @param password the password {@link MenuItem} + * @param isPasswordProtected flag is a password is present + * @param res Resources to load the corresponding strings. + */ public static void setupPasswordMenuItem(MenuItem password, boolean isPasswordProtected, Resources res) { if (isPasswordProtected) { - password.setTitle(res.getString( - R.string.share_via_link_menu_password_label, - res.getString(R.string.share_via_link_password_title) - )); + password.setTitle(getPasswordTitle(res, R.string.share_via_link_password_title)); } else { - password.setTitle(res.getString( - R.string.share_via_link_menu_password_label, - res.getString(R.string.share_via_link_no_password_title) - )); + password.setTitle(getPasswordTitle(res, R.string.share_via_link_no_password_title)); } } + private static String getPasswordTitle(Resources res, @StringRes int passwordValue) { + return res.getString( + R.string.share_via_link_menu_password_label, + res.getString(passwordValue) + ); + } + + /** + * sets up the expiration date {@link MenuItem}'s title based on the fact if an expiration date is present. + * + * @param expirationDate the expiration date {@link MenuItem} + * @param expirationDateValue the expiration date + * @param res Resources to load the corresponding strings. + */ public static void setupExpirationDateMenuItem(MenuItem expirationDate, long expirationDateValue, Resources res) { if (expirationDateValue > 0) { String formattedDate = @@ -87,6 +111,13 @@ public static void setupExpirationDateMenuItem(MenuItem expirationDate, long exp } } + /** + * sets up the {@link SearchView}. + * + * @param searchManager the {@link SearchManager} + * @param searchView the {@link SearchView} + * @param componentName the {@link ComponentName} + */ public static void setupSearchView(SearchManager searchManager, SearchView searchView, ComponentName componentName) { // assumes parent activity is the searchable activity searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName)); From d8c4be0604cb39b844a73827fc7e6c59e2f263e3 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 28 May 2018 14:59:59 +0200 Subject: [PATCH 65/75] fix lint for item divider, plus code formatting --- .../ui/decoration/SimpleListItemDividerDecoration.java | 5 +++-- .../owncloud/android/ui/fragment/FileDetailFragment.java | 2 ++ .../ui/fragment/util/FileDetailSharingFragmentHelper.java | 6 +----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java b/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java index ee67eb1ed791..1d5d0306f280 100644 --- a/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java +++ b/src/main/java/com/owncloud/android/ui/decoration/SimpleListItemDividerDecoration.java @@ -45,9 +45,10 @@ public class SimpleListItemDividerDecoration extends DividerItemDecoration { */ public SimpleListItemDividerDecoration(Context context) { super(context, DividerItemDecoration.VERTICAL); - final TypedArray a = context.obtainStyledAttributes(ATTRS); - divider = a.getDrawable(0); + final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS); + divider = styledAttributes.getDrawable(0); leftPadding = Math.round(72 * (context.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT)); + styledAttributes.recycle(); } @Override diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index a43c1bf8dbf1..78346d5fd1c5 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -362,6 +362,8 @@ private void prepareOptionsMenu(Menu menu) { if (!getResources().getBoolean(R.bool.large_land_layout)){ FileMenuFilter.hideMenuItems(menu.findItem(R.id.action_sync_account)); } + + } private boolean optionsItemSelected(MenuItem item) { diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java index 089e652b0356..29133d521687 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -95,13 +95,9 @@ private static String getPasswordTitle(Resources res, @StringRes int passwordVal */ public static void setupExpirationDateMenuItem(MenuItem expirationDate, long expirationDateValue, Resources res) { if (expirationDateValue > 0) { - String formattedDate = - SimpleDateFormat.getDateInstance().format( - new Date(expirationDateValue) - ); expirationDate.setTitle(res.getString( R.string.share_expiration_date_label, - formattedDate + SimpleDateFormat.getDateInstance().format(new Date(expirationDateValue)) )); } else { expirationDate.setTitle(res.getString( From f201a2dd6622b3ce3add49ca942c018f96500080 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 28 May 2018 17:30:55 +0200 Subject: [PATCH 66/75] first changes due to designers review --- .../fragment/FileDetailSharingFragment.java | 3 +-- .../util/FileDetailSharingFragmentHelper.java | 19 ++++--------------- src/main/res/layout/file_details_fragment.xml | 2 ++ .../layout/file_details_share_user_item.xml | 1 + .../layout/file_details_sharing_fragment.xml | 5 +++-- src/main/res/values/strings.xml | 10 ++++------ src/main/res/values/styles.xml | 5 +++++ 7 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 08cc75a1e30a..4d08b67991b3 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -281,8 +281,7 @@ private void prepareOptionsMenu(Menu menu) { ); FileDetailSharingFragmentHelper.setupPasswordMenuItem( menu.findItem(R.id.action_share_link_password), - publicShare.isPasswordProtected(), - res + publicShare.isPasswordProtected() ); FileDetailSharingFragmentHelper.setupExpirationDateMenuItem( menu.findItem(R.id.action_share_link_expiration_date), diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java index 29133d521687..68aaba9761c2 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -69,23 +69,15 @@ public static void setupHideFileListingMenuItem(MenuItem fileListing, * * @param password the password {@link MenuItem} * @param isPasswordProtected flag is a password is present - * @param res Resources to load the corresponding strings. */ - public static void setupPasswordMenuItem(MenuItem password, boolean isPasswordProtected, Resources res) { + public static void setupPasswordMenuItem(MenuItem password, boolean isPasswordProtected) { if (isPasswordProtected) { - password.setTitle(getPasswordTitle(res, R.string.share_via_link_password_title)); + password.setTitle(R.string.share_password_title); } else { - password.setTitle(getPasswordTitle(res, R.string.share_via_link_no_password_title)); + password.setTitle(R.string.share_no_password_title); } } - private static String getPasswordTitle(Resources res, @StringRes int passwordValue) { - return res.getString( - R.string.share_via_link_menu_password_label, - res.getString(passwordValue) - ); - } - /** * sets up the expiration date {@link MenuItem}'s title based on the fact if an expiration date is present. * @@ -100,10 +92,7 @@ public static void setupExpirationDateMenuItem(MenuItem expirationDate, long exp SimpleDateFormat.getDateInstance().format(new Date(expirationDateValue)) )); } else { - expirationDate.setTitle(res.getString( - R.string.share_expiration_date_label, - res.getString(R.string.share_via_link_menu_expiration_date_never) - )); + expirationDate.setTitle(R.string.share_no_expiration_date_label); } } diff --git a/src/main/res/layout/file_details_fragment.xml b/src/main/res/layout/file_details_fragment.xml index fc5f7c6538c4..afe1b938853a 100644 --- a/src/main/res/layout/file_details_fragment.xml +++ b/src/main/res/layout/file_details_fragment.xml @@ -52,6 +52,7 @@ android:ellipsize="middle" android:text="@string/placeholder_filename" android:textColor="@color/black" + android:textStyle="bold" android:textSize="16sp" /> @@ -73,6 +73,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" + android:textSize="16sp" android:text="@string/edit_permission_label"/> + android:textSize="16sp" /> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 457e35a1a14d..22409b797aa5 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -479,13 +479,11 @@ No data shared with users yet Add user or group Password protect (%1$s) - Set expiration date (%1$s) - never + Expires %1$s + Set expiration date Share link - Set expiration date - Protect with password - Secured - none + Password-protected + Set password edit Hide file listing Get link diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index ff529779ec32..c3fa50189020 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -250,4 +250,9 @@ @color/login_text_color @color/login_text_color + + From 1f58926ad050e468f632eed6b94558390b0d7303 Mon Sep 17 00:00:00 2001 From: nextcloud-android-bot <> Date: Mon, 28 May 2018 15:19:03 +0000 Subject: [PATCH 67/75] Drone: update Lint results to reflect reduced error/warning count [skip ci] --- scripts/lint/lint-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lint/lint-results.txt b/scripts/lint/lint-results.txt index 4d1eaf39a016..eaacc19c5e75 100644 --- a/scripts/lint/lint-results.txt +++ b/scripts/lint/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 104 warnings + Lint Report: 103 warnings From a3daf0642e2ef6478573593d2c68b30068a7d65f Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 28 May 2018 19:23:24 +0200 Subject: [PATCH 68/75] fix lint again --- src/main/res/values/dims.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/res/values/dims.xml b/src/main/res/values/dims.xml index 59f3dae86d87..517be6899d70 100644 --- a/src/main/res/values/dims.xml +++ b/src/main/res/values/dims.xml @@ -102,7 +102,6 @@ 72dp 72dp 72dp - 14sp 32dp 32dp 16sp From d72eb2a4b7ea6b34db190282d0a503229cf88788 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 28 May 2018 19:49:29 +0200 Subject: [PATCH 69/75] fix codacy --- .../ui/fragment/util/FileDetailSharingFragmentHelper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java index 68aaba9761c2..072f9a3a2967 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java +++ b/src/main/java/com/owncloud/android/ui/fragment/util/FileDetailSharingFragmentHelper.java @@ -23,7 +23,6 @@ import android.app.SearchManager; import android.content.ComponentName; import android.content.res.Resources; -import android.support.annotation.StringRes; import android.support.v7.widget.SearchView; import android.view.MenuItem; import android.view.inputmethod.EditorInfo; From 892db9f689e03fff0aff56ac5217d22a8aa634c1 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 29 May 2018 07:57:27 +0200 Subject: [PATCH 70/75] raise font size on file name --- src/main/res/layout/file_details_fragment.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/res/layout/file_details_fragment.xml b/src/main/res/layout/file_details_fragment.xml index afe1b938853a..81b6d9b1902d 100644 --- a/src/main/res/layout/file_details_fragment.xml +++ b/src/main/res/layout/file_details_fragment.xml @@ -53,7 +53,7 @@ android:text="@string/placeholder_filename" android:textColor="@color/black" android:textStyle="bold" - android:textSize="16sp" /> + android:textSize="20sp" /> Date: Tue, 29 May 2018 08:31:50 +0200 Subject: [PATCH 71/75] Fix theming issue with date picker dialog --- .../ui/dialog/ExpirationDatePickerDialogFragment.java | 3 +++ src/main/res/values/styles.xml | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java index 884956764ccc..ddcc97eb3802 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java @@ -24,6 +24,7 @@ import android.app.DatePickerDialog; import android.app.Dialog; +import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; @@ -34,6 +35,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.ui.activity.FileActivity; +import com.owncloud.android.utils.ThemeUtils; import java.util.Calendar; @@ -121,6 +123,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { // Create a new instance of DatePickerDialog DatePickerDialog dialog = new DatePickerDialog( getActivity(), + R.style.FallbackDatePickerDialogTheme, this, chosenDate.get(Calendar.YEAR), chosenDate.get(Calendar.MONTH), diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index c3fa50189020..fbfff9279804 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -39,6 +39,12 @@ #757575 + +