diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 3d41066559f7..36352541cc85 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -410 \ No newline at end of file +413 diff --git a/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/src/main/java/com/nextcloud/client/di/ComponentsModule.java index 8054366f39c5..83dd126ecbf9 100644 --- a/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -69,6 +69,7 @@ import com.owncloud.android.ui.fragment.FileDetailSharingFragment; import com.owncloud.android.ui.fragment.LocalFileListFragment; import com.owncloud.android.ui.fragment.OCFileListFragment; +import com.owncloud.android.ui.fragment.PhotoFragment; import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment; import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewImageFragment; @@ -136,6 +137,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract PreviewMediaFragment previewMediaFragment(); @ContributesAndroidInjector abstract PreviewTextFragment previewTextFragment(); + @ContributesAndroidInjector + abstract PhotoFragment photoFragment(); + @ContributesAndroidInjector abstract ReceiveExternalFilesActivity.DialogMultipleAccount dialogMultipleAccount(); @ContributesAndroidInjector abstract ReceiveExternalFilesActivity.DialogInputUploadFilename dialogInputUploadFilename(); diff --git a/src/main/java/com/nextcloud/client/preferences/AppPreferences.java b/src/main/java/com/nextcloud/client/preferences/AppPreferences.java index 544522834810..f963aed37c83 100644 --- a/src/main/java/com/nextcloud/client/preferences/AppPreferences.java +++ b/src/main/java/com/nextcloud/client/preferences/AppPreferences.java @@ -292,4 +292,8 @@ public interface AppPreferences { boolean isUserIdMigrated(); void setMigratedUserId(boolean value); + + void setPhotoSearchTimestamp(long timestamp); + + long getPhotoSearchTimestamp(); } diff --git a/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index 855b185b443d..ef832689c166 100644 --- a/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -72,6 +72,7 @@ public final class AppPreferencesImpl implements AppPreferences { private static final String PREF__LOCK = SettingsActivity.PREFERENCE_LOCK; private static final String PREF__SELECTED_ACCOUNT_NAME = "select_oc_account"; private static final String PREF__MIGRATED_USER_ID = "migrated_user_id"; + private static final String PREF__PHOTO_SEARCH_TIMESTAMP = "photo_search_timestamp"; private final Context context; private final SharedPreferences preferences; @@ -465,6 +466,16 @@ public void setMigratedUserId(boolean value) { preferences.edit().putBoolean(PREF__MIGRATED_USER_ID, value).apply(); } + @Override + public void setPhotoSearchTimestamp(long timestamp) { + preferences.edit().putLong(PREF__PHOTO_SEARCH_TIMESTAMP, timestamp).apply(); + } + + @Override + public long getPhotoSearchTimestamp() { + return preferences.getLong(PREF__PHOTO_SEARCH_TIMESTAMP, 0); + } + /** * Get preference value for a folder. * If folder is not set itself, it finds an ancestor that is set. diff --git a/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java b/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java index 34b4897d2bfe..6b0e747845fe 100644 --- a/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java +++ b/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java @@ -34,7 +34,7 @@ */ public class EmptyRecyclerView extends RecyclerView { private View mEmptyView; - private boolean hasFooter; + private boolean hasFooter = false; public EmptyRecyclerView(Context context) { super(context); 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 b0974d1eb435..ebd111868dc2 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -88,6 +88,7 @@ import com.owncloud.android.ui.events.DummyDrawerEvent; import com.owncloud.android.ui.events.SearchEvent; import com.owncloud.android.ui.fragment.OCFileListFragment; +import com.owncloud.android.ui.fragment.PhotoFragment; import com.owncloud.android.ui.trashbin.TrashbinActivity; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DrawerMenuUtil; @@ -424,16 +425,32 @@ private void selectNavigationItem(final MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.nav_all_files: - showFiles(false); - EventBus.getDefault().post(new ChangeMenuEvent()); + if (this instanceof FileDisplayActivity) { + if (((FileDisplayActivity) this).getListOfFilesFragment() instanceof PhotoFragment) { + showFiles(false); + Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); + intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } else { + showFiles(false); + EventBus.getDefault().post(new ChangeMenuEvent()); + } + } else { + showFiles(false); + Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); + intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } + break; case R.id.nav_favorites: handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH, SearchEvent.UnsetType.NO_UNSET), menuItem.getItemId()); break; case R.id.nav_photos: - handleSearchEvents(new SearchEvent("image/%", SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH, - SearchEvent.UnsetType.NO_UNSET), menuItem.getItemId()); + startPhotoSearch(menuItem); break; case R.id.nav_on_device: EventBus.getDefault().post(new ChangeMenuEvent()); @@ -479,22 +496,10 @@ private void selectNavigationItem(final MenuItem menuItem) { menuItem.setChecked(false); UserInfoActivity.openAccountRemovalConfirmationDialog(getAccount(), getSupportFragmentManager(), true); break; - case R.id.nav_recently_added: - handleSearchEvents(new SearchEvent("%", SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH, - SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem.getItemId()); - break; case R.id.nav_recently_modified: handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH, SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem.getItemId()); break; - case R.id.nav_shared: - handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.SHARED_SEARCH, - SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem.getItemId()); - break; - case R.id.nav_videos: - handleSearchEvents(new SearchEvent("video/%", SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH, - SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem.getItemId()); - break; default: if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK && menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) { @@ -507,9 +512,31 @@ private void selectNavigationItem(final MenuItem menuItem) { } } + private void startPhotoSearch(MenuItem menuItem) { + SearchEvent searchEvent = new SearchEvent("image/%", + SearchRemoteOperation.SearchType.PHOTO_SEARCH, + SearchEvent.UnsetType.NO_UNSET); + + Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setAction(Intent.ACTION_SEARCH); + intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent)); + intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId()); + startActivity(intent); + } + private void handleSearchEvents(SearchEvent searchEvent, int menuItemId) { if (this instanceof FileDisplayActivity) { - EventBus.getDefault().post(searchEvent); + if (((FileDisplayActivity) this).getListOfFilesFragment() instanceof PhotoFragment) { + Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setAction(Intent.ACTION_SEARCH); + intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent)); + intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId); + startActivity(intent); + } else { + EventBus.getDefault().post(searchEvent); + } } else { Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 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 a6d5b154002d..a3f7a22613d6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -76,6 +76,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.RestoreFileVersionRemoteOperation; +import com.owncloud.android.lib.resources.files.SearchRemoteOperation; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OwnCloudVersion; @@ -101,12 +102,14 @@ import com.owncloud.android.ui.asynctasks.FetchRemoteFileTask; import com.owncloud.android.ui.dialog.SendShareDialog; import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; +import com.owncloud.android.ui.events.SearchEvent; 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.FileFragment; import com.owncloud.android.ui.fragment.OCFileListFragment; +import com.owncloud.android.ui.fragment.PhotoFragment; import com.owncloud.android.ui.fragment.TaskRetainerFragment; import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; @@ -564,7 +567,21 @@ protected void onNewIntent(Intent intent) { if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); setIntent(intent); - Log_OC.w(TAG, "Ignored Intent requesting to query for " + query); + + SearchEvent searchEvent = Parcels.unwrap(intent.getParcelableExtra(OCFileListFragment.SEARCH_EVENT)); + if (SearchRemoteOperation.SearchType.PHOTO_SEARCH.equals(searchEvent.searchType)) { + Log_OC.d(this, "Switch to photo search fragment"); + + PhotoFragment photoFragment = new PhotoFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent)); + photoFragment.setArguments(bundle); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.left_fragment_container, photoFragment, TAG_LIST_OF_FILES); + transaction.commit(); + } else { + 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(); @@ -2556,6 +2573,25 @@ public void showFiles(boolean onDeviceOnly) { getListOfFilesFragment().refreshDirectory(); } + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(final SearchEvent event) { + Fragment fragment; + + if (SearchRemoteOperation.SearchType.PHOTO_SEARCH == event.searchType) { + Log_OC.d(this, "Switch to photo search fragment"); + + fragment = new PhotoFragment(); + } else { + Log_OC.d(this, "Switch to OCFileListFragment"); + + fragment = new OCFileListFragment(); + } + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.add(R.id.left_fragment_container, fragment, TAG_LIST_OF_FILES); + transaction.commit(); + } + @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(SyncEventFinished event) { Bundle bundle = event.getIntent().getExtras(); diff --git a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index fb127ec007bd..18be2ae4f925 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -65,7 +65,7 @@ public class LocalFileListAdapter extends RecyclerView.Adapter mFiles = new ArrayList<>(); private List mFilesAll = new ArrayList<>(); private boolean mLocalFolderPicker; - private boolean gridView; + private boolean gridView = false; private LocalFileListFragmentInterface localFileListFragmentInterface; private Set checkedFiles; diff --git a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 929f6291af7a..d398deef90f9 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -31,6 +31,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; @@ -41,6 +42,7 @@ import android.widget.Filter; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; @@ -109,6 +111,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter mFiles = new ArrayList<>(); private List mFilesAll = new ArrayList<>(); private boolean mHideItemOptions; + private long lastTimestamp; private boolean gridView; private boolean multiSelect; private Set checkedFiles; @@ -305,7 +308,12 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceof OCFileListFooterViewHolder) { - ((OCFileListFooterViewHolder) holder).footerText.setText(getFooterText()); + OCFileListFooterViewHolder footerViewHolder = (OCFileListFooterViewHolder) holder; + footerViewHolder.footerText.setText(getFooterText()); + footerViewHolder.progressBar.getIndeterminateDrawable().setColorFilter(ThemeUtils.primaryColor(mContext), + PorterDuff.Mode.SRC_IN); + footerViewHolder.progressBar.setVisibility( + ocFileListFragmentInterface.isLoading() ? View.VISIBLE : View.GONE); } else { OCFileListGridImageViewHolder gridViewHolder = (OCFileListGridImageViewHolder) holder; @@ -581,8 +589,10 @@ private void setThumbnail(OCFile file, ImageView thumbnailView) { thumbnailView.setBackgroundColor(mContext.getResources().getColor(R.color.background_color)); } } else { - thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), - account, mContext)); + thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), + file.getFileName(), + account, + mContext)); } } } @@ -719,11 +729,31 @@ public void swapDirectory( public void setData(List objects, ExtendedListFragment.SearchType searchType, - FileDataStorageManager storageManager, OCFile folder) { + FileDataStorageManager storageManager, OCFile folder, boolean clear) { if (storageManager != null && mStorageManager == null) { mStorageManager = storageManager; } - mFiles.clear(); + + if (clear) { + mFiles.clear(); + resetLastTimestamp(); + preferences.setPhotoSearchTimestamp(0); + + VirtualFolderType type; + switch (searchType) { + case FAVORITE_SEARCH: + type = VirtualFolderType.FAVORITE; + break; + case PHOTO_SEARCH: + type = VirtualFolderType.PHOTOS; + break; + default: + type = VirtualFolderType.NONE; + break; + } + + mStorageManager.deleteVirtuals(type); + } // early exit if (objects.size() > 0 && mStorageManager != null) { @@ -741,7 +771,7 @@ public void setData(List objects, ExtendedListFragment.SearchType search FileSortOrder sortOrder = preferences.getSortOrderByFolder(folder); mFiles = sortOrder.sortCloudFiles(mFiles); } else { - mFiles = FileStorageUtils.sortOcFolderDescDateModified(mFiles); + mFiles = FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(mFiles); } mFilesAll.clear(); @@ -793,6 +823,7 @@ private void parseShares(List objects) { private void parseVirtuals(List objects, ExtendedListFragment.SearchType searchType) { VirtualFolderType type; boolean onlyImages = false; + switch (searchType) { case FAVORITE_SEARCH: type = VirtualFolderType.FAVORITE; @@ -800,14 +831,22 @@ private void parseVirtuals(List objects, ExtendedListFragment.SearchType case PHOTO_SEARCH: type = VirtualFolderType.PHOTOS; onlyImages = true; + + int lastPosition = objects.size() - 1; + + if (lastPosition < 0) { + lastTimestamp = -1; + break; + } + + RemoteFile lastFile = (RemoteFile) objects.get(lastPosition); + lastTimestamp = lastFile.getModifiedTimestamp() / 1000; break; default: type = VirtualFolderType.NONE; break; } - mStorageManager.deleteVirtuals(type); - List contentValues = new ArrayList<>(); for (Object remoteFile : objects) { @@ -815,14 +854,24 @@ private void parseVirtuals(List objects, ExtendedListFragment.SearchType FileStorageUtils.searchForLocalFileInDefaultPath(ocFile, account); try { - ocFile = mStorageManager.saveFileWithParent(ocFile, mContext); - - // also sync folder content - if (ocFile.isFolder()) { - long currentSyncTime = System.currentTimeMillis(); - RemoteOperation refreshFolderOperation = new RefreshFolderOperation(ocFile, currentSyncTime, false, - false, mStorageManager, account, mContext); - refreshFolderOperation.execute(account, mContext); + if (ExtendedListFragment.SearchType.PHOTO_SEARCH == searchType) { + mStorageManager.saveFile(ocFile); + } else { + + ocFile = mStorageManager.saveFileWithParent(ocFile, mContext); + + // also sync folder content + if (ocFile.isFolder()) { + long currentSyncTime = System.currentTimeMillis(); + RemoteOperation refreshFolderOperation = new RefreshFolderOperation(ocFile, + currentSyncTime, + false, + false, + mStorageManager, + account, + mContext); + refreshFolderOperation.execute(account, mContext); + } } if (!onlyImages || MimeTypeUtil.isImage(ocFile)) { @@ -839,9 +888,23 @@ private void parseVirtuals(List objects, ExtendedListFragment.SearchType } } + preferences.setPhotoSearchTimestamp(System.currentTimeMillis()); mStorageManager.saveVirtuals(type, contentValues); } + public void showVirtuals(VirtualFolderType type, boolean onlyImages, FileDataStorageManager storageManager) { + mFiles = storageManager.getVirtualFolderContent(type, onlyImages); + + if (VirtualFolderType.PHOTOS == type) { + mFiles = FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(mFiles); + } + + mFilesAll.clear(); + mFilesAll.addAll(mFiles); + + new Handler(Looper.getMainLooper()).post(this::notifyDataSetChanged); + } + public void setSortOrder(OCFile folder, FileSortOrder sortOrder) { preferences.setSortOrder(folder, sortOrder); @@ -873,6 +936,14 @@ public Filter getFilter() { return mFilesFilter; } + public void resetLastTimestamp() { + lastTimestamp = -1; + } + + public long getLastTimestamp() { + return lastTimestamp; + } + @Override public void avatarGenerated(Drawable avatarDrawable, Object callContext) { ((ImageView) callContext).setImageDrawable(avatarDrawable); @@ -1034,6 +1105,9 @@ static class OCFileListFooterViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.footerText) public TextView footerText; + @BindView(R.id.loadingProgressBar) + public ProgressBar progressBar; + private OCFileListFooterViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); diff --git a/src/main/java/com/owncloud/android/ui/asynctasks/PhotoSearchTask.java b/src/main/java/com/owncloud/android/ui/asynctasks/PhotoSearchTask.java new file mode 100644 index 000000000000..b476b778426b --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/asynctasks/PhotoSearchTask.java @@ -0,0 +1,128 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2019 Tobias Kaminsky + * Copyright (C) 2019 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ + +package com.owncloud.android.ui.asynctasks; + +import android.accounts.Account; +import android.os.AsyncTask; + +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.files.SearchRemoteOperation; +import com.owncloud.android.ui.activity.ToolbarActivity; +import com.owncloud.android.ui.adapter.OCFileListAdapter; +import com.owncloud.android.ui.fragment.ExtendedListFragment; +import com.owncloud.android.ui.fragment.PhotoFragment; + +import java.lang.ref.WeakReference; + +public class PhotoSearchTask extends AsyncTask { + + private int columnCount; + private Account account; + private WeakReference photoFragmentWeakReference; + private SearchRemoteOperation searchRemoteOperation; + private FileDataStorageManager storageManager; + + public PhotoSearchTask(int columnsCount, + PhotoFragment photoFragment, + Account account, + SearchRemoteOperation searchRemoteOperation, + FileDataStorageManager storageManager) { + this.columnCount = columnsCount; + this.account = account; + this.photoFragmentWeakReference = new WeakReference<>(photoFragment); + this.searchRemoteOperation = searchRemoteOperation; + this.storageManager = storageManager; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + + if (photoFragmentWeakReference.get() == null) { + return; + } + PhotoFragment photoFragment = photoFragmentWeakReference.get(); + photoFragment.setPhotoSearchQueryRunning(true); + } + + @Override + protected RemoteOperationResult doInBackground(Void... voids) { + if (photoFragmentWeakReference.get() == null) { + return new RemoteOperationResult(new Exception("Photo fragment is null")); + } + PhotoFragment photoFragment = photoFragmentWeakReference.get(); + OCFileListAdapter adapter = photoFragment.getAdapter(); + + if (isCancelled()) { + return new RemoteOperationResult(new Exception("Cancelled")); + } else { + int limit = 15 * columnCount; + + long timestamp = -1; + if (adapter.getLastTimestamp() > 0) { + timestamp = adapter.getLastTimestamp(); + } + + searchRemoteOperation.setLimit(limit); + searchRemoteOperation.setTimestamp(timestamp); + + return searchRemoteOperation.execute(account, photoFragment.requireContext()); + } + } + + @Override + protected void onPostExecute(RemoteOperationResult result) { + if (photoFragmentWeakReference.get() != null) { + PhotoFragment photoFragment = photoFragmentWeakReference.get(); + + if (result.isSuccess() && result.getData() != null && !isCancelled()) { + if (result.getData() == null || result.getData().size() == 0) { + photoFragment.setSearchDidNotFindNewPhotos(true); + } else { + OCFileListAdapter adapter = photoFragment.getAdapter(); + + adapter.setData(result.getData(), + ExtendedListFragment.SearchType.PHOTO_SEARCH, + storageManager, + null, + false); + adapter.notifyDataSetChanged(); + Log_OC.d(this, "Search: count: " + result.getData().size() + " total: " + adapter.getFiles().size()); + } + } + + final ToolbarActivity fileDisplayActivity = (ToolbarActivity) photoFragment.getActivity(); + + if (fileDisplayActivity != null) { + fileDisplayActivity.setIndeterminate(false); + } + + if (!result.isSuccess() && !isCancelled()) { + photoFragment.setEmptyListMessage(ExtendedListFragment.SearchType.PHOTO_SEARCH); + } + + photoFragment.setPhotoSearchQueryRunning(false); + } + } +} diff --git a/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java index 153d25f55c5f..5d47214ece88 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java @@ -657,8 +657,10 @@ public void run() { mEmptyListMessage.setText(message); if (tintIcon) { - mEmptyListIcon.setImageDrawable(ThemeUtils.tintDrawable(icon, - ThemeUtils.primaryColor(getContext(), true))); + if (getContext() != null) { + mEmptyListIcon.setImageDrawable( + ThemeUtils.tintDrawable(icon, ThemeUtils.primaryColor(getContext(), true))); + } } else { mEmptyListIcon.setImageResource(icon); } 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 a9ea757fa877..6cfce9d753ae 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -137,7 +137,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileListBottomSheetActions, Injectable { - private static final String TAG = OCFileListFragment.class.getSimpleName(); + protected static final String TAG = OCFileListFragment.class.getSimpleName(); private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment"; @@ -160,7 +160,7 @@ public class OCFileListFragment extends ExtendedListFragment implements private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE"; - private static final String KEY_CURRENT_SEARCH_TYPE = "CURRENT_SEARCH_TYPE"; + protected static final String KEY_CURRENT_SEARCH_TYPE = "CURRENT_SEARCH_TYPE"; private static final String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER"; private static final String DIALOG_CREATE_DOCUMENT = "DIALOG_CREATE_DOCUMENT"; @@ -169,36 +169,36 @@ public class OCFileListFragment extends ExtendedListFragment implements @Inject AppPreferences preferences; @Inject UserAccountManager accountManager; - private FileFragment.ContainerActivity mContainerActivity; + protected FileFragment.ContainerActivity mContainerActivity; - private OCFile mFile; - private OCFileListAdapter mAdapter; - private boolean mOnlyFoldersClickable; - private boolean mFileSelectable; + protected OCFile mFile; + protected OCFileListAdapter mAdapter; + protected boolean mOnlyFoldersClickable; + protected boolean mFileSelectable; - private int mSystemBarActionModeColor; - private int mSystemBarColor; - private int mProgressBarActionModeColor; - private int mProgressBarColor; + protected int mSystemBarActionModeColor; + protected int mSystemBarColor; + protected int mProgressBarActionModeColor; + protected int mProgressBarColor; - private boolean mHideFab = true; - private ActionMode mActiveActionMode; - private OCFileListFragment.MultiChoiceModeListener mMultiChoiceModeListener; + protected boolean mHideFab = true; + protected ActionMode mActiveActionMode; + protected OCFileListFragment.MultiChoiceModeListener mMultiChoiceModeListener; - private SearchType currentSearchType; - private boolean searchFragment; - private SearchEvent searchEvent; - private AsyncTask remoteOperationAsyncTask; - private String mLimitToMimeType; + protected SearchType currentSearchType; + protected boolean searchFragment; + protected SearchEvent searchEvent; + protected AsyncTask remoteOperationAsyncTask; + protected String mLimitToMimeType; @Inject DeviceInfo deviceInfo; - private enum MenuItemAddRemove { + protected enum MenuItemAddRemove { DO_NOTHING, REMOVE_SORT, REMOVE_GRID_AND_SORT, ADD_SORT, ADD_GRID_AND_SORT, ADD_GRID_AND_SORT_WITH_SEARCH, REMOVE_SEARCH } - private MenuItemAddRemove menuItemAddRemoveValue = MenuItemAddRemove.DO_NOTHING; + protected MenuItemAddRemove menuItemAddRemoveValue = MenuItemAddRemove.DO_NOTHING; private List mOriginalMenuItems = new ArrayList<>(); @@ -237,6 +237,7 @@ public void onResume() { super.onResume(); } + /** * {@inheritDoc} */ @@ -303,6 +304,10 @@ public void onDetach() { public void onPause() { super.onPause(); mAdapter.cancelAllPendingTasks(); + + if (getActivity() != null) { + getActivity().getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT); + } } @@ -346,7 +351,11 @@ public void onActivityCreated(Bundle savedInstanceState) { registerFabListener(); } - searchEvent = Parcels.unwrap(getArguments().getParcelable(OCFileListFragment.SEARCH_EVENT)); + if (getArguments() == null) { + searchEvent = null; + } else { + searchEvent = Parcels.unwrap(getArguments().getParcelable(OCFileListFragment.SEARCH_EVENT)); + } prepareCurrentSearch(searchEvent); if (isGridViewPreferred(getCurrentFile())) { @@ -356,25 +365,25 @@ public void onActivityCreated(Bundle savedInstanceState) { setTitle(); } - private void prepareCurrentSearch(SearchEvent event) { + protected void prepareCurrentSearch(SearchEvent event) { if (isSearchEventSet(event)) { - if (SearchRemoteOperation.SearchType.FILE_SEARCH.equals(event.getSearchType())) { - currentSearchType = SearchType.FILE_SEARCH; - - } else if (SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH.equals(event.getSearchType())) { - if ("image/%".equals(event.getSearchQuery())) { - currentSearchType = SearchType.PHOTO_SEARCH; - } else if ("video/%".equals(event.getSearchQuery())) { - currentSearchType = SearchType.VIDEO_SEARCH; - } - } else if (SearchRemoteOperation.SearchType.FAVORITE_SEARCH.equals(event.getSearchType())) { - currentSearchType = SearchType.FAVORITE_SEARCH; - } else if (SearchRemoteOperation.SearchType.RECENTLY_ADDED_SEARCH.equals(event.getSearchType())) { - currentSearchType = SearchType.RECENTLY_ADDED_SEARCH; - } else if (SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH.equals(event.getSearchType())) { - currentSearchType = SearchType.RECENTLY_MODIFIED_SEARCH; - } else if (SearchRemoteOperation.SearchType.SHARED_SEARCH.equals(event.getSearchType())) { - currentSearchType = SearchType.SHARED_FILTER; + + switch (event.getSearchType()) { + case FILE_SEARCH: + currentSearchType = SearchType.FILE_SEARCH; + break; + + case FAVORITE_SEARCH: + currentSearchType = SearchType.FAVORITE_SEARCH; + break; + + case RECENTLY_MODIFIED_SEARCH: + currentSearchType = SearchType.RECENTLY_MODIFIED_SEARCH; + break; + + default: + // do nothing + break; } prepareActionBarItems(event); @@ -656,7 +665,7 @@ public void loadStateFrom(Bundle savedInstanceState) { /** * Init listener that will handle interactions in multiple selection mode. */ - private void setChoiceModeAsMultipleModal(Bundle savedInstanceState) { + protected void setChoiceModeAsMultipleModal(Bundle savedInstanceState) { if (savedInstanceState != null) { mMultiChoiceModeListener.loadStateFrom(savedInstanceState); } @@ -1326,9 +1335,6 @@ private void setTitle() { case FAVORITE_SEARCH: setTitle(R.string.drawer_item_favorites); break; - case PHOTO_SEARCH: - setTitle(R.string.drawer_item_photos); - break; case VIDEO_SEARCH: setTitle(R.string.drawer_item_videos); break; @@ -1349,22 +1355,20 @@ private void setTitle() { } - private void prepareActionBarItems(SearchEvent event) { + protected void prepareActionBarItems(SearchEvent event) { if (event != null) { - if (SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH.equals(event.getSearchType())) { - if ("image/%".equals(event.getSearchQuery())) { - menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT; - } else if ("video/%".equals(event.getSearchQuery())) { - menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH; - } - } else if (SearchRemoteOperation.SearchType.FAVORITE_SEARCH.equals(event.getSearchType())) { - menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT; - } else if (SearchRemoteOperation.SearchType.RECENTLY_ADDED_SEARCH.equals(event.getSearchType())) { - menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT; - } else if (SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH.equals(event.getSearchType())) { - menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT; - } else if (SearchRemoteOperation.SearchType.SHARED_SEARCH.equals(event.getSearchType())) { - menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH; + switch (event.getSearchType()) { + case FAVORITE_SEARCH: + menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT; + break; + + case RECENTLY_MODIFIED_SEARCH: + menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT; + break; + + default: + // do nothing + break; } } @@ -1374,24 +1378,23 @@ private void prepareActionBarItems(SearchEvent event) { } private void setEmptyView(SearchEvent event) { - if (event != null) { - if (SearchRemoteOperation.SearchType.FILE_SEARCH == event.getSearchType()) { - setEmptyListMessage(SearchType.FILE_SEARCH); - } else if (event.getSearchType() == SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH) { - if ("image/%".equals(event.getSearchQuery())) { - setEmptyListMessage(SearchType.PHOTO_SEARCH); - } else if ("video/%".equals(event.getSearchQuery())) { - setEmptyListMessage(SearchType.VIDEO_SEARCH); - } - } else if (SearchRemoteOperation.SearchType.FAVORITE_SEARCH == event.getSearchType()) { - setEmptyListMessage(SearchType.FAVORITE_SEARCH); - } else if (SearchRemoteOperation.SearchType.RECENTLY_ADDED_SEARCH == event.getSearchType()) { - setEmptyListMessage(SearchType.RECENTLY_ADDED_SEARCH); - } else if (SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH == event.getSearchType()) { - setEmptyListMessage(SearchType.RECENTLY_MODIFIED_SEARCH); - } else if (SearchRemoteOperation.SearchType.SHARED_SEARCH == event.getSearchType()) { - setEmptyListMessage(SearchType.SHARED_FILTER); + switch (event.getSearchType()) { + case FILE_SEARCH: + setEmptyListMessage(SearchType.FILE_SEARCH); + break; + + case FAVORITE_SEARCH: + setEmptyListMessage(SearchType.FAVORITE_SEARCH); + break; + + case RECENTLY_MODIFIED_SEARCH: + setEmptyListMessage(SearchType.RECENTLY_MODIFIED_SEARCH); + break; + + default: + setEmptyListMessage(SearchType.NO_SEARCH); + break; } } } @@ -1446,10 +1449,14 @@ public void onMessageEvent(FavoriteEvent event) { @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(final SearchEvent event) { + if (SearchRemoteOperation.SearchType.PHOTO_SEARCH == event.searchType) { + return; + } + prepareCurrentSearch(event); searchFragment = true; setEmptyListLoadingMessage(); - mAdapter.setData(new ArrayList<>(), SearchType.NO_SEARCH, mContainerActivity.getStorageManager(), mFile); + mAdapter.setData(new ArrayList<>(), SearchType.NO_SEARCH, mContainerActivity.getStorageManager(), mFile, true); setFabVisible(false); @@ -1467,11 +1474,7 @@ public void onMessageEvent(final SearchEvent event) { } }; - if (currentSearchType == SearchType.PHOTO_SEARCH) { - new Handler(Looper.getMainLooper()).post(this::switchToGridView); - } else { - new Handler(Looper.getMainLooper()).post(switchViewsRunnable); - } + new Handler(Looper.getMainLooper()).post(switchViewsRunnable); final Account currentAccount = accountManager.getCurrentAccount(); @@ -1501,11 +1504,15 @@ protected Object doInBackground(Object[] params) { } if (remoteOperationResult.isSuccess() && remoteOperationResult.getData() != null - && !isCancelled() && searchFragment) { + && !isCancelled() && searchFragment) { if (remoteOperationResult.getData() == null || remoteOperationResult.getData().size() == 0) { setEmptyView(event); } else { - mAdapter.setData(remoteOperationResult.getData(), currentSearchType, storageManager, mFile); + mAdapter.setData(remoteOperationResult.getData(), + currentSearchType, + storageManager, + mFile, + true); searchEvent = event; } @@ -1573,28 +1580,22 @@ public void onMessageEvent(EncryptionEvent event) { } } - private void setTitle(@StringRes final int title) { - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - if (getActivity() != null && ((FileDisplayActivity) getActivity()).getSupportActionBar() != null) { - ThemeUtils.setColoredTitle(((FileDisplayActivity) getActivity()).getSupportActionBar(), - title, getContext()); - } + protected void setTitle(@StringRes final int title) { + getActivity().runOnUiThread(() -> { + if (getActivity() != null && ((FileDisplayActivity) getActivity()).getSupportActionBar() != null) { + ThemeUtils.setColoredTitle(((FileDisplayActivity) getActivity()).getSupportActionBar(), + title, getContext()); } }); } - private void setTitle(final String title) { - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - if (getActivity() != null) { - ActionBar actionBar = ((FileDisplayActivity) getActivity()).getSupportActionBar(); + protected void setTitle(final String title) { + getActivity().runOnUiThread(() -> { + if (getActivity() != null) { + ActionBar actionBar = ((FileDisplayActivity) getActivity()).getSupportActionBar(); - if (actionBar != null) { - ThemeUtils.setColoredTitle(actionBar, title, getContext()); - } + if (actionBar != null) { + ThemeUtils.setColoredTitle(actionBar, title, getContext()); } } }); @@ -1669,4 +1670,9 @@ private boolean isSearchEventSet(SearchEvent event) { event.searchType == SearchRemoteOperation.SearchType.SHARED_SEARCH) && event.getUnsetType() != null; } + + @Override + public boolean isLoading() { + return false; + } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/PhotoFragment.java b/src/main/java/com/owncloud/android/ui/fragment/PhotoFragment.java new file mode 100644 index 000000000000..e6a71fdef746 --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/fragment/PhotoFragment.java @@ -0,0 +1,182 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2019 Tobias Kaminsky + * Copyright (C) 2019 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ + +package com.owncloud.android.ui.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.owncloud.android.datamodel.VirtualFolderType; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.files.SearchRemoteOperation; +import com.owncloud.android.ui.asynctasks.PhotoSearchTask; +import com.owncloud.android.ui.events.ChangeMenuEvent; +import com.owncloud.android.ui.events.SearchEvent; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +/** + * A Fragment that lists all files and folders in a given path. TODO refactor to get rid of direct dependency on + * FileDisplayActivity + */ +public class PhotoFragment extends OCFileListFragment { + private static final int MAX_ITEMS_PER_ROW = 10; + private boolean photoSearchQueryRunning = false; + private boolean photoSearchNoNew = false; + private SearchRemoteOperation searchRemoteOperation; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = super.onCreateView(inflater, container, savedInstanceState); + + getRecyclerView().addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NotNull RecyclerView recyclerView, int dx, int dy) { + loadMoreWhenEndReached(recyclerView, dy); + } + }); + + Log_OC.i(this, "onCreateView() in PhotoFragment end"); + return v; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + currentSearchType = SearchType.PHOTO_SEARCH; + + switchToGridView(); + + menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT; + requireActivity().invalidateOptionsMenu(); + + handleSearchEvent(searchEvent, false); + } + + @Override + public void onRefresh() { + super.onRefresh(); + + handleSearchEvent(searchEvent, true); + } + + @Override + public void onMessageEvent(ChangeMenuEvent changeMenuEvent) { + super.onMessageEvent(changeMenuEvent); + } + + private void handleSearchEvent(final SearchEvent event, boolean refresh) { + prepareCurrentSearch(event); + searchFragment = true; + setEmptyListLoadingMessage(); + + if (refresh || preferences.getPhotoSearchTimestamp() == 0 || + System.currentTimeMillis() - preferences.getPhotoSearchTimestamp() >= 30 * 1000) { + mAdapter.setData( + new ArrayList<>(), + SearchType.PHOTO_SEARCH, + mContainerActivity.getStorageManager(), + mFile, + true); + } else { + mAdapter.showVirtuals(VirtualFolderType.PHOTOS, true, mContainerActivity.getStorageManager()); + preferences.setPhotoSearchTimestamp(System.currentTimeMillis()); + + return; + } + + setFabVisible(false); + + if (currentSearchType != SearchType.SHARED_FILTER) { + boolean searchOnlyFolders = false; + if (getArguments() != null && getArguments().getBoolean(ARG_SEARCH_ONLY_FOLDER, false)) { + searchOnlyFolders = true; + } + + searchRemoteOperation = new SearchRemoteOperation(event.getSearchQuery(), + event.getSearchType(), + searchOnlyFolders); + } + + searchAndDisplay(); + } + + private void searchAndDisplay() { + if (!photoSearchQueryRunning && !photoSearchNoNew) { + new PhotoSearchTask(getColumnsCount(), + this, + accountManager.getCurrentAccount(), + searchRemoteOperation, + mContainerActivity.getStorageManager()) + .execute(); + } + } + + public void setPhotoSearchQueryRunning(boolean bool) { + photoSearchQueryRunning = bool; + } + + public void setSearchDidNotFindNewPhotos(boolean noNewPhotos) { + photoSearchNoNew = noNewPhotos; + } + + @Override + public boolean isLoading() { + return !photoSearchNoNew; + } + + private void loadMoreWhenEndReached(@NonNull RecyclerView recyclerView, int dy) { + if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { + GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager(); + + // scroll down + if (dy > 0 && !photoSearchQueryRunning) { + int visibleItemCount = gridLayoutManager.getChildCount(); + int totalItemCount = gridLayoutManager.getItemCount(); + int firstVisibleItem = gridLayoutManager.findFirstCompletelyVisibleItemPosition(); + + if ((totalItemCount - visibleItemCount) <= (firstVisibleItem + MAX_ITEMS_PER_ROW)) { + // Almost reached the end, continue to load new photos + if ((totalItemCount - visibleItemCount) > 0) { + searchAndDisplay(); + } + } + } + } + } +} diff --git a/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java b/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java index 3892fe491881..e4fe44d4875d 100644 --- a/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java +++ b/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java @@ -44,4 +44,6 @@ public interface OCFileListFragmentInterface { void onItemClicked(OCFile file); boolean onLongItemClicked(OCFile file); + + boolean isLoading(); } diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java index 798b8e172c8e..6ec60b989cc0 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java @@ -119,7 +119,7 @@ public PreviewImagePagerAdapter(FragmentManager fragmentManager, VirtualFolderTy mImageFiles = mStorageManager.getVirtualFolderContent(type, true); if (type == VirtualFolderType.PHOTOS) { - mImageFiles = FileStorageUtils.sortOcFolderDescDateModified(mImageFiles); + mImageFiles = FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(mImageFiles); } mObsoleteFragments = new HashSet<>(); diff --git a/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/src/main/java/com/owncloud/android/utils/FileStorageUtils.java index 47085f3106dc..606434ac8424 100644 --- a/src/main/java/com/owncloud/android/utils/FileStorageUtils.java +++ b/src/main/java/com/owncloud/android/utils/FileStorageUtils.java @@ -234,7 +234,7 @@ public static RemoteFile fillRemoteFile(OCFile ocFile) { return file; } - public static List sortOcFolderDescDateModified(List files) { + public static List sortOcFolderDescDateModifiedWithoutFavoritesFirst(List files) { final int multiplier = -1; Collections.sort(files, (o1, o2) -> { @SuppressFBWarnings(value = "Bx", justification = "Would require stepping up API level") @@ -242,6 +242,12 @@ public static List sortOcFolderDescDateModified(List files) { return multiplier * obj1.compareTo(o2.getModificationTimestamp()); }); + return files; + } + + public static List sortOcFolderDescDateModified(List files) { + files = sortOcFolderDescDateModifiedWithoutFavoritesFirst(files); + return FileSortOrder.sortCloudFilesByFavourite(files); } diff --git a/src/main/res/layout/list_footer.xml b/src/main/res/layout/list_footer.xml index 905dd6abc994..c0b85733afa0 100644 --- a/src/main/res/layout/list_footer.xml +++ b/src/main/res/layout/list_footer.xml @@ -1,21 +1,27 @@ + android:showDividers="none"> + + + android:gravity="center" + android:padding="@dimen/standard_padding" + android:textColor="@color/secondaryTextColor" /> - \ No newline at end of file +