summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/DocumentsUI/res/color/item_doc_list_background_activated.xml4
-rw-r--r--packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml12
-rw-r--r--packages/DocumentsUI/res/layout/item_doc_list.xml12
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java77
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/ListItem.java52
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java35
6 files changed, 157 insertions, 35 deletions
diff --git a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
index 90e2b7e653c6..7d7a110864d9 100644
--- a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
+++ b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
@@ -16,10 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:state_focused="true"
- android:color="@color/platform_blue_a200"
- android:alpha="0.1" />
- <item
android:state_activated="true"
android:color="?android:attr/colorAccent"
android:alpha="0.1" />
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index d1243200dd5d..231e110fa09d 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -14,10 +14,16 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background">
+ android:background="@drawable/item_doc_list_background"
+ android:orientation="horizontal">
+
+ <View
+ android:id="@+id/focus_indicator"
+ android:layout_width="4dp"
+ android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
@@ -121,4 +127,4 @@
</LinearLayout>
-</FrameLayout>
+</com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index c576669d8958..085df352bb4a 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -14,10 +14,16 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.DocListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background">
+ android:background="@drawable/item_doc_list_background"
+ android:orientation="horizontal">
+
+ <View
+ android:id="@+id/focus_indicator"
+ android:layout_width="4dp"
+ android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
@@ -131,4 +137,4 @@
</LinearLayout>
-</FrameLayout>
+</com.android.documentsui.DocListItem>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 766268983ccc..a317ef2ea431 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -68,6 +68,7 @@ import android.support.v7.widget.RecyclerView.LayoutManager;
import android.support.v7.widget.RecyclerView.OnItemTouchListener;
import android.support.v7.widget.RecyclerView.RecyclerListener;
import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.support.v7.widget.SimpleItemAnimator;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Formatter;
@@ -79,12 +80,12 @@ import android.util.TypedValue;
import android.view.ActionMode;
import android.view.DragEvent;
import android.view.GestureDetector;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ImageView;
@@ -134,6 +135,7 @@ public class DirectoryFragment extends Fragment {
private Model mModel;
private MultiSelectManager mSelectionManager;
private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
+ private ItemClickListener mItemClickListener = new ItemClickListener();
private View mEmptyView;
private RecyclerView mRecView;
@@ -238,7 +240,7 @@ public class DirectoryFragment extends Fragment {
// TODO: Rather than update columns on layout changes, push this
// code (or something like it) into GridLayoutManager.
mRecView.addOnLayoutChangeListener(
- new OnLayoutChangeListener() {
+ new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(
@@ -251,6 +253,9 @@ public class DirectoryFragment extends Fragment {
}
});
+ // TODO: Restore transition animations. See b/24802917.
+ ((SimpleItemAnimator) mRecView.getItemAnimator()).setSupportsChangeAnimations(false);
+
// TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
if (DEBUG_ENABLE_DND) {
setupDragAndDropOnDirectoryView(mRecView);
@@ -450,7 +455,10 @@ public class DirectoryFragment extends Fragment {
}
private boolean onSingleTapUp(MotionEvent e) {
- if (Events.isTouchEvent(e) && mSelectionManager.getSelection().isEmpty()) {
+ // Only respond to touch events. Single-click mouse events are selection events and are
+ // handled by the selection manager. Tap events that occur while the selection manager is
+ // active are also selection events.
+ if (Events.isTouchEvent(e) && !mSelectionManager.hasSelection()) {
int position = getEventAdapterPosition(e);
if (position != RecyclerView.NO_POSITION) {
return handleViewItem(position);
@@ -825,7 +833,7 @@ public class DirectoryFragment extends Fragment {
Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
.setAction(
R.string.undo,
- new android.view.View.OnClickListener() {
+ new View.OnClickListener() {
@Override
public void onClick(View view) {}
})
@@ -889,10 +897,16 @@ public class DirectoryFragment extends Fragment {
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
- private static final class DocumentHolder extends RecyclerView.ViewHolder {
+ private static final class DocumentHolder
+ extends RecyclerView.ViewHolder
+ implements View.OnKeyListener
+ {
// each data item is just a string in this case
public View view;
public String docId; // The stable document id.
+ private ClickListener mClickListener;
+ private View.OnKeyListener mKeyListener;
+
public DocumentHolder(View view) {
super(view);
this.view = view;
@@ -900,6 +914,38 @@ public class DirectoryFragment extends Fragment {
// So we set it here. Note that touch mode focus is a separate issue - see
// View.setFocusableInTouchMode and View.isInTouchMode for more info.
this.view.setFocusable(true);
+ this.view.setOnKeyListener(this);
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // Intercept enter key-up events, and treat them as clicks. Forward other events.
+ if (event.getAction() == KeyEvent.ACTION_UP &&
+ keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (mClickListener != null) {
+ mClickListener.onClick(this);
+ }
+ return true;
+ } else if (mKeyListener != null) {
+ return mKeyListener.onKey(v, keyCode, event);
+ }
+ return false;
+ }
+
+ public void addClickListener(ClickListener listener) {
+ // Just handle one for now; switch to a list if necessary.
+ checkState(mClickListener == null);
+ mClickListener = listener;
+ }
+
+ public void addOnKeyListener(View.OnKeyListener listener) {
+ // Just handle one for now; switch to a list if necessary.
+ checkState(mKeyListener == null);
+ mKeyListener = listener;
+ }
+
+ interface ClickListener {
+ public void onClick(DocumentHolder doc);
}
}
@@ -952,10 +998,11 @@ public class DirectoryFragment extends Fragment {
default:
throw new IllegalStateException("Unsupported layout mode.");
}
- // Key event bubbling doesn't work properly, so instead of setting one key listener on
- // the RecyclerView, we have to set it on each Item. See b/24865023.
- item.setOnKeyListener(mSelectionManager);
- return new DocumentHolder(item);
+
+ DocumentHolder holder = new DocumentHolder(item);
+ holder.addClickListener(mItemClickListener);
+ holder.addOnKeyListener(mSelectionManager);
+ return holder;
}
@Override
@@ -1957,6 +2004,18 @@ public class DirectoryFragment extends Fragment {
}
}
+ private class ItemClickListener implements DocumentHolder.ClickListener {
+ @Override
+ public void onClick(DocumentHolder doc) {
+ final int position = doc.getAdapterPosition();
+ if (mSelectionManager.hasSelection()) {
+ mSelectionManager.toggleSelection(position);
+ } else {
+ handleViewItem(position);
+ }
+ }
+ }
+
private class ModelUpdateListener extends Model.UpdateListener {
@Override
public void onModelUpdate(Model model) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ListItem.java b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
new file mode 100644
index 000000000000..5c40f1b20d74
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Layout for a single item in List mode. This class overrides the default focus listener in order
+ * to light up a focus indicator when it is focused.
+ */
+public class ListItem extends LinearLayout
+{
+ public ListItem(Context context) {
+ super(context);
+ }
+
+ public ListItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ View indicator = findViewById(R.id.focus_indicator);
+ if (gainFocus) {
+ TypedValue color = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.colorAccent, color, true);
+ indicator.setBackgroundColor(color.data);
+ } else {
+ indicator.setBackgroundColor(android.R.color.transparent);
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index 87c037dcd738..b9c4da11ca09 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
@@ -183,6 +183,10 @@ public final class MultiSelectManager implements View.OnKeyListener {
mCallbacks.add(callback);
}
+ public boolean hasSelection() {
+ return !mSelection.isEmpty();
+ }
+
/**
* Returns a Selection object that provides a live view
* on the current selection.
@@ -217,7 +221,7 @@ public final class MultiSelectManager implements View.OnKeyListener {
*/
@VisibleForTesting
public boolean setItemSelected(int position, boolean selected) {
- if (mSingleSelect && !mSelection.isEmpty()) {
+ if (mSingleSelect && hasSelection()) {
clearSelectionQuietly();
}
return setItemsSelected(position, 1, selected);
@@ -263,7 +267,7 @@ public final class MultiSelectManager implements View.OnKeyListener {
private void clearSelectionQuietly() {
mRanger = null;
- if (mSelection.isEmpty()) {
+ if (!hasSelection()) {
return;
}
if (mIntermediateSelection == null) {
@@ -292,7 +296,7 @@ public final class MultiSelectManager implements View.OnKeyListener {
@VisibleForTesting
boolean onSingleTapUp(InputEvent input) {
if (DEBUG) Log.d(TAG, "Processing tap event.");
- if (mSelection.isEmpty()) {
+ if (!hasSelection()) {
// if this is a mouse click on an item, start selection mode.
// TODO: && input.isPrimaryButtonPressed(), but it is returning false.
if (input.isOverItem() && input.isMouseEvent()) {
@@ -335,7 +339,7 @@ public final class MultiSelectManager implements View.OnKeyListener {
*
* @param position
*/
- private void toggleSelection(int position) {
+ void toggleSelection(int position) {
// Position may be special "no position" during certain
// transitional phases. If so, skip handling of the event.
if (position == RecyclerView.NO_POSITION) {
@@ -351,7 +355,7 @@ public final class MultiSelectManager implements View.OnKeyListener {
if (!canSelect) {
return;
}
- if (mSingleSelect && !mSelection.isEmpty()) {
+ if (mSingleSelect && hasSelection()) {
clearSelectionQuietly();
}
@@ -398,7 +402,7 @@ public final class MultiSelectManager implements View.OnKeyListener {
if (selected) {
boolean canSelect = notifyBeforeItemStateChange(i, true);
if (canSelect) {
- if (mSingleSelect && !mSelection.isEmpty()) {
+ if (mSingleSelect && hasSelection()) {
clearSelectionQuietly();
}
selectAndNotify(i);
@@ -1959,7 +1963,11 @@ public final class MultiSelectManager implements View.OnKeyListener {
}
if (searchDir != -1) {
View targetView = view.focusSearch(searchDir);
- target = mEnvironment.getAdapterPositionForChildView(targetView);
+ // TargetView can be null, for example, if the user pressed <down> at the bottom of
+ // the list.
+ if (targetView != null) {
+ target = mEnvironment.getAdapterPositionForChildView(targetView);
+ }
}
}
@@ -1972,18 +1980,13 @@ public final class MultiSelectManager implements View.OnKeyListener {
mEnvironment.focusItem(target);
if (event.isShiftPressed()) {
- if (mSelection.isEmpty()) {
+ if (!hasSelection()) {
// If there is no selection, start a selection when the user presses shift-arrow.
toggleSelection(mEnvironment.getAdapterPositionForChildView(view));
- } else {
- // Deal with b/24802917 (selected items can't be focused) by adjusting the
- // selection sorted the focused item isn't in the selection.
- target -= Integer.signum(target - mRanger.mBegin);
- mRanger.snapSelection(target);
}
- } else if (!event.isShiftPressed() && !mSelection.isEmpty()) {
- // If there is a selection, clear it if the user presses arrow with no shift.
- clearSelection();
+
+ mRanger.snapSelection(target);
+ notifySelectionChanged();
}
return true;