mirror of
https://github.com/nextcloud/android.git
synced 2024-11-22 13:15:35 +03:00
Finish action mode when drawer starts to open, recover it when drawer is closed
# Conflicts: # build.gradle # src/com/owncloud/android/ui/activity/DrawerActivity.java # src/com/owncloud/android/ui/fragment/OCFileListFragment.java
This commit is contained in:
parent
175816b70e
commit
5a97d31392
4 changed files with 297 additions and 66 deletions
|
@ -41,7 +41,6 @@ dependencies {
|
|||
compile "com.android.support:design:${supportLibraryVersion}"
|
||||
compile 'com.jakewharton:disklrucache:2.0.2'
|
||||
compile "com.android.support:appcompat-v7:${supportLibraryVersion}"
|
||||
compile "com.android.support:cardview-v7:${supportLibraryVersion}"
|
||||
compile 'com.getbase:floatingactionbutton:1.10.1'
|
||||
|
||||
/// dependencies for local unit tests
|
||||
|
@ -62,7 +61,10 @@ dependencies {
|
|||
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
|
||||
|
||||
// UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests
|
||||
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
|
||||
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
|
||||
|
||||
// fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details
|
||||
androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}"
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
|
|||
};
|
||||
|
||||
// Set the drawer toggle as the DrawerListener
|
||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||
mDrawerLayout.addDrawerListener(mDrawerToggle);
|
||||
mDrawerToggle.setDrawerIndicatorEnabled(true);
|
||||
}
|
||||
|
||||
|
@ -796,4 +796,17 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds other listeners to react on changes of the drawer layout.
|
||||
*
|
||||
* @param listener Object interested in changes of the drawer layout.
|
||||
*/
|
||||
public void addDrawerListener(DrawerLayout.DrawerListener listener) {
|
||||
if (mDrawerLayout != null) {
|
||||
mDrawerLayout.addDrawerListener(listener);
|
||||
} else {
|
||||
Log_OC.e(TAG, "Drawer layout not ready to add drawer listener");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ import android.content.SharedPreferences;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.ActionMode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
@ -60,6 +62,8 @@ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
|
|||
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
|
||||
import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
|
||||
import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
|
||||
import com.owncloud.android.ui.helpers.SparseBooleanArrayParcelable;
|
||||
import com.owncloud.android.ui.preview.PreviewAudioFragment;
|
||||
import com.owncloud.android.ui.preview.PreviewImageFragment;
|
||||
import com.owncloud.android.ui.preview.PreviewMediaFragment;
|
||||
import com.owncloud.android.ui.preview.PreviewTextFragment;
|
||||
|
@ -107,6 +111,7 @@ public class OCFileListFragment extends ExtendedListFragment {
|
|||
private boolean mHideFab = true;
|
||||
private boolean miniFabClicked = false;
|
||||
private ActionMode mActiveActionMode;
|
||||
private OCFileListFragment.MultiChoiceModeListener mMultiChoiceModeListener;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -151,7 +156,7 @@ public class OCFileListFragment extends ExtendedListFragment {
|
|||
Bundle args = getArguments();
|
||||
boolean allowContextualActions = (args != null) && args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, false);
|
||||
if (allowContextualActions) {
|
||||
setChoiceModeAsMultipleModal();
|
||||
setChoiceModeAsMultipleModal(savedInstanceState);
|
||||
}
|
||||
Log_OC.i(TAG, "onCreateView() end");
|
||||
return v;
|
||||
|
@ -346,76 +351,195 @@ public class OCFileListFragment extends ExtendedListFragment {
|
|||
com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void setChoiceModeAsMultipleModal() {
|
||||
/**
|
||||
* Handler for multiple selection mode.
|
||||
*
|
||||
* Manages input from the user when one or more files or folders are selected in the list.
|
||||
*
|
||||
* Also listens to changes in navigation drawer to hide and recover multiple selection when it's opened
|
||||
* and closed.
|
||||
*/
|
||||
private class MultiChoiceModeListener
|
||||
implements AbsListView.MultiChoiceModeListener, DrawerLayout.DrawerListener {
|
||||
|
||||
setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
private static final String KEY_ACTION_MODE_CLOSED_BY_DRAWER = "KILLED_ACTION_MODE";
|
||||
private static final String KEY_SELECTION_WHEN_CLOSED_BY_DRAWER = "CHECKED_ITEMS";
|
||||
|
||||
setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
||||
/**
|
||||
* True when action mode is finished because the drawer was opened
|
||||
*/
|
||||
private boolean mActionModeClosedByDrawer = false;
|
||||
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
||||
getListView().invalidateViews();
|
||||
mode.invalidate();
|
||||
}
|
||||
/**
|
||||
* Selected items in list when action mode is closed by drawer
|
||||
*/
|
||||
private SparseBooleanArray mSelectionWhenActionModeClosedByDrawer = null;
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mActiveActionMode = mode;
|
||||
@Override
|
||||
public void onDrawerSlide(View drawerView, float slideOffset) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.file_actions_menu, menu);
|
||||
mode.invalidate();
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
//set gray color
|
||||
DisplayUtils.colorStatusBar(getActivity(), mSystemBarActionModeColor);
|
||||
DisplayUtils.colorToolbarProgressBar(getActivity(), mProgressBarActionModeColor);
|
||||
|
||||
// hide FAB in multi selection mode
|
||||
setFabEnabled(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
List<OCFile> checkedFiles = mAdapter.getCheckedItems(getListView());
|
||||
final int checkedCount = checkedFiles.size();
|
||||
String title = getResources().getQuantityString(
|
||||
R.plurals.items_selected_count,
|
||||
checkedCount,
|
||||
checkedCount
|
||||
);
|
||||
mode.setTitle(title);
|
||||
FileMenuFilter mf = new FileMenuFilter(
|
||||
checkedFiles,
|
||||
((FileActivity) getActivity()).getAccount(),
|
||||
mContainerActivity,
|
||||
getActivity()
|
||||
);
|
||||
mf.filter(menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
return onFileActionChosen(item.getItemId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
mActiveActionMode = null;
|
||||
|
||||
// reset to previous color
|
||||
DisplayUtils.colorStatusBar(getActivity(), mSystemBarColor);
|
||||
DisplayUtils.colorToolbarProgressBar(getActivity(), mProgressBarColor);
|
||||
|
||||
// show FAB on multi selection mode exit
|
||||
if(!mHideFab) {
|
||||
setFabEnabled(true);
|
||||
/**
|
||||
* When the navigation drawer is closed, action mode is recovered in the same state as was
|
||||
* when the drawer was (started to be) opened.
|
||||
*
|
||||
* @param drawerView Navigation drawer just closed.
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerClosed(View drawerView) {
|
||||
if (mSelectionWhenActionModeClosedByDrawer !=null && mActionModeClosedByDrawer) {
|
||||
for (int i = 0; i< mSelectionWhenActionModeClosedByDrawer.size(); i++) {
|
||||
if (mSelectionWhenActionModeClosedByDrawer.valueAt(i)) {
|
||||
getListView().setItemChecked(
|
||||
mSelectionWhenActionModeClosedByDrawer.keyAt(i),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mSelectionWhenActionModeClosedByDrawer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the action mode is active when the navigation drawer starts to move, the action
|
||||
* mode is closed and the selection stored to be recovered when the drawer is closed.
|
||||
*
|
||||
* @param newState One of STATE_IDLE, STATE_DRAGGING or STATE_SETTLING.
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerStateChanged(int newState) {
|
||||
if (DrawerLayout.STATE_DRAGGING == newState && mActiveActionMode != null) {
|
||||
mSelectionWhenActionModeClosedByDrawer = getListView().getCheckedItemPositions().clone();
|
||||
mActiveActionMode.finish();
|
||||
mActionModeClosedByDrawer = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update action mode bar when an item is selected / unselected in the list
|
||||
*/
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
||||
getListView().invalidateViews();
|
||||
mode.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load menu and customize UI when action mode is started.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mActiveActionMode = mode;
|
||||
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.file_actions_menu, menu);
|
||||
mode.invalidate();
|
||||
|
||||
//set gray color
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window w = getActivity().getWindow();
|
||||
mStatusBarColor = w.getStatusBarColor();
|
||||
w.setStatusBarColor(mStatusBarColorActionMode);
|
||||
}
|
||||
|
||||
// hide FAB in multi selection mode
|
||||
setFabEnabled(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates available action in menu depending on current selection.
|
||||
*/
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
List<OCFile> checkedFiles = mAdapter.getCheckedItems(getListView());
|
||||
final int checkedCount = checkedFiles.size();
|
||||
String title = getResources().getQuantityString(
|
||||
R.plurals.items_selected_count,
|
||||
checkedCount,
|
||||
checkedCount
|
||||
);
|
||||
mode.setTitle(title);
|
||||
FileMenuFilter mf = new FileMenuFilter(
|
||||
checkedFiles,
|
||||
((FileActivity) getActivity()).getAccount(),
|
||||
mContainerActivity,
|
||||
getActivity()
|
||||
);
|
||||
mf.filter(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the corresponding action when a menu item is tapped by the user.
|
||||
*/
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
return onFileActionChosen(item.getItemId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores UI.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
mActiveActionMode = null;
|
||||
|
||||
// reset to previous color
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getActivity().getWindow().setStatusBarColor(mStatusBarColor);
|
||||
}
|
||||
|
||||
// show FAB on multi selection mode exit
|
||||
if(!mHideFab) {
|
||||
setFabEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void storeStateIn(Bundle outState) {
|
||||
outState.putBoolean(KEY_ACTION_MODE_CLOSED_BY_DRAWER, mActionModeClosedByDrawer);
|
||||
if (mSelectionWhenActionModeClosedByDrawer != null) {
|
||||
SparseBooleanArrayParcelable sbap = new SparseBooleanArrayParcelable(
|
||||
mSelectionWhenActionModeClosedByDrawer
|
||||
);
|
||||
outState.putParcelable(KEY_SELECTION_WHEN_CLOSED_BY_DRAWER, sbap);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadStateFrom(Bundle savedInstanceState) {
|
||||
mActionModeClosedByDrawer = savedInstanceState.getBoolean(
|
||||
KEY_ACTION_MODE_CLOSED_BY_DRAWER,
|
||||
mActionModeClosedByDrawer
|
||||
);
|
||||
SparseBooleanArrayParcelable sbap = savedInstanceState.getParcelable(
|
||||
KEY_SELECTION_WHEN_CLOSED_BY_DRAWER
|
||||
);
|
||||
if (sbap != null) {
|
||||
mSelectionWhenActionModeClosedByDrawer = sbap.getSparseBooleanArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init listener that will handle interactions in multiple selection mode.
|
||||
*/
|
||||
private void setChoiceModeAsMultipleModal(Bundle savedInstanceState) {
|
||||
setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
mMultiChoiceModeListener = new MultiChoiceModeListener();
|
||||
if (savedInstanceState != null) {
|
||||
mMultiChoiceModeListener.loadStateFrom(savedInstanceState);
|
||||
}
|
||||
setMultiChoiceModeListener(mMultiChoiceModeListener);
|
||||
((FileActivity)getActivity()).addDrawerListener(mMultiChoiceModeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -425,6 +549,7 @@ public class OCFileListFragment extends ExtendedListFragment {
|
|||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelable(KEY_FILE, mFile);
|
||||
mMultiChoiceModeListener.storeStateIn(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* ownCloud Android client application
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui.helpers;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
/**
|
||||
* Wraps a SparseBooleanArrayParcelable to allow its serialization and desearialization
|
||||
* through {@link Parcelable} interface.
|
||||
*/
|
||||
public class SparseBooleanArrayParcelable implements Parcelable {
|
||||
|
||||
public static Parcelable.Creator<SparseBooleanArrayParcelable> CREATOR =
|
||||
new Parcelable.Creator<SparseBooleanArrayParcelable>() {
|
||||
|
||||
@Override
|
||||
public SparseBooleanArrayParcelable createFromParcel(Parcel source) {
|
||||
// read size of array from source
|
||||
int size = source.readInt();
|
||||
|
||||
// then pairs of (key, value)s, in the object to wrap
|
||||
SparseBooleanArray sba = new SparseBooleanArray();
|
||||
int key;
|
||||
boolean value;
|
||||
for (int i = 0; i < size; i++) {
|
||||
key = source.readInt();
|
||||
value = (source.readInt() != 0);
|
||||
sba.put(key, value);
|
||||
}
|
||||
|
||||
// wrap SparseBooleanArray
|
||||
return new SparseBooleanArrayParcelable(sba);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SparseBooleanArrayParcelable[] newArray(int size) {
|
||||
return new SparseBooleanArrayParcelable[size];
|
||||
}
|
||||
};
|
||||
|
||||
private final SparseBooleanArray mSba;
|
||||
|
||||
public SparseBooleanArrayParcelable(SparseBooleanArray sba) {
|
||||
if (sba == null) {
|
||||
throw new IllegalArgumentException("Cannot wrap a null SparseBooleanArray");
|
||||
}
|
||||
mSba = sba;
|
||||
}
|
||||
|
||||
public SparseBooleanArray getSparseBooleanArray() {
|
||||
return mSba;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
// first, size of the array
|
||||
dest.writeInt(mSba.size());
|
||||
|
||||
// then, pairs of (key, value)
|
||||
for (int i = 0; i < mSba.size(); i++) {
|
||||
dest.writeInt(mSba.keyAt(i));
|
||||
dest.writeInt(mSba.valueAt(i) ? 1 : 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue