diff options
17 files changed, 504 insertions, 361 deletions
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 75f8b59..6ab810c 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -128,6 +128,9 @@ public class LinkProperties implements Parcelable { return interfaceNames; } + /** + * Returns all the addresses on this link. + */ public Collection<InetAddress> getAddresses() { Collection<InetAddress> addresses = new ArrayList<InetAddress>(); for (LinkAddress linkAddress : mLinkAddresses) { @@ -136,14 +139,43 @@ public class LinkProperties implements Parcelable { return Collections.unmodifiableCollection(addresses); } + /** + * Returns all the addresses on this link and all the links stacked above it. + */ + public Collection<InetAddress> getAllAddresses() { + Collection<InetAddress> addresses = new ArrayList<InetAddress>(); + for (LinkAddress linkAddress : mLinkAddresses) { + addresses.add(linkAddress.getAddress()); + } + for (LinkProperties stacked: mStackedLinks.values()) { + addresses.addAll(stacked.getAllAddresses()); + } + return addresses; + } + public void addLinkAddress(LinkAddress address) { if (address != null) mLinkAddresses.add(address); } + /** + * Returns all the addresses on this link. + */ public Collection<LinkAddress> getLinkAddresses() { return Collections.unmodifiableCollection(mLinkAddresses); } + /** + * Returns all the addresses on this link and all the links stacked above it. + */ + public Collection<LinkAddress> getAllLinkAddresses() { + Collection<LinkAddress> addresses = new ArrayList<LinkAddress>(); + addresses.addAll(mLinkAddresses); + for (LinkProperties stacked: mStackedLinks.values()) { + addresses.addAll(stacked.getAllLinkAddresses()); + } + return addresses; + } + public void addDns(InetAddress dns) { if (dns != null) mDnses.add(dns); } @@ -426,13 +458,11 @@ public class LinkProperties implements Parcelable { } /** - * Return two lists, a list of addresses that would be removed from - * mLinkAddresses and a list of addresses that would be added to - * mLinkAddress which would then result in target and mLinkAddresses - * being the same list. + * Compares the addresses in this LinkProperties with another + * LinkProperties, examining only addresses on the base link. * - * @param target is a LinkProperties with the new list of addresses - * @return the removed and added lists. + * @param target a LinkProperties with the new list of addresses + * @return the differences between the addresses. */ public CompareResult<LinkAddress> compareAddresses(LinkProperties target) { /* @@ -456,13 +486,11 @@ public class LinkProperties implements Parcelable { } /** - * Return two lists, a list of dns addresses that would be removed from - * mDnses and a list of addresses that would be added to - * mDnses which would then result in target and mDnses - * being the same list. + * Compares the DNS addresses in this LinkProperties with another + * LinkProperties, examining only DNS addresses on the base link. * - * @param target is a LinkProperties with the new list of dns addresses - * @return the removed and added lists. + * @param target a LinkProperties with the new list of dns addresses + * @return the differences between the DNS addresses. */ public CompareResult<InetAddress> compareDnses(LinkProperties target) { /* @@ -487,15 +515,13 @@ public class LinkProperties implements Parcelable { } /** - * Return two lists, a list of routes that would be removed from - * mRoutes and a list of routes that would be added to - * mRoutes which would then result in target and mRoutes - * being the same list. + * Compares all routes in this LinkProperties with another LinkProperties, + * examining both the the base link and all stacked links. * - * @param target is a LinkProperties with the new list of routes - * @return the removed and added lists. + * @param target a LinkProperties with the new list of routes + * @return the differences between the routes. */ - public CompareResult<RouteInfo> compareRoutes(LinkProperties target) { + public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) { /* * Duplicate the RouteInfos into removed, we will be removing * routes which are common between mRoutes and target diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java index d6a7ee2..d8290f4 100644 --- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java +++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java @@ -273,28 +273,47 @@ public class LinkPropertiesTest extends TestCase { // Check comparisons work. LinkProperties lp2 = new LinkProperties(lp); assertAllRoutesHaveInterface("wlan0", lp); - assertEquals(0, lp.compareRoutes(lp2).added.size()); - assertEquals(0, lp.compareRoutes(lp2).removed.size()); + assertEquals(0, lp.compareAllRoutes(lp2).added.size()); + assertEquals(0, lp.compareAllRoutes(lp2).removed.size()); lp2.setInterfaceName("p2p0"); assertAllRoutesHaveInterface("p2p0", lp2); - assertEquals(3, lp.compareRoutes(lp2).added.size()); - assertEquals(3, lp.compareRoutes(lp2).removed.size()); + assertEquals(3, lp.compareAllRoutes(lp2).added.size()); + assertEquals(3, lp.compareAllRoutes(lp2).removed.size()); } @SmallTest public void testStackedInterfaces() { LinkProperties rmnet0 = new LinkProperties(); rmnet0.setInterfaceName("rmnet0"); + rmnet0.addLinkAddress(new LinkAddress( + NetworkUtils.numericToInetAddress(ADDRV6), 128)); LinkProperties clat4 = new LinkProperties(); clat4.setInterfaceName("clat4"); + clat4.addLinkAddress(new LinkAddress( + NetworkUtils.numericToInetAddress(ADDRV4), 32)); assertEquals(0, rmnet0.getStackedLinks().size()); + assertEquals(1, rmnet0.getAddresses().size()); + assertEquals(1, rmnet0.getLinkAddresses().size()); + assertEquals(1, rmnet0.getAllAddresses().size()); + assertEquals(1, rmnet0.getAllLinkAddresses().size()); + rmnet0.addStackedLink(clat4); assertEquals(1, rmnet0.getStackedLinks().size()); + assertEquals(1, rmnet0.getAddresses().size()); + assertEquals(1, rmnet0.getLinkAddresses().size()); + assertEquals(2, rmnet0.getAllAddresses().size()); + assertEquals(2, rmnet0.getAllLinkAddresses().size()); + rmnet0.addStackedLink(clat4); assertEquals(1, rmnet0.getStackedLinks().size()); + assertEquals(1, rmnet0.getAddresses().size()); + assertEquals(1, rmnet0.getLinkAddresses().size()); + assertEquals(2, rmnet0.getAllAddresses().size()); + assertEquals(2, rmnet0.getAllLinkAddresses().size()); + assertEquals(0, clat4.getStackedLinks().size()); // Modify an item in the returned collection to see what happens. diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk index 1e45807..853353d 100644 --- a/packages/DocumentsUI/Android.mk +++ b/packages/DocumentsUI/Android.mk @@ -5,6 +5,8 @@ LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 + LOCAL_PACKAGE_NAME := DocumentsUI LOCAL_CERTIFICATE := platform diff --git a/packages/DocumentsUI/res/drawable-hdpi/drawer_shadow.9.png b/packages/DocumentsUI/res/drawable-hdpi/drawer_shadow.9.png Binary files differnew file mode 100644 index 0000000..224cc4f --- /dev/null +++ b/packages/DocumentsUI/res/drawable-hdpi/drawer_shadow.9.png diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer.png Binary files differnew file mode 100644 index 0000000..ff7b1de --- /dev/null +++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer.png diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search.png Binary files differnew file mode 100644 index 0000000..cc661e3 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search.png diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/activity.xml index f96d459..eb6d803 100644 --- a/packages/DocumentsUI/res/layout/activity.xml +++ b/packages/DocumentsUI/res/layout/activity.xml @@ -14,20 +14,34 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/drawer_layout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> + android:layout_height="match_parent"> - <FrameLayout - android:id="@+id/directory" + <LinearLayout android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="1" /> + android:layout_height="match_parent" + android:orientation="vertical"> - <FrameLayout - android:id="@+id/save" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> + <FrameLayout + android:id="@+id/directory" + android:layout_width="match_parent" + android:layout_height="0dip" + android:layout_weight="1" /> + + <FrameLayout + android:id="@+id/save" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + </LinearLayout> + + <ListView + android:id="@+id/roots_list" + android:layout_width="300dp" + android:layout_height="match_parent" + android:layout_gravity="start" + android:background="#fff" /> -</LinearLayout> +</android.support.v4.widget.DrawerLayout> diff --git a/packages/DocumentsUI/res/layout/item_backend.xml b/packages/DocumentsUI/res/layout/item_backend.xml deleted file mode 100644 index 6ec7566..0000000 --- a/packages/DocumentsUI/res/layout/item_backend.xml +++ /dev/null @@ -1,58 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 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. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="?android:attr/listPreferredItemPaddingEnd" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> - - <FrameLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@color/chip" - android:foreground="?android:attr/selectableItemBackground" - android:duplicateParentState="true"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:padding="8dp" - android:orientation="horizontal"> - - <ImageView - android:id="@android:id/icon" - android:layout_width="24dip" - android:layout_height="24dip" - android:layout_marginEnd="8dp" - android:scaleType="centerInside" - android:contentDescription="@null" /> - - <TextView - android:id="@android:id/text1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textAlignment="viewStart" /> - - </LinearLayout> - - </FrameLayout> - -</FrameLayout> diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml new file mode 100644 index 0000000..e9cf3aa --- /dev/null +++ b/packages/DocumentsUI/res/layout/item_root.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:gravity="center_vertical" + android:orientation="horizontal"> + + <ImageView + android:id="@android:id/icon" + android:layout_width="@android:dimen/app_icon_size" + android:layout_height="@android:dimen/app_icon_size" + android:layout_rowSpan="2" + android:layout_marginEnd="8dip" + android:scaleType="centerInside" + android:contentDescription="@null" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textAlignment="viewStart" /> + + <TextView + android:id="@android:id/summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textAlignment="viewStart" /> + + </LinearLayout> + +</LinearLayout> diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_title.xml new file mode 100644 index 0000000..fe6c14d --- /dev/null +++ b/packages/DocumentsUI/res/layout/item_title.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="vertical"> + + <TextView + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textAlignment="viewStart" /> + + <TextView + android:id="@android:id/summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textAlignment="viewStart" /> + +</LinearLayout> diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml index bf7c161..a0d03b2 100644 --- a/packages/DocumentsUI/res/menu/activity.xml +++ b/packages/DocumentsUI/res/menu/activity.xml @@ -19,5 +19,12 @@ android:id="@+id/menu_create_dir" android:title="@string/menu_create_dir" android:icon="@drawable/ic_menu_create_dir" - android:showAsAction="always" /> + android:showAsAction="ifRoom" /> + <item + android:id="@+id/menu_search" + android:title="@string/menu_search" + android:icon="@drawable/ic_menu_search" + android:showAsAction="always|collapseActionView" + android:actionViewClass="android.widget.SearchView" + android:imeOptions="actionSearch" /> </menu> diff --git a/packages/DocumentsUI/res/menu/directory.xml b/packages/DocumentsUI/res/menu/directory.xml index c1fa228..12d0324 100644 --- a/packages/DocumentsUI/res/menu/directory.xml +++ b/packages/DocumentsUI/res/menu/directory.xml @@ -25,9 +25,4 @@ android:title="@string/menu_list" android:icon="@drawable/ic_menu_list" android:showAsAction="ifRoom" /> - <item - android:id="@+id/menu_sort" - android:title="@string/menu_sort" - android:icon="@drawable/ic_menu_sort" - android:showAsAction="ifRoom" /> </menu> diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index 6ae2d12..18e486d 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -17,20 +17,24 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label">Documents</string> - <string name="title_open">Open</string> - <string name="title_save">Save</string> + <string name="title_open">Open from</string> + <string name="title_save">Save to</string> <string name="menu_create_dir">Create folder</string> <string name="menu_grid">Grid view</string> <string name="menu_list">List view</string> <string name="menu_sort">Sort by</string> + <string name="menu_search">Search</string> <string name="menu_open">Open</string> <string name="menu_save">Save</string> <string name="mode_selected_count"><xliff:g id="count" example="3">%1$d</xliff:g> selected</string> - <string name="sort_name">Name</string> - <string name="sort_date">Date modified</string> + <string name="sort_name">By name</string> + <string name="sort_date">By date modified</string> + + <string name="drawer_open">Open navigation drawer</string> + <string name="drawer_close">Close navigation drawer</string> </resources> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BackendFragment.java b/packages/DocumentsUI/src/com/android/documentsui/BackendFragment.java deleted file mode 100644 index fc13487..0000000 --- a/packages/DocumentsUI/src/com/android/documentsui/BackendFragment.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2013 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.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ProviderInfo; -import android.content.res.Resources.NotFoundException; -import android.database.Cursor; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Bundle; -import android.provider.DocumentsContract; -import android.provider.DocumentsContract.RootColumns; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.GridView; -import android.widget.ImageView; -import android.widget.TextView; - -import com.google.android.collect.Lists; - -import java.util.List; - -/** - * Display all known storage roots. - */ -public class BackendFragment extends Fragment { - - // TODO: cluster backends by type - - private GridView mGridView; - private BackendAdapter mAdapter; - - public static void show(FragmentManager fm) { - final BackendFragment fragment = new BackendFragment(); - - final FragmentTransaction ft = fm.beginTransaction(); - ft.replace(R.id.directory, fragment); - ft.setBreadCrumbTitle("TOP"); - ft.commitAllowingStateLoss(); - } - - public static class Root { - public int rootType; - public Uri uri; - public Drawable icon; - public String title; - public String summary; - - public static Root fromCursor(Context context, ProviderInfo info, Cursor cursor) { - final Root root = new Root(); - - root.rootType = cursor.getInt(cursor.getColumnIndex(RootColumns.ROOT_TYPE)); - root.uri = DocumentsContract.buildDocumentUri( - info.authority, cursor.getString(cursor.getColumnIndex(RootColumns.GUID))); - - final PackageManager pm = context.getPackageManager(); - final int icon = cursor.getInt(cursor.getColumnIndex(RootColumns.ICON)); - if (icon != 0) { - try { - root.icon = pm.getResourcesForApplication(info.applicationInfo) - .getDrawable(icon); - } catch (NotFoundException e) { - throw new RuntimeException(e); - } catch (NameNotFoundException e) { - throw new RuntimeException(e); - } - } else { - root.icon = info.loadIcon(pm); - } - - root.title = cursor.getString(cursor.getColumnIndex(RootColumns.TITLE)); - if (root.title == null) { - root.title = info.loadLabel(pm).toString(); - } - - root.summary = cursor.getString(cursor.getColumnIndex(RootColumns.SUMMARY)); - - return root; - } - } - - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final Context context = inflater.getContext(); - - // Gather roots from known storage providers - final List<ProviderInfo> providers = context.getPackageManager() - .queryContentProviders(null, -1, PackageManager.GET_META_DATA); - final List<Root> roots = Lists.newArrayList(); - for (ProviderInfo info : providers) { - if (info.metaData != null - && info.metaData.containsKey(DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) { - // TODO: populate roots on background thread, and cache results - final Uri uri = DocumentsContract.buildRootsUri(info.authority); - final Cursor cursor = context.getContentResolver() - .query(uri, null, null, null, null); - try { - while (cursor.moveToNext()) { - roots.add(Root.fromCursor(context, info, cursor)); - } - } finally { - cursor.close(); - } - } - } - - final View view = inflater.inflate(R.layout.fragment_backend, container, false); - - mGridView = (GridView) view.findViewById(R.id.grid); - mGridView.setOnItemClickListener(mItemListener); - - mAdapter = new BackendAdapter(context, roots); - mGridView.setAdapter(mAdapter); - mGridView.setNumColumns(GridView.AUTO_FIT); - - return view; - } - - private OnItemClickListener mItemListener = new OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - final Root root = mAdapter.getItem(position); - ((DocumentsActivity) getActivity()).onRootPicked(root); - } - }; - - public static class BackendAdapter extends ArrayAdapter<Root> { - public BackendAdapter(Context context, List<Root> list) { - super(context, android.R.layout.simple_list_item_1, list); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_backend, parent, false); - } - - final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); - final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1); - - final PackageManager pm = parent.getContext().getPackageManager(); - final Root root = getItem(position); - icon.setImageDrawable(root.icon); - text1.setText(root.title); - - return convertView; - } - } -} diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 531eaf3..8b3dd99 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -16,17 +16,12 @@ package com.android.documentsui; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.LoaderManager.LoaderCallbacks; import android.content.Context; import android.content.CursorLoader; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Loader; import android.database.Cursor; import android.net.Uri; @@ -65,8 +60,6 @@ public class DirectoryFragment extends Fragment { // TODO: show storage backend in item views when requested - private static final String TAG_SORT = "sort"; - private ListView mListView; private GridView mGridView; @@ -81,7 +74,8 @@ public class DirectoryFragment extends Fragment { private static final int LOADER_DOCUMENTS = 2; - public static void show(FragmentManager fm, Uri uri, String displayName) { + public static void show( + FragmentManager fm, Uri uri, String displayName, boolean addToBackStack) { final Bundle args = new Bundle(); args.putParcelable(EXTRA_URI, uri); @@ -90,7 +84,9 @@ public class DirectoryFragment extends Fragment { final FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.directory, fragment); - ft.addToBackStack(displayName); + if (addToBackStack) { + ft.addToBackStack(displayName); + } ft.setBreadCrumbTitle(displayName); ft.commitAllowingStateLoss(); } @@ -136,7 +132,13 @@ public class DirectoryFragment extends Fragment { sortOrder = null; } - final Uri contentsUri = DocumentsContract.buildContentsUri(uri); + final Uri contentsUri; + if (uri.getQueryParameter(DocumentsContract.PARAM_QUERY) != null) { + contentsUri = uri; + } else { + contentsUri = DocumentsContract.buildContentsUri(uri); + } + return new CursorLoader(context, contentsUri, null, null, null, sortOrder); } @@ -198,9 +200,6 @@ public class DirectoryFragment extends Fragment { updateMode(); getFragmentManager().invalidateOptionsMenu(); return true; - } else if (id == R.id.menu_sort) { - SortFragment.show(this); - return true; } else { return super.onOptionsItemSelected(item); } @@ -238,7 +237,7 @@ public class DirectoryFragment extends Fragment { } } - private void updateSortBy() { + public void updateSortBy() { getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks); } @@ -358,38 +357,6 @@ public class DirectoryFragment extends Fragment { } } - public static class SortFragment extends DialogFragment { - public static void show(DirectoryFragment parent) { - if (!parent.isAdded()) return; - - final SortFragment dialog = new SortFragment(); - dialog.setTargetFragment(parent, 0); - dialog.show(parent.getFragmentManager(), TAG_SORT); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Context context = getActivity(); - final DisplayState state = getDisplayState(this); - - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.menu_sort); - builder.setSingleChoiceItems(new CharSequence[] { - getText(R.string.sort_name), - getText(R.string.sort_date), - }, state.sortBy, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - state.sortBy = which; - ((DirectoryFragment) getTargetFragment()).updateSortBy(); - dismiss(); - } - }); - - return builder.create(); - } - } - private static int getDocumentFlags(Context context, Uri uri) { final Cursor cursor = context.getContentResolver().query(uri, new String[] { DocumentColumns.FLAGS }, null, null, null); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index e61cea6..13def57 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -25,7 +25,6 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.FragmentManager; -import android.app.FragmentManager.BackStackEntry; import android.app.FragmentManager.OnBackStackChangedListener; import android.content.ClipData; import android.content.ContentResolver; @@ -35,25 +34,42 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; +import android.content.res.Resources.NotFoundException; import android.database.Cursor; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.DocumentColumns; +import android.provider.DocumentsContract.RootColumns; +import android.support.v4.app.ActionBarDrawerToggle; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v4.widget.DrawerLayout.DrawerListener; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.SearchView; +import android.widget.SearchView.OnQueryTextListener; import android.widget.TextView; -import com.android.documentsui.BackendFragment.Root; +import com.google.android.collect.Lists; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -63,18 +79,30 @@ public class DocumentsActivity extends Activity { // TODO: fragment to show recently opened documents // TODO: pull actionbar icon from current backend + private static final String TAG_CREATE_DIRECTORY = "create_directory"; + private static final int ACTION_OPEN = 1; private static final int ACTION_CREATE = 2; private int mAction; private String[] mAcceptMimes; + private SearchView mSearchView; + + private DrawerLayout mDrawerLayout; + private ActionBarDrawerToggle mDrawerToggle; + + private ArrayList<Root> mRoots = Lists.newArrayList(); + private RootsAdapter mRootsAdapter; + private ListView mRootsList; + private final DisplayState mDisplayState = new DisplayState(); - private boolean mIgnoreNextNavigation; + private Root mCurrentRoot; private Uri mCurrentDir; private boolean mCurrentSupportsCreate; + private boolean mCurrentSupportsSearch; @Override public void onCreate(Bundle icicle) { @@ -106,40 +134,91 @@ public class DocumentsActivity extends Activity { setContentView(R.layout.activity); getFragmentManager().addOnBackStackChangedListener(mStackListener); - BackendFragment.show(getFragmentManager()); - - updateActionBar(); if (mAction == ACTION_CREATE) { final String mimeType = getIntent().getType(); final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE); SaveFragment.show(getFragmentManager(), mimeType, title); } + + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + mRootsAdapter = new RootsAdapter(this, mRoots); + mRootsList = (ListView) findViewById(R.id.roots_list); + mRootsList.setAdapter(mRootsAdapter); + mRootsList.setOnItemClickListener(mRootsListener); + + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, + R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close); + + mDrawerLayout.setDrawerListener(mDrawerListener); + mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + + mDrawerLayout.openDrawer(mRootsList); + + updateActionBar(); + updateRoots(); + } + + private DrawerListener mDrawerListener = new DrawerListener() { + @Override + public void onDrawerSlide(View drawerView, float slideOffset) { + mDrawerToggle.onDrawerSlide(drawerView, slideOffset); + } + + @Override + public void onDrawerOpened(View drawerView) { + mDrawerToggle.onDrawerOpened(drawerView); + updateActionBar(); + } + + @Override + public void onDrawerClosed(View drawerView) { + mDrawerToggle.onDrawerClosed(drawerView); + updateActionBar(); + } + + @Override + public void onDrawerStateChanged(int newState) { + mDrawerToggle.onDrawerStateChanged(newState); + } + }; + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + mDrawerToggle.syncState(); } public void updateActionBar() { final FragmentManager fm = getFragmentManager(); final ActionBar actionBar = getActionBar(); - if (fm.getBackStackEntryCount() > 0) { - actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - actionBar.setDisplayShowHomeEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setTitle(null); - actionBar.setListNavigationCallbacks(mStackAdapter, mNavigationListener); - actionBar.setSelectedNavigationItem(mStackAdapter.getCount() - 1); - mIgnoreNextNavigation = true; + actionBar.setDisplayShowHomeEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); - } else { + if (mDrawerLayout.isDrawerOpen(mRootsList)) { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); - actionBar.setDisplayShowHomeEnabled(false); - actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setIcon(new ColorDrawable()); if (mAction == ACTION_OPEN) { actionBar.setTitle(R.string.title_open); } else if (mAction == ACTION_CREATE) { actionBar.setTitle(R.string.title_save); } + + } else { + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + if (mCurrentRoot != null) { + actionBar.setIcon(mCurrentRoot.icon); + } + actionBar.setTitle(null); + actionBar.setListNavigationCallbacks(mSortAdapter, mSortListener); + + if (fm.getBackStackEntryCount() > 0) { + mDrawerToggle.setDrawerIndicatorEnabled(false); + } else { + mDrawerToggle.setDrawerIndicatorEnabled(true); + } } } @@ -147,6 +226,25 @@ public class DocumentsActivity extends Activity { public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.activity, menu); + + final MenuItem searchMenu = menu.findItem(R.id.menu_search); + mSearchView = (SearchView) searchMenu.getActionView(); + mSearchView.setOnQueryTextListener(new OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + // TODO: clear existing directory stack? + final Uri searchUri = DocumentsContract.buildSearchUri(mCurrentDir, query); + DirectoryFragment.show(getFragmentManager(), searchUri, query, true); + mSearchView.setIconified(true); + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + return false; + } + }); + return true; } @@ -158,17 +256,29 @@ public class DocumentsActivity extends Activity { createDir.setVisible(mAction == ACTION_CREATE); createDir.setEnabled(mCurrentSupportsCreate); + // TODO: close any search in-progress when hiding + final MenuItem search = menu.findItem(R.id.menu_search); + search.setVisible(mCurrentSupportsSearch); + return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { + if (mDrawerToggle.onOptionsItemSelected(item)) { + return true; + } + final int id = item.getItemId(); if (id == android.R.id.home) { getFragmentManager().popBackStack(); updateActionBar(); + return true; } else if (id == R.id.menu_create_dir) { CreateDirectoryFragment.show(getFragmentManager()); + return true; + } else if (id == R.id.menu_search) { + return false; } return super.onOptionsItemSelected(item); } @@ -180,46 +290,73 @@ public class DocumentsActivity extends Activity { } }; - private BaseAdapter mStackAdapter = new BaseAdapter() { + // TODO: support additional sort orders + private BaseAdapter mSortAdapter = new BaseAdapter() { @Override public int getCount() { - return getFragmentManager().getBackStackEntryCount(); + return 2; } @Override public Object getItem(int position) { - return getFragmentManager().getBackStackEntryAt(position); + switch (position) { + case 0: + return getText(R.string.sort_name); + case 1: + return getText(R.string.sort_date); + default: + return null; + } } @Override public long getItemId(int position) { - return getFragmentManager().getBackStackEntryAt(position).getId(); + return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_title, parent, false); + } + + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); + + final FragmentManager fm = getFragmentManager(); + final int count = fm.getBackStackEntryCount(); + if (count > 0) { + title.setText(fm.getBackStackEntryAt(count - 1).getBreadCrumbTitle()); + } else if (mCurrentRoot != null) { + title.setText(mCurrentRoot.title); + } else { + title.setText(null); + } + + summary.setText((String) getItem(position)); + + return convertView; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_dropdown_item_1line, parent, false); } - final BackStackEntry entry = getFragmentManager().getBackStackEntryAt(position); final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1); - text1.setText(entry.getBreadCrumbTitle()); + text1.setText((String) getItem(position)); return convertView; } }; - private OnNavigationListener mNavigationListener = new OnNavigationListener() { + private OnNavigationListener mSortListener = new OnNavigationListener() { @Override public boolean onNavigationItemSelected(int itemPosition, long itemId) { - if (mIgnoreNextNavigation) { - mIgnoreNextNavigation = false; - return false; - } - - getFragmentManager().popBackStack((int) itemId, 0); + // TODO: request updated sort order return true; } }; @@ -231,6 +368,7 @@ public class DocumentsActivity extends Activity { public void onDirectoryChanged(Uri uri, int flags) { mCurrentDir = uri; mCurrentSupportsCreate = (flags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0; + mCurrentSupportsSearch = (flags & DocumentsContract.FLAG_SUPPORTS_SEARCH) != 0; if (mAction == ACTION_CREATE) { final FragmentManager fm = getFragmentManager(); @@ -240,15 +378,11 @@ public class DocumentsActivity extends Activity { invalidateOptionsMenu(); } - public void onRootPicked(Root root) { - DirectoryFragment.show(getFragmentManager(), root.uri, root.title); - } - public void onDocumentPicked(Document doc) { final FragmentManager fm = getFragmentManager(); if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(doc.mimeType)) { // Nested directory picked, recurse using new fragment - DirectoryFragment.show(fm, doc.uri, doc.displayName); + DirectoryFragment.show(fm, doc.uri, doc.displayName, true); } else if (mAction == ACTION_OPEN) { // Explicit file picked, return onFinished(doc.uri); @@ -313,6 +447,46 @@ public class DocumentsActivity extends Activity { public static final int SORT_BY_DATE = 1; } + public static class Root { + public int rootType; + public Uri uri; + public Drawable icon; + public String title; + public String summary; + + public static Root fromCursor(Context context, ProviderInfo info, Cursor cursor) { + final Root root = new Root(); + + root.rootType = cursor.getInt(cursor.getColumnIndex(RootColumns.ROOT_TYPE)); + root.uri = DocumentsContract.buildDocumentUri( + info.authority, cursor.getString(cursor.getColumnIndex(RootColumns.GUID))); + + final PackageManager pm = context.getPackageManager(); + final int icon = cursor.getInt(cursor.getColumnIndex(RootColumns.ICON)); + if (icon != 0) { + try { + root.icon = pm.getResourcesForApplication(info.applicationInfo) + .getDrawable(icon); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + } else { + root.icon = info.loadIcon(pm); + } + + root.title = cursor.getString(cursor.getColumnIndex(RootColumns.TITLE)); + if (root.title == null) { + root.title = info.loadLabel(pm).toString(); + } + + root.summary = cursor.getString(cursor.getColumnIndex(RootColumns.SUMMARY)); + + return root; + } + } + public static class Document { public Uri uri; public String mimeType; @@ -386,7 +560,74 @@ public class DocumentsActivity extends Activity { } } - private static final String TAG_CREATE_DIRECTORY = "create_directory"; + /** + * Gather roots from all known storage providers. + */ + private void updateRoots() { + mRoots.clear(); + + final List<ProviderInfo> providers = getPackageManager() + .queryContentProviders(null, -1, PackageManager.GET_META_DATA); + for (ProviderInfo info : providers) { + if (info.metaData != null + && info.metaData.containsKey(DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) { + // TODO: populate roots on background thread, and cache results + final Uri uri = DocumentsContract.buildRootsUri(info.authority); + final Cursor cursor = getContentResolver().query(uri, null, null, null, null); + try { + while (cursor.moveToNext()) { + mRoots.add(Root.fromCursor(this, info, cursor)); + } + } finally { + cursor.close(); + } + } + } + } + + private OnItemClickListener mRootsListener = new OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + // Clear entire backstack and start in new root + final FragmentManager fm = getFragmentManager(); + while (fm.getBackStackEntryCount() > 0) { + fm.popBackStackImmediate(); + } + + mCurrentRoot = mRootsAdapter.getItem(position); + DirectoryFragment.show( + getFragmentManager(), mCurrentRoot.uri, mCurrentRoot.title, false); + + mDrawerLayout.closeDrawers(); + } + }; + + public static class RootsAdapter extends ArrayAdapter<Root> { + public RootsAdapter(Context context, List<Root> list) { + super(context, android.R.layout.simple_list_item_1, list); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_root, parent, false); + } + + final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); + + final Root root = getItem(position); + icon.setImageDrawable(root.icon); + title.setText(root.title); + + summary.setText(root.summary); + summary.setVisibility(root.summary != null ? View.VISIBLE : View.GONE); + + return convertView; + } + } public static class CreateDirectoryFragment extends DialogFragment { public static void show(FragmentManager fm) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index cdf9f21..17ae85f 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -2433,7 +2433,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(); if (curLp != null) { // check for the delta between the current set and the new - routeDiff = curLp.compareRoutes(newLp); + routeDiff = curLp.compareAllRoutes(newLp); dnsDiff = curLp.compareDnses(newLp); } else if (newLp != null) { routeDiff.added = newLp.getAllRoutes(); |