diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2016-06-17 19:01:51 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-06-17 19:01:52 +0000 |
commit | fbfac5861047e772262978ea8084c2fb024f90c6 (patch) | |
tree | 4795790c4273f6e4e1d93e7886a2eb393a144ab6 /packages/DocumentsUI | |
parent | 653d5b2eb2aa5aa51ae4111c8d2ba8d5174fbd0d (diff) | |
parent | 43b62a8eefa10d2460f143e4239d7e0ad153e6ee (diff) |
Merge "Implementing breadcrumb for tablet devices for navigation in the bar."
Diffstat (limited to 'packages/DocumentsUI')
10 files changed, 617 insertions, 268 deletions
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml index f7824c1ebd1e..4898f13d893a 100644 --- a/packages/DocumentsUI/res/layout/drawer_layout.xml +++ b/packages/DocumentsUI/res/layout/drawer_layout.xml @@ -41,8 +41,8 @@ android:theme="?actionBarTheme" android:popupTheme="?actionBarPopupTheme"> - <Spinner - android:id="@+id/stack" + <com.android.documentsui.DropdownBreadcrumb + android:id="@+id/breadcrumb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="4dp" diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml index deb089418fe9..2ea936641243 100644 --- a/packages/DocumentsUI/res/layout/fixed_layout.xml +++ b/packages/DocumentsUI/res/layout/fixed_layout.xml @@ -39,14 +39,10 @@ android:theme="?actionBarTheme" android:popupTheme="?actionBarPopupTheme"> - <Spinner - android:id="@+id/stack" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="4dp" - android:popupTheme="?actionBarPopupTheme" - android:background="@android:color/transparent" - android:overlapAnchor="true" /> + <com.android.documentsui.HorizontalBreadcrumb + android:id="@+id/breadcrumb" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> </com.android.documentsui.DocumentsToolbar> diff --git a/packages/DocumentsUI/res/layout/navigation_breadcrumb_item.xml b/packages/DocumentsUI/res/layout/navigation_breadcrumb_item.xml new file mode 100644 index 000000000000..ab9d3b26d3bc --- /dev/null +++ b/packages/DocumentsUI/res/layout/navigation_breadcrumb_item.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 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. +--> + + +<!-- + CoordinatorLayout is necessary for various components (e.g. Snackbars, and + floating action buttons) to operate correctly. +--> +<!-- + focusableInTouchMode is set in order to force key events to go to the activity's global key + callback, which is necessary for proper event routing. See BaseActivity.onKeyDown. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentTop="true" + android:gravity="center_vertical" + android:orientation="horizontal"> + + <TextView + android:id="@+id/breadcrumb_text" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:textAppearance="@android:style/TextAppearance.Material.Widget.ActionBar.Title" /> + + <ImageView + android:id="@+id/breadcrumb_arrow" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_breadcrumb_arrow" + android:layout_marginTop="2dp" + android:layout_marginRight="3dp" + android:layout_marginLeft="3dp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index 6f17b547f8b4..7e1c53794ab2 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -48,9 +48,9 @@ import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; -import android.widget.Spinner; import com.android.documentsui.MenuManager.DirectoryDetails; +import com.android.documentsui.NavigationViewManager.Breadcrumb; import com.android.documentsui.SearchViewManager.SearchManagerListener; import com.android.documentsui.State.ViewMode; import com.android.documentsui.dirlist.AnimationView; @@ -69,7 +69,7 @@ import java.util.List; import java.util.concurrent.Executor; public abstract class BaseActivity extends Activity - implements SearchManagerListener, NavigationView.Environment { + implements SearchManagerListener, NavigationViewManager.Environment { private static final String BENCHMARK_TESTING_PACKAGE = "com.android.documentsui.appperftests"; @@ -78,7 +78,7 @@ public abstract class BaseActivity extends Activity RootsCache mRoots; SearchViewManager mSearchManager; DrawerController mDrawer; - NavigationView mNavigator; + NavigationViewManager mNavigator; List<EventListener> mEventListeners = new ArrayList<>(); private final String mTag; @@ -141,13 +141,9 @@ public abstract class BaseActivity extends Activity mSearchManager = new SearchViewManager(this, icicle); DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar); + Breadcrumb breadcrumb = (Breadcrumb) findViewById(R.id.breadcrumb); setActionBar(toolbar); - mNavigator = new NavigationView( - mDrawer, - toolbar, - (Spinner) findViewById(R.id.stack), - mState, - this); + mNavigator = new NavigationViewManager(mDrawer, toolbar, mState, this, breadcrumb); // Base classes must update result in their onCreate. setResult(Activity.RESULT_CANCELED); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DropdownBreadcrumb.java b/packages/DocumentsUI/src/com/android/documentsui/DropdownBreadcrumb.java new file mode 100644 index 000000000000..71d7334960e8 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/DropdownBreadcrumb.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2016 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.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import com.android.documentsui.NavigationViewManager.Breadcrumb; +import com.android.documentsui.NavigationViewManager.Environment; +import com.android.documentsui.model.DocumentInfo; +import com.android.documentsui.model.RootInfo; + +import java.util.function.Consumer; + +/** + * Dropdown implementation of breadcrumb used for phone device layouts + */ + +public final class DropdownBreadcrumb extends Spinner implements Breadcrumb { + + private DropdownAdapter mAdapter; + + public DropdownBreadcrumb( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public DropdownBreadcrumb(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DropdownBreadcrumb(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DropdownBreadcrumb(Context context) { + super(context); + } + + @Override + public void setup(Environment env, State state, Consumer<Integer> listener) { + mAdapter = new DropdownAdapter(state, env); + setOnItemSelectedListener( + new OnItemSelectedListener() { + @Override + public void onItemSelected( + AdapterView<?> parent, View view, int position, long id) { + listener.accept(position); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) {} + }); + } + + @Override + public void show(boolean visibility) { + if (visibility) { + setVisibility(VISIBLE); + setAdapter(mAdapter); + } else { + setVisibility(GONE); + setAdapter(null); + } + } + + @Override + public void postUpdate() { + setSelection(mAdapter.getCount() - 1, false); + } + + private static final class DropdownAdapter extends BaseAdapter { + private Environment mEnv; + private State mState; + + public DropdownAdapter(State state, Environment env) { + mState = state; + mEnv = env; + } + + @Override + public int getCount() { + return mState.stack.size(); + } + + @Override + public DocumentInfo getItem(int position) { + return mState.stack.get(mState.stack.size() - position - 1); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_subdir_title, parent, false); + } + + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + final DocumentInfo doc = getItem(position); + + if (position == 0) { + final RootInfo root = mEnv.getCurrentRoot(); + title.setText(root.title); + } else { + title.setText(doc.displayName); + } + + return convertView; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_subdir, parent, false); + } + + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + final DocumentInfo doc = getItem(position); + + if (position == 0) { + final RootInfo root = mEnv.getCurrentRoot(); + title.setText(root.title); + } else { + title.setText(doc.displayName); + } + + return convertView; + } + } + +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/HorizontalBreadcrumb.java b/packages/DocumentsUI/src/com/android/documentsui/HorizontalBreadcrumb.java new file mode 100644 index 000000000000..9f6b79b68daa --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/HorizontalBreadcrumb.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2016 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.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.documentsui.NavigationViewManager.Breadcrumb; +import com.android.documentsui.NavigationViewManager.Environment; +import com.android.documentsui.model.DocumentInfo; +import com.android.documentsui.model.RootInfo; + +import java.util.function.Consumer; + +/** + * Horizontal implementation of breadcrumb used for tablet / desktop device layouts + */ +public final class HorizontalBreadcrumb extends RecyclerView implements Breadcrumb { + + private LinearLayoutManager mLayoutManager; + private BreadcrumbAdapter mAdapter; + private Consumer<Integer> mListener; + + public HorizontalBreadcrumb(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public HorizontalBreadcrumb(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public HorizontalBreadcrumb(Context context) { + super(context); + } + + @Override + public void setup(Environment env, + com.android.documentsui.State state, + Consumer<Integer> listener) { + + mListener = listener; + mLayoutManager = new LinearLayoutManager( + getContext(), LinearLayoutManager.HORIZONTAL, false); + mAdapter = new BreadcrumbAdapter(state, env); + + setLayoutManager(mLayoutManager); + addOnItemTouchListener(new ClickListener(getContext(), this::onSingleTapUp)); + } + + @Override + public void show(boolean visibility) { + if (visibility) { + setVisibility(VISIBLE); + setAdapter(mAdapter); + mLayoutManager.scrollToPosition(mAdapter.getItemCount() - 1); + } else { + setVisibility(GONE); + setAdapter(null); + } + } + + @Override + public void postUpdate() { + } + + private void onSingleTapUp(MotionEvent e) { + View itemView = findChildViewUnder(e.getX(), e.getY()); + int pos = getChildAdapterPosition(itemView); + if (pos != mAdapter.getItemCount() - 1) { + mListener.accept(pos); + } + } + + private static final class BreadcrumbAdapter + extends RecyclerView.Adapter<BreadcrumbHolder> { + + private Environment mEnv; + private com.android.documentsui.State mState; + + public BreadcrumbAdapter(com.android.documentsui.State state, Environment env) { + mState = state; + mEnv = env; + } + + @Override + public BreadcrumbHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.navigation_breadcrumb_item, null); + return new BreadcrumbHolder(v); + } + + @Override + public void onBindViewHolder(BreadcrumbHolder holder, int position) { + final DocumentInfo doc = getItem(position); + + if (position == 0) { + final RootInfo root = mEnv.getCurrentRoot(); + holder.title.setText(root.title); + } else { + holder.title.setText(doc.displayName); + } + + if (position == getItemCount() - 1) { + holder.arrow.setVisibility(View.GONE); + } + } + + private DocumentInfo getItem(int position) { + return mState.stack.get(mState.stack.size() - position - 1); + } + + @Override + public int getItemCount() { + return mState.stack.size(); + } + } + + private static class BreadcrumbHolder extends RecyclerView.ViewHolder { + + protected TextView title; + protected ImageView arrow; + + public BreadcrumbHolder(View itemView) { + super(itemView); + title = (TextView) itemView.findViewById(R.id.breadcrumb_text); + arrow = (ImageView) itemView.findViewById(R.id.breadcrumb_arrow); + } + } + + private static final class ClickListener extends GestureDetector + implements OnItemTouchListener { + + public ClickListener(Context context, Consumer<MotionEvent> listener) { + super(context, new SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(MotionEvent e) { + listener.accept(e); + return true; + } + }); + } + + @Override + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { + onTouchEvent(e); + return false; + } + + @Override + public void onTouchEvent(RecyclerView rv, MotionEvent e) { + onTouchEvent(e); + } + + @Override + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + } + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java deleted file mode 100644 index 3373c23ed212..000000000000 --- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2016 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 static android.view.View.GONE; -import static android.view.View.VISIBLE; -import static com.android.documentsui.Shared.DEBUG; - -import android.annotation.Nullable; -import android.graphics.drawable.Drawable; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.BaseAdapter; -import android.widget.Spinner; -import android.widget.TextView; - -import com.android.documentsui.dirlist.AnimationView; -import com.android.documentsui.model.DocumentInfo; -import com.android.documentsui.model.RootInfo; - -/** - * A facade over the portions of the app and drawer toolbars. - */ -class NavigationView { - - private static final String TAG = "NavigationView"; - - private final DrawerController mDrawer; - private final DocumentsToolbar mToolbar; - private final Spinner mBreadcrumb; - private final State mState; - private final NavigationView.Environment mEnv; - private final BreadcrumbAdapter mBreadcrumbAdapter; - - private boolean mIgnoreNextNavigation; - - public NavigationView( - DrawerController drawer, - DocumentsToolbar toolbar, - Spinner breadcrumb, - State state, - NavigationView.Environment env) { - - mToolbar = toolbar; - mBreadcrumb = breadcrumb; - mDrawer = drawer; - mState = state; - mEnv = env; - - mBreadcrumbAdapter = new BreadcrumbAdapter(mState, mEnv); - mToolbar.setNavigationOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - onNavigationIconClicked(); - } - - }); - - mBreadcrumb.setOnItemSelectedListener( - new OnItemSelectedListener() { - @Override - public void onItemSelected( - AdapterView<?> parent, View view, int position, long id) { - onBreadcrumbItemSelected(position); - } - - @Override - public void onNothingSelected(AdapterView<?> parent) {} - }); - - } - - private void onNavigationIconClicked() { - if (mDrawer.isPresent()) { - mDrawer.setOpen(true, DrawerController.OPENED_HAMBURGER); - } - } - - private void onBreadcrumbItemSelected(int position) { - if (mIgnoreNextNavigation) { - mIgnoreNextNavigation = false; - return; - } - - while (mState.stack.size() > position + 1) { - mState.popDocument(); - } - mEnv.refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE); - } - - void update() { - - // TODO: Looks to me like this block is never getting hit. - if (mEnv.isSearchExpanded()) { - mToolbar.setTitle(null); - mBreadcrumb.setVisibility(View.GONE); - mBreadcrumb.setAdapter(null); - return; - } - - mDrawer.setTitle(mEnv.getDrawerTitle()); - - mToolbar.setNavigationIcon(getActionBarIcon()); - mToolbar.setNavigationContentDescription(R.string.drawer_open); - - if (mState.stack.size() <= 1) { - showBreadcrumb(false); - String title = mEnv.getCurrentRoot().title; - if (DEBUG) Log.d(TAG, "New toolbar title is: " + title); - mToolbar.setTitle(title); - } else { - showBreadcrumb(true); - mToolbar.setTitle(null); - mIgnoreNextNavigation = true; - mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1, false); - } - - if (DEBUG) Log.d(TAG, "Final toolbar title is: " + mToolbar.getTitle()); - } - - private void showBreadcrumb(boolean visibility) { - if (visibility) { - mBreadcrumb.setVisibility(VISIBLE); - mBreadcrumb.setAdapter(mBreadcrumbAdapter); - } else { - mBreadcrumb.setVisibility(GONE); - mBreadcrumb.setAdapter(null); - } - } - - // Hamburger if drawer is present, else sad nullness. - private @Nullable Drawable getActionBarIcon() { - if (mDrawer.isPresent()) { - return mToolbar.getContext().getDrawable(R.drawable.ic_hamburger); - } else { - return null; - } - } - - void revealRootsDrawer(boolean open) { - mDrawer.setOpen(open); - } - - /** - * Class providing toolbar with runtime access to useful activity data. - */ - static final class BreadcrumbAdapter extends BaseAdapter { - - private Environment mEnv; - private State mState; - - public BreadcrumbAdapter(State state, Environment env) { - mState = state; - mEnv = env; - } - - @Override - public int getCount() { - return mState.stack.size(); - } - - @Override - public DocumentInfo getItem(int position) { - return mState.stack.get(mState.stack.size() - position - 1); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_subdir_title, parent, false); - } - - final TextView title = (TextView) convertView.findViewById(android.R.id.title); - final DocumentInfo doc = getItem(position); - - if (position == 0) { - final RootInfo root = mEnv.getCurrentRoot(); - title.setText(root.title); - } else { - title.setText(doc.displayName); - } - - return convertView; - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_subdir, parent, false); - } - - final TextView title = (TextView) convertView.findViewById(android.R.id.title); - final DocumentInfo doc = getItem(position); - - if (position == 0) { - final RootInfo root = mEnv.getCurrentRoot(); - title.setText(root.title); - } else { - title.setText(doc.displayName); - } - - return convertView; - } - } - - interface Environment { - RootInfo getCurrentRoot(); - String getDrawerTitle(); - void refreshCurrentRootAndDirectory(int animation); - boolean isSearchExpanded(); - } -} diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationViewManager.java new file mode 100644 index 000000000000..25542461748a --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationViewManager.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2016 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 static com.android.documentsui.Shared.DEBUG; + +import android.annotation.Nullable; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.View; + +import com.android.documentsui.dirlist.AnimationView; +import com.android.documentsui.model.RootInfo; + +import java.util.function.Consumer; + +/** + * A facade over the portions of the app and drawer toolbars. + */ +public class NavigationViewManager { + + private static final String TAG = "NavigationViewManager"; + + final DrawerController mDrawer; + final DocumentsToolbar mToolbar; + final State mState; + final NavigationViewManager.Environment mEnv; + final Breadcrumb mBreadcrumb; + + public NavigationViewManager( + DrawerController drawer, + DocumentsToolbar toolbar, + State state, + NavigationViewManager.Environment env, + Breadcrumb breadcrumb) { + + mToolbar = toolbar; + mDrawer = drawer; + mState = state; + mEnv = env; + mBreadcrumb = breadcrumb; + mBreadcrumb.setup(env, state, this::onNavigationItemSelected); + + mToolbar.setNavigationOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + onNavigationIconClicked(); + } + }); + } + + private void onNavigationIconClicked() { + if (mDrawer.isPresent()) { + mDrawer.setOpen(true, DrawerController.OPENED_HAMBURGER); + } + } + + void onNavigationItemSelected(int position) { + boolean changed = false; + while (mState.stack.size() > position + 1) { + changed = true; + mState.popDocument(); + } + if (changed) { + mEnv.refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE); + } + } + + void update() { + + // TODO: Looks to me like this block is never getting hit. + if (mEnv.isSearchExpanded()) { + mToolbar.setTitle(null); + mBreadcrumb.show(false); + return; + } + + mDrawer.setTitle(mEnv.getDrawerTitle()); + + mToolbar.setNavigationIcon(getActionBarIcon()); + mToolbar.setNavigationContentDescription(R.string.drawer_open); + + if (mState.stack.size() <= 1) { + mBreadcrumb.show(false); + String title = mEnv.getCurrentRoot().title; + if (DEBUG) Log.d(TAG, "New toolbar title is: " + title); + mToolbar.setTitle(title); + } else { + mBreadcrumb.show(true); + mToolbar.setTitle(null); + mBreadcrumb.postUpdate(); + } + + if (DEBUG) Log.d(TAG, "Final toolbar title is: " + mToolbar.getTitle()); + } + + // Hamburger if drawer is present, else sad nullness. + private @Nullable Drawable getActionBarIcon() { + if (mDrawer.isPresent()) { + return mToolbar.getContext().getDrawable(R.drawable.ic_hamburger); + } else { + return null; + } + } + + void revealRootsDrawer(boolean open) { + mDrawer.setOpen(open); + } + + interface Breadcrumb { + void setup(Environment env, State state, Consumer<Integer> listener); + void show(boolean visibility); + void postUpdate(); + } + + interface Environment { + RootInfo getCurrentRoot(); + String getDrawerTitle(); + void refreshCurrentRootAndDirectory(int animation); + boolean isSearchExpanded(); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java index 1688f35afd6e..2c0a8a8db0ab 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java @@ -49,6 +49,8 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { @Override public void initTestFiles() throws RemoteException { + mDocsHelper.createFolder(rootDir0, dirName1); + mDocsHelper.createDocument(rootDir0, "text/plain", "file0.log"); mDocsHelper.createDocument(rootDir0, "image/png", "file1.png"); mDocsHelper.createDocument(rootDir0, "text/csv", "file2.csv"); @@ -124,6 +126,30 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { bots.directory.assertDocumentsPresent("Kung Fu Panda"); } + public void testOpenBreadcrumb() throws Exception { + initTestFiles(); + + bots.roots.openRoot(ROOT_0_ID); + + bots.directory.openDocument(dirName1); + if (bots.main.isTablet()) { + openBreadcrumbTabletHelper(); + } else { + openBreadcrumbPhoneHelper(); + } + bots.main.assertBreadcrumbItemsPresent(dirName1, "TEST_ROOT_0"); + bots.main.clickBreadcrumbItem("TEST_ROOT_0"); + + bots.directory.assertDocumentsPresent(dirName1); + } + + private void openBreadcrumbTabletHelper() throws Exception { + } + + private void openBreadcrumbPhoneHelper() throws Exception { + bots.main.clickDropdownBreadcrumb(); + } + public void testDeleteDocument() throws Exception { initTestFiles(); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java index 15f176b0e9c3..7484ed9c168d 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java @@ -17,20 +17,20 @@ package com.android.documentsui.bots; import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.withResourceName; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; import static android.support.test.espresso.action.ViewActions.typeText; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withResourceName; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; -import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.is; import android.content.Context; import android.content.res.Configuration; @@ -41,21 +41,22 @@ import android.support.test.uiautomator.UiObject; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObjectNotFoundException; import android.support.test.uiautomator.UiSelector; -import android.support.test.uiautomator.Until; -import android.support.v7.widget.SearchView; -import android.util.Log; import android.widget.ImageView; import android.widget.Spinner; import android.widget.Toolbar; -import junit.framework.AssertionFailedError; import com.android.documentsui.R; import com.android.documentsui.model.DocumentInfo; import com.android.internal.view.menu.ActionMenuItemView; +import junit.framework.Assert; +import junit.framework.AssertionFailedError; + import org.hamcrest.Description; import org.hamcrest.Matcher; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -80,7 +81,10 @@ public class UiBot extends BaseBot { } public void assertBreadcrumbTitle(String expected) { - onView(isAssignableFrom(Spinner.class)).check(matches(withBreadcrumbTitle(is(expected)))); + if (!isTablet()) { + onView(allOf(isAssignableFrom(Spinner.class), withId(R.id.breadcrumb))) + .check(matches(withBreadcrumbTitle(is(expected)))); + } } public void assertMenuEnabled(int id, boolean enabled) { @@ -208,6 +212,43 @@ public class UiBot extends BaseBot { : findObject("com.android.documentsui:id/menu_search", "android:id/search_button"); } + public void clickBreadcrumbItem(String label) throws UiObjectNotFoundException { + if (isTablet()) { + findBreadcrumb(label).click(); + } else { + findMenuWithName(label).click(); + } + } + + public void clickDropdownBreadcrumb() throws UiObjectNotFoundException { + assertFalse(isTablet()); + onView(isAssignableFrom(Spinner.class)).perform(click()); + } + + public UiObject findBreadcrumb(String label) throws UiObjectNotFoundException { + final UiSelector breadcrumbList = new UiSelector().resourceId( + "com.android.documentsui:id/breadcrumb"); + + // Wait for the first list item to appear + new UiObject(breadcrumbList.childSelector(new UiSelector())).waitForExists(mTimeout); + + return mDevice.findObject(breadcrumbList.childSelector(new UiSelector().text(label))); + } + + public void assertBreadcrumbItemsPresent(String... labels) throws UiObjectNotFoundException { + List<String> absent = new ArrayList<>(); + for (String label : labels) { + // For non-Tablet devices, a dropdown List menu is shown instead + if (isTablet() ? !findBreadcrumb(label).exists() : findMenuWithName(label) == null) { + absent.add(label); + } + } + if (!absent.isEmpty()) { + Assert.fail("Expected documents " + Arrays.asList(labels) + + ", but missing " + absent); + } + } + UiObject findActionModeBar() { return findObject("android:id/action_mode_bar"); } |