Merge "Enable stylus scale by default for apps targeting MNC+"
diff --git a/api/current.txt b/api/current.txt
index a126699..baadf5d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30888,6 +30888,7 @@
method public int getEvdoSnr();
method public int getGsmBitErrorRate();
method public int getGsmSignalStrength();
+ method public int getLevel();
method public boolean isGsm();
method public void writeToParcel(android.os.Parcel, int);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index e149048..d3be68b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33041,6 +33041,7 @@
method public int getEvdoSnr();
method public int getGsmBitErrorRate();
method public int getGsmSignalStrength();
+ method public int getLevel();
method public boolean isGsm();
method public void writeToParcel(android.os.Parcel, int);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10d6d01..ab5f811 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2541,11 +2541,16 @@
if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity));
AssistContent content = new AssistContent();
- Intent intent = new Intent(r.activity.getIntent());
- intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
- intent.removeUnsafeExtras();
- content.setIntent(intent);
+ Intent activityIntent = r.activity.getIntent();
+ if (activityIntent != null) {
+ Intent intent = new Intent(activityIntent);
+ intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+ intent.removeUnsafeExtras();
+ content.setIntent(intent);
+ } else {
+ content.setIntent(new Intent());
+ }
r.activity.onProvideAssistContent(content);
data.putParcelable(AssistContent.ASSIST_KEY, content);
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 179957d..9d1d312 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -597,6 +597,15 @@
}
}
+ /** @hide */
+ public long getNextWakeFromIdleTime() {
+ try {
+ return mService.getNextWakeFromIdleTime();
+ } catch (RemoteException ex) {
+ return Long.MAX_VALUE;
+ }
+ }
+
/**
* Gets information about the next alarm clock currently scheduled.
*
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index d5719f5..327c00b 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -33,6 +33,7 @@
boolean setTime(long millis);
void setTimeZone(String zone);
void remove(in PendingIntent operation);
+ long getNextWakeFromIdleTime();
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
}
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 56a3954..1aef0c1 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -62,6 +62,211 @@
<div class="toggle-content opened">
<p id="rev21"><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+/>Android Support Library, revision 22.1.0</a> <em>(April 2015)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+ <dl>
+ <dt>Changes for <a href="features.html#v4">v4 support library:</a></dt>
+ <dd>
+ <ul>
+
+ <li>Added the {@link android.support.v4.graphics.ColorUtils ColorUtils} class
+ to provide a set of color-related utility methods.
+ </li>
+ <li>Added the {@link android.support.v4.graphics.drawable.DrawableCompat#unwrap unwrap()} and
+ {@link android.support.v4.graphics.drawable.DrawableCompat#wrap wrap()} methods to the
+ {@link android.support.v4.graphics.drawable.DrawableCompat} class , allowing you to use
+ {@link android.support.v4.graphics.drawable.DrawableCompat#setTint setTint()},
+ {@link android.support.v4.graphics.drawable.DrawableCompat#setTintList setTintList()},
+ and {@link android.support.v4.graphics.drawable.DrawableCompat#setTintMode setTintMode()}
+ on all API level 4 or higher devices.
+ </li>
+ <li>Added the {@link android.support.v4.os.TraceCompat} class to
+ write trace events to the system trace buffer, which can then be collected and
+ visualized using the <a href="{@docRoot}tools/help/systrace.html">Systrace</a> tool.
+ </li>
+ <li>Added the {@link android.support.v4.util.CircularIntArray} class
+ to create circular integer array data structures.
+ </li>
+ <li>Added the {@link android.support.v4.util.CircularArray#clear clear()},
+ {@link android.support.v4.util.CircularArray#removeFromStart removeFromStart()},
+ and {@link android.support.v4.util.CircularArray#removeFromEnd removeFromEnd()}
+ methods to the {@link android.support.v4.util.CircularArray} class. Also, changed the
+ existing methods in this class to be non-final.
+ </li>
+ <li>Added the {@link android.support.v4.view.InputDeviceCompat}
+ as a helper class to access data in the
+ {@link android.support.v4.view.InputDeviceCompat} class.
+ </li>
+ <li>Added the {@link android.support.v4.view.LayoutInflaterCompat}
+ class as a helper class to access data in the
+ {@link android.support.v4.view.LayoutInflaterCompat} class
+ and added the {@link android.support.v4.view.LayoutInflaterFactory} interface.
+ </li>
+ <li>Added classes, methods, and interfaces to support nested scrolling.
+ <ul>
+ <li>Added the {@link android.support.v4.view.NestedScrollingChildHelper}
+ and {@link android.support.v4.view.NestedScrollingParentHelper}
+ helper classes for implementing nested scrolling parent and child views.</li>
+ <li>Added the {@link android.support.v4.view.NestedScrollingChild}
+ interface to be implemented by {@link android.view.View} subclasses.</li>
+ <li>Added the {@link android.support.v4.view.NestedScrollingParent}
+ and {@link android.support.v4.view.ScrollingView} interfaces to support
+ scrolling operations and provide scroll related APIs.</li>
+ <li>Added the
+ {@link android.support.v4.view.ViewGroupCompat#getNestedScrollAxes
+ getNestedScrollAxes()} method to the {@link android.support.v4.view.ViewGroupCompat}
+ class.</li>
+ <li>Added methods to the {@link android.support.v4.view.ViewParentCompat} class to
+ support nested scrolling.
+ </li>
+ <li>Added the {@link android.support.v4.widget.NestedScrollView}
+ class to support nested scrolling parent and child on both new and old versions of
+ Android.
+ </li>
+ </ul>
+ </li>
+ <li>Added methods and constants to the {@link android.support.v4.view.MotionEventCompat}
+ class for getting axis values and event source.
+ </li>
+ <li>Updated the {@link android.support.v4.view.accessibility.AccessibilityNodeInfoCompat}
+ class to add methods for errors, content invalidation and labels.
+ </li>
+ <li>Added the following interpolation classses for animation:
+ {@link android.support.v4.view.animation.FastOutLinearInInterpolator},
+ {@link android.support.v4.view.animation.FastOutSlowInInterpolator},
+ {@link android.support.v4.view.animation.LinearOutSlowInInterpolator},
+ {@link android.support.v4.view.animation.LinearOutSlowInInterpolator}, and
+ {@link android.support.v4.view.animation.PathInterpolatorCompat}.
+ </li>
+ <li>Added the {@link android.support.v4.widget.Space} class to create gaps between
+ components in general purpose layouts. This class is deprecated in the gridlayout library.
+ </li>
+ <li>Added the {@link android.support.v4.widget.TextViewCompat} class for accessing
+ features in a {@link android.widget.TextView}.
+ </li>
+ <li>Added a displacement parameter to the
+ {@link android.support.v4.widget.EdgeEffectCompat#onPull onPull()} method in the
+ {@link android.support.v4.widget.EdgeEffectCompat} class.
+ </li>
+
+ </ul>
+ </dd>
+
+
+ <dt>Changes for <a href="features.html#v7-appcompat">v7 appcompat library</a>:</dt>
+ <dd>
+ <ul>
+ <li>Added tint support to appcompat widgets, including
+ {@link android.support.v7.widget.AppCompatAutoCompleteTextView},
+ {@link android.support.v7.widget.AppCompatButton},
+ {@link android.support.v7.widget.AppCompatCheckBox},
+ {@link android.support.v7.widget.AppCompatCheckedTextView},
+ {@link android.support.v7.widget.AppCompatEditText},
+ {@link android.support.v7.widget.AppCompatMultiAutoCompleteTextView},
+ {@link android.support.v7.widget.AppCompatRadioButton},
+ {@link android.support.v7.widget.AppCompatRatingBar},
+ {@link android.support.v7.widget.AppCompatSpinner}, and
+ {@link android.support.v7.widget.AppCompatTextView}.
+ </li>
+ <li>Updated the {@link android.support.v7.app.AppCompatActivity} as the base
+ class for activities that use the support library action bar features. This class
+ replaces the deprecated {@link android.support.v7.app.ActionBarActivity}.
+ </li>
+ <li>Added the
+ {@link android.support.v7.app.AppCompatCallback} interface
+ to be implemented for AppCompat to be able to perform callbacks.
+ </li>
+ <li>Added the
+ {@link android.support.v7.app.AppCompatDelegate} abstract class
+ as a delegate you can use to extend AppCompat's support to any activity.
+ </li>
+ <li>Added the
+ {@link android.support.v7.app.AppCompatDialog} class
+ as the base class for AppCompat themed dialogs.
+ </li>
+ <li>Added the spinner style
+ {@link android.support.v7.app.AlertDialog} and
+ {@link android.support.v7.app.AlertDialog.Builder} classes to provide an AppCompat
+ themed {@link android.app.AlertDialog}.
+ </li>
+ <li>Added the {@link android.support.v7.graphics.Palette.Builder} class
+ for generating {@link android.support.v7.graphics.Palette} instances.
+ <ul>
+ <li>Added the
+ {@link android.support.v7.graphics.Palette#from}
+ method to the {@link android.support.v7.graphics.Palette} class to
+ start generating a Palette with the returned
+ {@link android.support.v7.graphics.Palette.Builder} instance.
+ </li>
+ <li>Deprecated the {@link android.support.v7.graphics.Palette#generate generate()} and
+ {@link android.support.v7.graphics.Palette#generateAsync generateAsync()} methods.
+ </li>
+ </ul>
+ </li>
+
+ <li>Added the
+ {@link android.support.v7.widget.GridLayout.Spec#getAbsoluteAlignment
+ getAbsoluteAlignment()} method to the {@link android.support.v7.widget.GridLayout.Spec}
+ class.
+ </li>
+ <li>Deprecated use of <code>app:theme</code> for styling
+ {@link android.support.v7.widget.Toolbar}. You can now use
+ <code>android:theme</code> for toolbars on all API level 7 and higher devices and
+ <code>android:theme</code> support for all widgets on API level 11 and higher devices.
+ </li>
+ </ul>
+ </dd>
+
+
+ <dt>Changes for <a href="features.html#v17-leanback">v17 leanback library</a>:</dt>
+ <dd>
+ <ul>
+ <li> Added {@link android.support.v17.leanback.app.GuidedStepFragment},
+ {@link android.support.v17.leanback.widget.GuidanceStylist} and
+ {@link android.support.v17.leanback.widget.GuidedActionsStylist} to support
+ creating multi-step decision flows.
+ </li>
+ </ul>
+ </dd>
+
+
+ <dt>Changes for <a href="features.html#v7-recyclerview">v7 recyclerview library</a>:</dt>
+ <dd>
+ <ul>
+ <li>Added {@link android.support.v7.util.SortedList} classes to display items in
+ a list order and provide notification of changes to the list.
+ </li>
+ <li>Added the {@link android.support.v7.widget.util.SortedListAdapterCallback} class
+ that can bind a sorted list to a
+ {@link android.support.v7.widget.RecyclerView.Adapter} class.
+ </li>
+ </ul>
+ </dd>
+
+
+ <dt>Changes for v8 renderscript library:</dt>
+ <dd>
+ <ul>
+ <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicHistogram} class for
+ use as a histogram filter.</li>
+ <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicResize} class for
+ performing a resize of a 2D allocation.
+ </li>
+ </ul>
+ </dd>
+
+ </dl>
+
+
+ </div>
+</div> <!-- end of collapsible section -->
+
+
+
+<div class="toggle-content closed">
+ <p id="rev21"><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""
/>Android Support Library, revision 22</a> <em>(March 2015)</em>
</p>
<div class="toggle-content-toggleme">
@@ -159,6 +364,10 @@
</ul>
</dd>
+ </div>
+</div>
+
+
<div class="toggle-content closed">
@@ -1113,4 +1322,3 @@
<p>Initial release with the v4 library.</p>
</div>
</div>
-
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 8039b71..efe71d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -16,17 +16,47 @@
package com.android.documentsui;
+import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
+import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.Executor;
+import libcore.io.IoUtils;
import android.app.Activity;
import android.app.Fragment;
-import android.content.pm.ResolveInfo;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Root;
+import android.util.Log;
import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnActionExpandListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.widget.SearchView.OnQueryTextListener;
+import android.widget.TextView;
+import com.android.documentsui.RecentsProvider.ResumeColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
@@ -34,20 +64,125 @@
import com.google.common.collect.Maps;
abstract class BaseActivity extends Activity {
+
+ static final String EXTRA_STATE = "state";
+
+ private final String mTag;
+ RootsCache mRoots;
+
public abstract State getDisplayState();
- public abstract RootInfo getCurrentRoot();
- public abstract void onStateChanged();
- public abstract void setRootsDrawerOpen(boolean open);
public abstract void onDocumentPicked(DocumentInfo doc);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
- public abstract DocumentInfo getCurrentDirectory();
- public abstract void setPending(boolean pending);
- public abstract void onStackPicked(DocumentStack stack);
- public abstract void onPickRequested(DocumentInfo pickTarget);
- public abstract void onAppPicked(ResolveInfo info);
- public abstract void onRootPicked(RootInfo root, boolean closeDrawer);
- public abstract void onSaveRequested(DocumentInfo replaceTarget);
- public abstract void onSaveRequested(String mimeType, String displayName);
+ abstract void onTaskFinished(Uri... uris);
+ abstract void onDirectoryChanged(int anim);
+ abstract void updateActionBar();
+ abstract void saveStackBlocking();
+
+ public BaseActivity(String tag) {
+ mTag = tag;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mRoots = DocumentsApplication.getRootsCache(this);
+ }
+
+ void onStackRestored(boolean restored, boolean external) {}
+
+ void onRootPicked(RootInfo root) {
+ State state = getDisplayState();
+
+ // Clear entire backstack and start in new root
+ state.stack.root = root;
+ state.stack.clear();
+ state.stackTouched = true;
+
+ if (!mRoots.isRecentsRoot(root)) {
+ new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+ } else {
+ onCurrentDirectoryChanged(ANIM_SIDE);
+ }
+ }
+
+ void expandMenus(Menu menu) {
+ for (int i = 0; i < menu.size(); i++) {
+ final MenuItem item = menu.getItem(i);
+ switch (item.getItemId()) {
+ case R.id.menu_advanced:
+ case R.id.menu_file_size:
+ break;
+ default:
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int id = item.getItemId();
+ if (id == android.R.id.home) {
+ onBackPressed();
+ return true;
+ } else if (id == R.id.menu_create_dir) {
+ CreateDirectoryFragment.show(getFragmentManager());
+ return true;
+ } else if (id == R.id.menu_search) {
+ return false;
+ } else if (id == R.id.menu_sort_name) {
+ setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
+ return true;
+ } else if (id == R.id.menu_sort_date) {
+ setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
+ return true;
+ } else if (id == R.id.menu_sort_size) {
+ setUserSortOrder(State.SORT_ORDER_SIZE);
+ return true;
+ } else if (id == R.id.menu_grid) {
+ setUserMode(State.MODE_GRID);
+ return true;
+ } else if (id == R.id.menu_list) {
+ setUserMode(State.MODE_LIST);
+ return true;
+ } else if (id == R.id.menu_advanced) {
+ setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
+ return true;
+ } else if (id == R.id.menu_file_size) {
+ setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
+ return true;
+ } else if (id == R.id.menu_settings) {
+ final RootInfo root = getCurrentRoot();
+ final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
+ intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
+ DocumentsContract.Root.MIME_TYPE_ITEM);
+ startActivity(intent);
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Call this when directory changes. Prior to root fragment update
+ * the (abstract) directoryChanged method will be called.
+ * @param anim
+ */
+ final void onCurrentDirectoryChanged(int anim) {
+ onDirectoryChanged(anim);
+
+ final RootsFragment roots = RootsFragment.get(getFragmentManager());
+ if (roots != null) {
+ roots.onCurrentRootChanged();
+ }
+
+ updateActionBar();
+ invalidateOptionsMenu();
+ }
+
+ final String getCallingPackageMaybeExtra() {
+ final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
+ return (extra != null) ? extra : getCallingPackage();
+ }
public static BaseActivity get(Fragment fragment) {
return (BaseActivity) fragment.getActivity();
@@ -169,4 +304,382 @@
}
};
}
+
+ void setDisplayAdvancedDevices(boolean display) {
+ State state = getDisplayState();
+ LocalPreferences.setDisplayAdvancedDevices(this, display);
+ state.showAdvanced = state.forceAdvanced | display;
+ RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
+ invalidateOptionsMenu();
+ }
+
+ void setDisplayFileSize(boolean display) {
+ LocalPreferences.setDisplayFileSize(this, display);
+ getDisplayState().showSize = display;
+ DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
+ invalidateOptionsMenu();
+ }
+
+ void onStateChanged() {
+ invalidateOptionsMenu();
+ }
+
+ /**
+ * Set state sort order based on explicit user action.
+ */
+ void setUserSortOrder(int sortOrder) {
+ getDisplayState().userSortOrder = sortOrder;
+ DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
+ }
+
+ /**
+ * Set state mode based on explicit user action.
+ */
+ void setUserMode(int mode) {
+ getDisplayState().userMode = mode;
+ DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
+ }
+
+ void setPending(boolean pending) {
+ final SaveFragment save = SaveFragment.get(getFragmentManager());
+ if (save != null) {
+ save.setPending(pending);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle state) {
+ super.onSaveInstanceState(state);
+ state.putParcelable(EXTRA_STATE, getDisplayState());
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ }
+
+ RootInfo getCurrentRoot() {
+ State state = getDisplayState();
+ if (state.stack.root != null) {
+ return state.stack.root;
+ } else {
+ return mRoots.getRecentsRoot();
+ }
+ }
+
+ public DocumentInfo getCurrentDirectory() {
+ return getDisplayState().stack.peek();
+ }
+
+ public Executor getCurrentExecutor() {
+ final DocumentInfo cwd = getCurrentDirectory();
+ if (cwd != null && cwd.authority != null) {
+ return ProviderExecutor.forAuthority(cwd.authority);
+ } else {
+ return AsyncTask.THREAD_POOL_EXECUTOR;
+ }
+ }
+
+ public void onStackPicked(DocumentStack stack) {
+ try {
+ // Update the restored stack to ensure we have freshest data
+ stack.updateDocuments(getContentResolver());
+
+ State state = getDisplayState();
+ state.stack = stack;
+ state.stackTouched = true;
+ onCurrentDirectoryChanged(ANIM_SIDE);
+
+ } catch (FileNotFoundException e) {
+ Log.w(mTag, "Failed to restore stack: " + e);
+ }
+ }
+
+ final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+ private RootInfo mRoot;
+
+ public PickRootTask(RootInfo root) {
+ mRoot = root;
+ }
+
+ @Override
+ protected DocumentInfo doInBackground(Void... params) {
+ try {
+ final Uri uri = DocumentsContract.buildDocumentUri(
+ mRoot.authority, mRoot.documentId);
+ return DocumentInfo.fromUri(getContentResolver(), uri);
+ } catch (FileNotFoundException e) {
+ Log.w(mTag, "Failed to find root", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(DocumentInfo result) {
+ if (result != null) {
+ State state = getDisplayState();
+ state.stack.push(result);
+ state.stackTouched = true;
+ onCurrentDirectoryChanged(ANIM_SIDE);
+ }
+ }
+ }
+
+ final class RestoreStackTask extends AsyncTask<Void, Void, Void> {
+ private volatile boolean mRestoredStack;
+ private volatile boolean mExternal;
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ State state = getDisplayState();
+ RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
+
+ // Restore last stack for calling package
+ final String packageName = getCallingPackageMaybeExtra();
+ final Cursor cursor = getContentResolver()
+ .query(RecentsProvider.buildResume(packageName), null, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
+ final byte[] rawStack = cursor.getBlob(
+ cursor.getColumnIndex(ResumeColumns.STACK));
+ DurableUtils.readFromArray(rawStack, state.stack);
+ mRestoredStack = true;
+ }
+ } catch (IOException e) {
+ Log.w(mTag, "Failed to resume: " + e);
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ if (mRestoredStack) {
+ // Update the restored stack to ensure we have freshest data
+ final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+ try {
+ state.stack.updateRoot(matchingRoots);
+ state.stack.updateDocuments(getContentResolver());
+ } catch (FileNotFoundException e) {
+ Log.w(mTag, "Failed to restore stack: " + e);
+ state.stack.reset();
+ mRestoredStack = false;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (isDestroyed()) return;
+ getDisplayState().restored = true;
+ onCurrentDirectoryChanged(ANIM_NONE);
+
+ onStackRestored(mRestoredStack, mExternal);
+
+ getDisplayState().restored = true;
+ onCurrentDirectoryChanged(ANIM_NONE);
+ }
+ }
+
+ final class ItemSelectedListener implements OnItemSelectedListener {
+
+ boolean mIgnoreNextNavigation;
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (mIgnoreNextNavigation) {
+ mIgnoreNextNavigation = false;
+ return;
+ }
+
+ State state = getDisplayState();
+ while (state.stack.size() > position + 1) {
+ state.stackTouched = true;
+ state.stack.pop();
+ }
+ onCurrentDirectoryChanged(ANIM_UP);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Ignored
+ }
+ }
+
+ /**
+ * Class providing toolbar with runtime access to useful activity data.
+ */
+ final class StackAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return getDisplayState().stack.size();
+ }
+
+ @Override
+ public DocumentInfo getItem(int position) {
+ State state = getDisplayState();
+ return state.stack.get(state.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 = 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 ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
+ final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+ final DocumentInfo doc = getItem(position);
+
+ if (position == 0) {
+ final RootInfo root = getCurrentRoot();
+ title.setText(root.title);
+ subdir.setVisibility(View.GONE);
+ } else {
+ title.setText(doc.displayName);
+ subdir.setVisibility(View.VISIBLE);
+ }
+
+ return convertView;
+ }
+ }
+
+ /**
+ * Facade over the various search parts in the menu.
+ */
+ final class SearchManager implements
+ SearchView.OnCloseListener, OnActionExpandListener, OnQueryTextListener {
+
+ protected boolean mSearchExpanded;
+ protected boolean mIgnoreNextClose;
+ protected boolean mIgnoreNextCollapse;
+
+ private MenuItem mMenu;
+ private SearchView mView;
+
+ public void install(MenuItem menu) {
+ assert(mMenu == null);
+ mMenu = menu;
+ mView = (SearchView) menu.getActionView();
+
+ mMenu.setOnActionExpandListener(this);
+ mView.setOnQueryTextListener(this);
+ mView.setOnCloseListener(this);
+ }
+
+ /**
+ * @param root Info about the current directory.
+ */
+ void update(RootInfo root) {
+ if (mMenu == null) {
+ Log.d(mTag, "showMenu called before Search MenuItem installed.");
+ return;
+ }
+ State state = getDisplayState();
+ if (state.currentSearch != null) {
+ mMenu.expandActionView();
+
+ mView.setIconified(false);
+ mView.clearFocus();
+ mView.setQuery(state.currentSearch, false);
+ } else {
+ mIgnoreNextClose = true;
+ mView.setIconified(true);
+ mView.clearFocus();
+
+ mIgnoreNextCollapse = true;
+ mMenu.collapseActionView();
+ }
+
+ showMenu(root != null
+ && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
+ }
+
+ void showMenu(boolean visible) {
+ if (mMenu == null) {
+ Log.d(mTag, "showMenu called before Search MenuItem installed.");
+ return;
+ }
+ mMenu.setVisible(visible);
+ }
+
+ boolean isSearching() {
+ return getDisplayState().currentSearch != null;
+ }
+
+ boolean isExpanded() {
+ return mSearchExpanded;
+ }
+
+ @Override
+ public boolean onClose() {
+ mSearchExpanded = false;
+ if (mIgnoreNextClose) {
+ mIgnoreNextClose = false;
+ return false;
+ }
+
+ getDisplayState().currentSearch = null;
+ onCurrentDirectoryChanged(ANIM_NONE);
+ return false;
+ }
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ mSearchExpanded = true;
+ updateActionBar();
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ mSearchExpanded = false;
+ if (mIgnoreNextCollapse) {
+ mIgnoreNextCollapse = false;
+ return true;
+ }
+
+ getDisplayState().currentSearch = null;
+ onCurrentDirectoryChanged(ANIM_NONE);
+ return true;
+ }
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ mSearchExpanded = true;
+ getDisplayState().currentSearch = query;
+ mView.clearFocus();
+ onCurrentDirectoryChanged(ANIM_NONE);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return false;
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 37a14c6..7cf58cc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -28,7 +28,6 @@
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Fragment;
@@ -311,8 +310,9 @@
updateDisplayState();
// When launched into empty recents, show drawer
- if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched) {
- ((BaseActivity) context).setRootsDrawerOpen(true);
+ if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched &&
+ context instanceof DocumentsActivity) {
+ ((DocumentsActivity) context).setRootsDrawerOpen(true);
}
// Restore any previous instance state
@@ -503,7 +503,8 @@
open.setVisible(!manageOrBrowse);
share.setVisible(manageOrBrowse);
delete.setVisible(manageOrBrowse);
- copy.setVisible(manageOrBrowse);
+ // Disable copying from the Recents view.
+ copy.setVisible(manageOrBrowse && mType != TYPE_RECENT_OPEN);
return true;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index a2a789f..9d828de 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -21,16 +21,17 @@
import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTINATION;
-import static com.android.documentsui.BaseActivity.State.MODE_GRID;
-import static com.android.documentsui.BaseActivity.State.MODE_LIST;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import java.util.Arrays;
+import java.util.List;
+
import android.app.Activity;
+import android.app.Fragment;
import android.app.FragmentManager;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
@@ -42,7 +43,6 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.database.Cursor;
import android.graphics.Point;
import android.net.Uri;
import android.os.AsyncTask;
@@ -54,51 +54,27 @@
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.MenuItem.OnActionExpandListener;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
import android.widget.Spinner;
-import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
-import libcore.io.IoUtils;
-
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.RecentsProvider.ResumeColumns;
import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Executor;
-
public class DocumentsActivity extends BaseActivity {
+ private static final int CODE_FORWARD = 42;
public static final String TAG = "Documents";
- private static final String EXTRA_STATE = "state";
-
- private static final int CODE_FORWARD = 42;
-
private boolean mShowAsDialog;
- private SearchView mSearchView;
-
private Toolbar mToolbar;
private Spinner mToolbarStack;
@@ -110,21 +86,20 @@
private DirectoryContainerView mDirectoryContainer;
- private boolean mIgnoreNextNavigation;
- private boolean mIgnoreNextClose;
- private boolean mIgnoreNextCollapse;
-
- private boolean mSearchExpanded;
-
- private RootsCache mRoots;
private State mState;
+ private SearchManager mSearchManager;
+ private ItemSelectedListener mStackListener;
+ private BaseAdapter mStackAdapter;
+
+ public DocumentsActivity() {
+ super(TAG);
+ }
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mRoots = DocumentsApplication.getRootsCache(this);
-
setResult(Activity.RESULT_CANCELED);
setContentView(R.layout.activity);
@@ -157,16 +132,16 @@
mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
- if (icicle != null) {
- mState = icicle.getParcelable(EXTRA_STATE);
- } else {
- buildDefaultState();
- }
+ mState = (icicle != null)
+ ? icicle.<State>getParcelable(EXTRA_STATE)
+ : buildDefaultState();
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitleTextAppearance(context,
android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+ mStackAdapter = new StackAdapter();
+ mStackListener = new ItemSelectedListener();
mToolbarStack = (Spinner) findViewById(R.id.stack);
mToolbarStack.setOnItemSelectedListener(mStackListener);
@@ -176,6 +151,7 @@
android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
}
+ mSearchManager = new SearchManager();
setActionBar(mToolbar);
// Hide roots when we're managing a specific root
@@ -220,55 +196,57 @@
}
}
- private void buildDefaultState() {
- mState = new State();
+ private State buildDefaultState() {
+ State state = new State();
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
- mState.action = ACTION_OPEN;
+ state.action = ACTION_OPEN;
} else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
- mState.action = ACTION_CREATE;
+ state.action = ACTION_CREATE;
} else if (Intent.ACTION_GET_CONTENT.equals(action)) {
- mState.action = ACTION_GET_CONTENT;
+ state.action = ACTION_GET_CONTENT;
} else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
- mState.action = ACTION_OPEN_TREE;
+ state.action = ACTION_OPEN_TREE;
} else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
- mState.action = ACTION_MANAGE;
+ state.action = ACTION_MANAGE;
} else if (DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT.equals(action)) {
- mState.action = ACTION_BROWSE;
+ state.action = ACTION_BROWSE;
} else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
- mState.action = ACTION_OPEN_COPY_DESTINATION;
+ state.action = ACTION_OPEN_COPY_DESTINATION;
}
- if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
- mState.allowMultiple = intent.getBooleanExtra(
+ if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
+ state.allowMultiple = intent.getBooleanExtra(
Intent.EXTRA_ALLOW_MULTIPLE, false);
}
- if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
- mState.acceptMimes = new String[] { "*/*" };
- mState.allowMultiple = true;
+ if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+ state.acceptMimes = new String[] { "*/*" };
+ state.allowMultiple = true;
} else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
- mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
+ state.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
} else {
- mState.acceptMimes = new String[] { intent.getType() };
+ state.acceptMimes = new String[] { intent.getType() };
}
- mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
- mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
- mState.showAdvanced = mState.forceAdvanced
+ state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+ state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ state.showAdvanced = state.forceAdvanced
| LocalPreferences.getDisplayAdvancedDevices(this);
- if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
- mState.showSize = true;
+ if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+ state.showSize = true;
} else {
- mState.showSize = LocalPreferences.getDisplayFileSize(this);
+ state.showSize = LocalPreferences.getDisplayFileSize(this);
}
- if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
- mState.directoryCopy = intent.getBooleanExtra(
+ if (state.action == ACTION_OPEN_COPY_DESTINATION) {
+ state.directoryCopy = intent.getBooleanExtra(
BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
}
+
+ return state;
}
private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
@@ -290,7 +268,7 @@
mState.restored = true;
if (root != null) {
- onRootPicked(root, true);
+ onRootPicked(root);
} else {
Log.w(TAG, "Failed to find root: " + mRootUri);
finish();
@@ -298,71 +276,55 @@
}
}
- private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
- private volatile boolean mRestoredStack;
- private volatile boolean mExternal;
+ @Override
+ void onStackRestored(boolean restored, boolean external) {
+ // Show drawer when no stack restored, but only when requesting
+ // non-visual content. However, if we last used an external app,
+ // drawer is always shown.
- @Override
- protected Void doInBackground(Void... params) {
- // Restore last stack for calling package
- final String packageName = getCallingPackageMaybeExtra();
- final Cursor cursor = getContentResolver()
- .query(RecentsProvider.buildResume(packageName), null, null, null, null);
- try {
- if (cursor.moveToFirst()) {
- mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
- final byte[] rawStack = cursor.getBlob(
- cursor.getColumnIndex(ResumeColumns.STACK));
- DurableUtils.readFromArray(rawStack, mState.stack);
- mRestoredStack = true;
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to resume: " + e);
- } finally {
- IoUtils.closeQuietly(cursor);
- }
-
- if (mRestoredStack) {
- // Update the restored stack to ensure we have freshest data
- final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
- try {
- mState.stack.updateRoot(matchingRoots);
- mState.stack.updateDocuments(getContentResolver());
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- mState.stack.reset();
- mRestoredStack = false;
- }
- }
-
- return null;
+ boolean showDrawer = false;
+ if (!restored) {
+ showDrawer = true;
+ }
+ if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
+ showDrawer = false;
+ }
+ if (external && mState.action == ACTION_GET_CONTENT) {
+ showDrawer = true;
}
- @Override
- protected void onPostExecute(Void result) {
- if (isDestroyed()) return;
- mState.restored = true;
+ if (showDrawer) {
+ setRootsDrawerOpen(true);
+ }
+ }
- // Show drawer when no stack restored, but only when requesting
- // non-visual content. However, if we last used an external app,
- // drawer is always shown.
+ public void onAppPicked(ResolveInfo info) {
+ final Intent intent = new Intent(getIntent());
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ intent.setComponent(new ComponentName(
+ info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
+ startActivityForResult(intent, CODE_FORWARD);
+ }
- boolean showDrawer = false;
- if (!mRestoredStack) {
- showDrawer = true;
- }
- if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
- showDrawer = false;
- }
- if (mExternal && mState.action == ACTION_GET_CONTENT) {
- showDrawer = true;
- }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "onActivityResult() code=" + resultCode);
- if (showDrawer) {
- setRootsDrawerOpen(true);
- }
+ // Only relay back results when not canceled; otherwise stick around to
+ // let the user pick another app/backend.
+ if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
- onCurrentDirectoryChanged(ANIM_NONE);
+ // Remember that we last picked via external app
+ final String packageName = getCallingPackageMaybeExtra();
+ final ContentValues values = new ContentValues();
+ values.put(ResumeColumns.EXTERNAL, 1);
+ getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
+
+ // Pass back result to original caller
+ setResult(resultCode, data);
+ finish();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
}
}
@@ -397,7 +359,6 @@
updateActionBar();
}
- @Override
public void setRootsDrawerOpen(boolean open) {
if (!mShowAsDialog) {
if (open) {
@@ -416,6 +377,7 @@
}
}
+ @Override
public void updateActionBar() {
if (mRootsToolbar != null) {
if (mState.action == ACTION_OPEN ||
@@ -447,7 +409,7 @@
});
}
- if (mSearchExpanded) {
+ if (mSearchManager.isExpanded()) {
mToolbar.setTitle(null);
mToolbarStack.setVisibility(View.GONE);
mToolbarStack.setAdapter(null);
@@ -461,7 +423,7 @@
mToolbarStack.setVisibility(View.VISIBLE);
mToolbarStack.setAdapter(mStackAdapter);
- mIgnoreNextNavigation = true;
+ mStackListener.mIgnoreNextNavigation = true;
mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
}
}
@@ -469,79 +431,18 @@
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
+ boolean showMenu = super.onCreateOptionsMenu(menu);
+
getMenuInflater().inflate(R.menu.activity, menu);
// Most actions are visible when showing as dialog
if (mShowAsDialog) {
- for (int i = 0; i < menu.size(); i++) {
- final MenuItem item = menu.getItem(i);
- switch (item.getItemId()) {
- case R.id.menu_advanced:
- case R.id.menu_file_size:
- break;
- default:
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- }
+ expandMenus(menu);
}
- final MenuItem searchMenu = menu.findItem(R.id.menu_search);
- mSearchView = (SearchView) searchMenu.getActionView();
- mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- mSearchExpanded = true;
- mState.currentSearch = query;
- mSearchView.clearFocus();
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
+ this.mSearchManager.install(menu.findItem(R.id.menu_search));
- @Override
- public boolean onQueryTextChange(String newText) {
- return false;
- }
- });
-
- searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
- @Override
- public boolean onMenuItemActionExpand(MenuItem item) {
- mSearchExpanded = true;
- updateActionBar();
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(MenuItem item) {
- mSearchExpanded = false;
- if (mIgnoreNextCollapse) {
- mIgnoreNextCollapse = false;
- return true;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
- });
-
- mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
- @Override
- public boolean onClose() {
- mSearchExpanded = false;
- if (mIgnoreNextClose) {
- mIgnoreNextClose = false;
- return false;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return false;
- }
- });
-
- return true;
+ return showMenu;
}
@Override
@@ -554,7 +455,6 @@
final DocumentInfo cwd = getCurrentDirectory();
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem search = menu.findItem(R.id.menu_search);
final MenuItem sort = menu.findItem(R.id.menu_sort);
final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
final MenuItem grid = menu.findItem(R.id.menu_grid);
@@ -564,36 +464,23 @@
final MenuItem settings = menu.findItem(R.id.menu_settings);
sort.setVisible(cwd != null);
- grid.setVisible(mState.derivedMode != MODE_GRID);
- list.setVisible(mState.derivedMode != MODE_LIST);
+ grid.setVisible(mState.derivedMode != State.MODE_GRID);
+ list.setVisible(mState.derivedMode != State.MODE_LIST);
- if (mState.currentSearch != null) {
- // Search uses backend ranking; no sorting
- sort.setVisible(false);
- search.expandActionView();
+ mSearchManager.update(root);
- mSearchView.setIconified(false);
- mSearchView.clearFocus();
- mSearchView.setQuery(mState.currentSearch, false);
- } else {
- mIgnoreNextClose = true;
- mSearchView.setIconified(true);
- mSearchView.clearFocus();
-
- mIgnoreNextCollapse = true;
- search.collapseActionView();
- }
+ // Search uses backend ranking; no sorting
+ sort.setVisible(mSearchManager.isSearching());
// Only sort by size when visible
sortSize.setVisible(mState.showSize);
- boolean searchVisible;
boolean fileSizeVisible = !(mState.action == ACTION_MANAGE
|| mState.action == ACTION_BROWSE);
if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
createDir.setVisible(cwd != null && cwd.isCreateSupported());
- searchVisible = false;
+ mSearchManager.showMenu(false);
// No display options in recent directories
if (cwd == null) {
@@ -607,14 +494,8 @@
}
} else {
createDir.setVisible(false);
-
- searchVisible = root != null
- && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0);
}
- // TODO: close any search in-progress when hiding
- search.setVisible(searchVisible);
-
advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
? R.string.menu_advanced_hide : R.string.menu_advanced_show);
fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
@@ -634,90 +515,7 @@
if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
-
- final int id = item.getItemId();
- if (id == android.R.id.home) {
- onBackPressed();
- return true;
- } else if (id == R.id.menu_create_dir) {
- CreateDirectoryFragment.show(getFragmentManager());
- return true;
- } else if (id == R.id.menu_search) {
- return false;
- } else if (id == R.id.menu_sort_name) {
- setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
- return true;
- } else if (id == R.id.menu_sort_date) {
- setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
- return true;
- } else if (id == R.id.menu_sort_size) {
- setUserSortOrder(State.SORT_ORDER_SIZE);
- return true;
- } else if (id == R.id.menu_grid) {
- setUserMode(State.MODE_GRID);
- return true;
- } else if (id == R.id.menu_list) {
- setUserMode(State.MODE_LIST);
- return true;
- } else if (id == R.id.menu_advanced) {
- setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
- return true;
- } else if (id == R.id.menu_file_size) {
- setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
- return true;
- } else if (id == R.id.menu_settings) {
- final RootInfo root = getCurrentRoot();
- final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
- intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
- DocumentsContract.Root.MIME_TYPE_ITEM);
- startActivity(intent);
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void setDisplayAdvancedDevices(boolean display) {
- LocalPreferences.setDisplayAdvancedDevices(this, display);
- mState.showAdvanced = mState.forceAdvanced | display;
- RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- private void setDisplayFileSize(boolean display) {
- LocalPreferences.setDisplayFileSize(this, display);
- mState.showSize = display;
- DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- @Override
- public void onStateChanged() {
- invalidateOptionsMenu();
- }
-
- /**
- * Set state sort order based on explicit user action.
- */
- private void setUserSortOrder(int sortOrder) {
- mState.userSortOrder = sortOrder;
- DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
- }
-
- /**
- * Set state mode based on explicit user action.
- */
- private void setUserMode(int mode) {
- mState.userMode = mode;
- DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
- }
-
- @Override
- public void setPending(boolean pending) {
- final SaveFragment save = SaveFragment.get(getFragmentManager());
- if (save != null) {
- save.setPending(pending);
- }
+ return super.onOptionsItemSelected(item);
}
@Override
@@ -740,131 +538,12 @@
}
@Override
- protected void onSaveInstanceState(Bundle state) {
- super.onSaveInstanceState(state);
- state.putParcelable(EXTRA_STATE, mState);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle state) {
- super.onRestoreInstanceState(state);
- }
-
- private BaseAdapter mStackAdapter = new BaseAdapter() {
- @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 = 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 ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final DocumentInfo doc = getItem(position);
-
- if (position == 0) {
- final RootInfo root = getCurrentRoot();
- title.setText(root.title);
- subdir.setVisibility(View.GONE);
- } else {
- title.setText(doc.displayName);
- subdir.setVisibility(View.VISIBLE);
- }
-
- return convertView;
- }
- };
-
- private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (mIgnoreNextNavigation) {
- mIgnoreNextNavigation = false;
- return;
- }
-
- while (mState.stack.size() > position + 1) {
- mState.stackTouched = true;
- mState.stack.pop();
- }
- onCurrentDirectoryChanged(ANIM_UP);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Ignored
- }
- };
-
- @Override
- public RootInfo getCurrentRoot() {
- if (mState.stack.root != null) {
- return mState.stack.root;
- } else {
- return mRoots.getRecentsRoot();
- }
- }
-
- @Override
- public DocumentInfo getCurrentDirectory() {
- return mState.stack.peek();
- }
-
- private String getCallingPackageMaybeExtra() {
- final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
- return (extra != null) ? extra : getCallingPackage();
- }
-
- public Executor getCurrentExecutor() {
- final DocumentInfo cwd = getCurrentDirectory();
- if (cwd != null && cwd.authority != null) {
- return ProviderExecutor.forAuthority(cwd.authority);
- } else {
- return AsyncTask.THREAD_POOL_EXECUTOR;
- }
- }
-
- @Override
public State getDisplayState() {
return mState;
}
- private void onCurrentDirectoryChanged(int anim) {
+ @Override
+ void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
@@ -883,7 +562,7 @@
// Start recents in grid when requesting visual things
final boolean visualMimes = MimePredicate.mimeMatches(
MimePredicate.VISUAL_MIMES, mState.acceptMimes);
- mState.userMode = visualMimes ? MODE_GRID : MODE_LIST;
+ mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
mState.derivedMode = mState.userMode;
}
} else {
@@ -913,108 +592,20 @@
pick.setPickTarget(mState.action, cwd, displayName);
}
}
+ }
- final RootsFragment roots = RootsFragment.get(fm);
- if (roots != null) {
- roots.onCurrentRootChanged();
- }
+ void onSaveRequested(DocumentInfo replaceTarget) {
+ new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
+ }
- updateActionBar();
- invalidateOptionsMenu();
- dumpStack();
+ void onSaveRequested(String mimeType, String displayName) {
+ new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
}
@Override
- public void onStackPicked(DocumentStack stack) {
- try {
- // Update the restored stack to ensure we have freshest data
- stack.updateDocuments(getContentResolver());
-
- mState.stack = stack;
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
-
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- }
- }
-
- @Override
- public void onRootPicked(RootInfo root, boolean closeDrawer) {
- // Clear entire backstack and start in new root
- mState.stack.root = root;
- mState.stack.clear();
- mState.stackTouched = true;
-
- if (!mRoots.isRecentsRoot(root)) {
- new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
- } else {
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
-
- if (closeDrawer) {
- setRootsDrawerOpen(false);
- }
- }
-
- private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
- private RootInfo mRoot;
-
- public PickRootTask(RootInfo root) {
- mRoot = root;
- }
-
- @Override
- protected DocumentInfo doInBackground(Void... params) {
- try {
- final Uri uri = DocumentsContract.buildDocumentUri(
- mRoot.authority, mRoot.documentId);
- return DocumentInfo.fromUri(getContentResolver(), uri);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to find root", e);
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(DocumentInfo result) {
- if (result != null) {
- mState.stack.push(result);
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
- }
- }
-
- @Override
- public void onAppPicked(ResolveInfo info) {
- final Intent intent = new Intent(getIntent());
- intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- intent.setComponent(new ComponentName(
- info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
- startActivityForResult(intent, CODE_FORWARD);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "onActivityResult() code=" + resultCode);
-
- // Only relay back results when not canceled; otherwise stick around to
- // let the user pick another app/backend.
- if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
- // Remember that we last picked via external app
- final String packageName = getCallingPackageMaybeExtra();
- final ContentValues values = new ContentValues();
- values.put(ResumeColumns.EXTERNAL, 1);
- getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
- // Pass back result to original caller
- setResult(resultCode, data);
- finish();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
+ void onRootPicked(RootInfo root) {
+ super.onRootPicked(root);
+ setRootsDrawerOpen(false);
}
@Override
@@ -1076,17 +667,6 @@
}
}
- @Override
- public void onSaveRequested(DocumentInfo replaceTarget) {
- new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
- public void onSaveRequested(String mimeType, String displayName) {
- new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
public void onPickRequested(DocumentInfo pickTarget) {
Uri result;
if (mState.action == ACTION_OPEN_TREE) {
@@ -1101,7 +681,8 @@
new PickFinishTask(result).executeOnExecutor(getCurrentExecutor());
}
- private void saveStackBlocking() {
+ @Override
+ void saveStackBlocking() {
final ContentResolver resolver = getContentResolver();
final ContentValues values = new ContentValues();
@@ -1124,7 +705,8 @@
resolver.insert(RecentsProvider.buildResume(packageName), values);
}
- private void onFinished(Uri... uris) {
+ @Override
+ void onTaskFinished(Uri... uris) {
Log.d(TAG, "onFinished() " + Arrays.toString(uris));
final Intent intent = new Intent();
@@ -1159,7 +741,52 @@
finish();
}
- private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+ public static DocumentsActivity get(Fragment fragment) {
+ return (DocumentsActivity) fragment.getActivity();
+ }
+
+ private final class PickFinishTask extends AsyncTask<Void, Void, Void> {
+ private final Uri mUri;
+
+ public PickFinishTask(Uri uri) {
+ mUri = uri;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ saveStackBlocking();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ onTaskFinished(mUri);
+ }
+ }
+
+ final class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+ private final Uri[] mUris;
+
+ public ExistingFinishTask(Uri... uris) {
+ mUris = uris;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ saveStackBlocking();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ onTaskFinished(mUris);
+ }
+ }
+
+ /**
+ * Task that creates a new document in the background.
+ */
+ final class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
private final String mMimeType;
private final String mDisplayName;
@@ -1201,7 +828,7 @@
@Override
protected void onPostExecute(Uri result) {
if (result != null) {
- onFinished(result);
+ onTaskFinished(result);
} else {
Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
.show();
@@ -1210,50 +837,4 @@
setPending(false);
}
}
-
- private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri[] mUris;
-
- public ExistingFinishTask(Uri... uris) {
- mUris = uris;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUris);
- }
- }
-
- private class PickFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri mUri;
-
- public PickFinishTask(Uri uri) {
- mUri = uri;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUri);
- }
- }
-
- private void dumpStack() {
- Log.d(TAG, "Current stack: ");
- Log.d(TAG, " * " + mState.stack.root);
- for (DocumentInfo doc : mState.stack) {
- Log.d(TAG, " +-- " + doc);
- }
- }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 7ea51b9..e899379 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -75,7 +75,7 @@
private View.OnClickListener mPickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- final BaseActivity activity = BaseActivity.get(PickFragment.this);
+ final DocumentsActivity activity = DocumentsActivity.get(PickFragment.this);
activity.onPickRequested(mPickTarget);
}
};
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 26aecc5..e11d7d9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -109,8 +109,9 @@
mAdapter.swapStacks(data);
// When launched into empty recents, show drawer
- if (mAdapter.isEmpty() && !state.stackTouched) {
- ((BaseActivity) context).setRootsDrawerOpen(true);
+ if (mAdapter.isEmpty() && !state.stackTouched &&
+ context instanceof DocumentsActivity) {
+ ((DocumentsActivity) context).setRootsDrawerOpen(true);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index ed5e123..fd67a77 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -174,11 +174,12 @@
private OnItemClickListener mItemListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final BaseActivity activity = BaseActivity.get(RootsFragment.this);
- final Item item = mAdapter.getItem(position);
+ Item item = mAdapter.getItem(position);
if (item instanceof RootItem) {
- activity.onRootPicked(((RootItem) item).root, true);
+ BaseActivity activity = BaseActivity.get(RootsFragment.this);
+ activity.onRootPicked(((RootItem) item).root);
} else if (item instanceof AppItem) {
+ DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
activity.onAppPicked(((AppItem) item).info);
} else {
throw new IllegalStateException("Unknown root: " + item);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index a13fccc..ce98db2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -113,7 +113,7 @@
private View.OnClickListener mSaveListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- final BaseActivity activity = BaseActivity.get(SaveFragment.this);
+ final DocumentsActivity activity = DocumentsActivity.get(SaveFragment.this);
if (mReplaceTarget != null) {
activity.onSaveRequested(mReplaceTarget);
} else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
index 976f21d..f89b182 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -16,50 +16,29 @@
package com.android.documentsui;
-
import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
import static com.android.documentsui.DirectoryFragment.ANIM_UP;
import android.app.Activity;
-import android.app.Fragment;
import android.app.FragmentManager;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
-import android.content.ComponentName;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
import android.graphics.Point;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Debug;
import android.provider.DocumentsContract;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v4.widget.DrawerLayout.DrawerListener;
+import android.provider.DocumentsContract.Root;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
import android.widget.Spinner;
-import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -70,55 +49,37 @@
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
-import libcore.io.IoUtils;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
-import java.util.concurrent.Executor;
+/**
+ * Activity providing a directly launchable file management activity.
+ */
public class StandaloneActivity extends BaseActivity {
public static final String TAG = "StandaloneFileManagement";
- private static final String EXTRA_STATE = "state";
-
- private static final int CODE_FORWARD = 42;
-
- private SearchView mSearchView;
-
private Toolbar mToolbar;
private Spinner mToolbarStack;
-
private Toolbar mRootsToolbar;
-
- private ActionBarDrawerToggle mDrawerToggle;
-
private DirectoryContainerView mDirectoryContainer;
-
- private boolean mIgnoreNextNavigation;
- private boolean mIgnoreNextClose;
- private boolean mIgnoreNextCollapse;
-
- private boolean mSearchExpanded;
-
- private RootsCache mRoots;
+ private SearchManager mSearchManager;
private State mState;
+ private ItemSelectedListener mStackListener;
+ private BaseAdapter mStackAdapter;
+
+ public StandaloneActivity() {
+ super(TAG);
+ }
@Override
public void onCreate(Bundle icicle) {
- // Debug.waitForDebugger();
super.onCreate(icicle);
- mRoots = DocumentsApplication.getRootsCache(this);
-
setResult(Activity.RESULT_CANCELED);
setContentView(R.layout.activity);
final Context context = this;
- final Resources res = getResources();
// Strongly define our horizontal dimension; we leave vertical as
final WindowManager.LayoutParams a = getWindow().getAttributes();
@@ -131,16 +92,17 @@
mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
- if (icicle != null) {
- mState = icicle.getParcelable(EXTRA_STATE);
- } else {
- buildDefaultState();
- }
+ mState = (icicle != null)
+ ? icicle.<State>getParcelable(EXTRA_STATE)
+ : buildDefaultState();
+ mSearchManager = new SearchManager();
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitleTextAppearance(context,
android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+ mStackAdapter = new StackAdapter();
+ mStackListener = new ItemSelectedListener();
mToolbarStack = (Spinner) findViewById(R.id.stack);
mToolbarStack.setOnItemSelectedListener(mStackListener);
@@ -167,87 +129,33 @@
}
}
- private void buildDefaultState() {
- mState = new State();
+ private State buildDefaultState() {
+ State state = new State();
final Intent intent = getIntent();
- mState.action = State.ACTION_BROWSE_ALL;
- mState.acceptMimes = new String[] { "*/*" };
- mState.allowMultiple = true;
- mState.acceptMimes = new String[] { intent.getType() };
- mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
- mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
- mState.showAdvanced = mState.forceAdvanced
+ state.action = State.ACTION_BROWSE_ALL;
+ state.acceptMimes = new String[] { "*/*" };
+ state.allowMultiple = true;
+ state.acceptMimes = new String[] { intent.getType() };
+ state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+ state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ state.showAdvanced = state.forceAdvanced
| LocalPreferences.getDisplayAdvancedDevices(this);
- mState.showSize = true;
+ state.showSize = true;
final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
if (stack != null)
- mState.stack = stack;
- }
+ state.stack = stack;
- private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
- private volatile boolean mRestoredStack;
- private volatile boolean mExternal;
-
- @Override
- protected Void doInBackground(Void... params) {
- // Restore last stack for calling package
- final String packageName = getCallingPackageMaybeExtra();
- final Cursor cursor = getContentResolver()
- .query(RecentsProvider.buildResume(packageName), null, null, null, null);
- try {
- if (cursor.moveToFirst()) {
- mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
- final byte[] rawStack = cursor.getBlob(
- cursor.getColumnIndex(ResumeColumns.STACK));
- DurableUtils.readFromArray(rawStack, mState.stack);
- mRestoredStack = true;
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to resume: " + e);
- } finally {
- IoUtils.closeQuietly(cursor);
- }
-
- if (mRestoredStack) {
- // Update the restored stack to ensure we have freshest data
- final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
- try {
- mState.stack.updateRoot(matchingRoots);
- mState.stack.updateDocuments(getContentResolver());
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- mState.stack.reset();
- mRestoredStack = false;
- }
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if (isDestroyed()) return;
- mState.restored = true;
- onCurrentDirectoryChanged(ANIM_NONE);
- }
+ return state;
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- if (mDrawerToggle != null) {
- mDrawerToggle.syncState();
- }
updateActionBar();
}
@Override
- public void setRootsDrawerOpen(boolean open) {
- Log.w(TAG, "Trying to change state of roots drawer to > " + (open ? "open" : "closed"));
- // throw new UnsupportedOperationException();
- }
-
public void updateActionBar() {
final RootInfo root = getCurrentRoot();
mToolbar.setNavigationIcon(
@@ -255,7 +163,7 @@
mToolbar.setNavigationContentDescription(R.string.drawer_open);
mToolbar.setNavigationOnClickListener(null);
- if (mSearchExpanded) {
+ if (mSearchManager.isExpanded()) {
mToolbar.setTitle(null);
mToolbarStack.setVisibility(View.GONE);
mToolbarStack.setAdapter(null);
@@ -269,7 +177,7 @@
mToolbarStack.setVisibility(View.VISIBLE);
mToolbarStack.setAdapter(mStackAdapter);
- mIgnoreNextNavigation = true;
+ mStackListener.mIgnoreNextNavigation = true;
mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
}
}
@@ -277,220 +185,60 @@
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
+ boolean showMenu = super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.activity, menu);
- for (int i = 0; i < menu.size(); i++) {
- final MenuItem item = menu.getItem(i);
- switch (item.getItemId()) {
- case R.id.menu_advanced:
- case R.id.menu_file_size:
- break;
- default:
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- }
+ expandMenus(menu);
- final MenuItem searchMenu = menu.findItem(R.id.menu_search);
- mSearchView = (SearchView) searchMenu.getActionView();
- mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- mSearchExpanded = true;
- mState.currentSearch = query;
- mSearchView.clearFocus();
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
+ this.mSearchManager.install(menu.findItem(R.id.menu_search));
- @Override
- public boolean onQueryTextChange(String newText) {
- return false;
- }
- });
-
- searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
- @Override
- public boolean onMenuItemActionExpand(MenuItem item) {
- mSearchExpanded = true;
- updateActionBar();
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(MenuItem item) {
- mSearchExpanded = false;
- if (mIgnoreNextCollapse) {
- mIgnoreNextCollapse = false;
- return true;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
- });
-
- mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
- @Override
- public boolean onClose() {
- mSearchExpanded = false;
- if (mIgnoreNextClose) {
- mIgnoreNextClose = false;
- return false;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return false;
- }
- });
-
- return true;
+ return showMenu;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- final FragmentManager fm = getFragmentManager();
-
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem search = menu.findItem(R.id.menu_search);
final MenuItem sort = menu.findItem(R.id.menu_sort);
final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
final MenuItem grid = menu.findItem(R.id.menu_grid);
final MenuItem list = menu.findItem(R.id.menu_list);
final MenuItem advanced = menu.findItem(R.id.menu_advanced);
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+ final MenuItem settings = menu.findItem(R.id.menu_settings);
- sort.setVisible(cwd != null);
grid.setVisible(mState.derivedMode != State.MODE_GRID);
list.setVisible(mState.derivedMode != State.MODE_LIST);
- if (mState.currentSearch != null) {
- // Search uses backend ranking; no sorting
- sort.setVisible(false);
-
- search.expandActionView();
-
- mSearchView.setIconified(false);
- mSearchView.clearFocus();
- mSearchView.setQuery(mState.currentSearch, false);
- } else {
- mIgnoreNextClose = true;
- mSearchView.setIconified(true);
- mSearchView.clearFocus();
-
- mIgnoreNextCollapse = true;
- search.collapseActionView();
- }
+ mSearchManager.update(root);
+ sort.setVisible(cwd != null && !mSearchManager.isSearching());
// Only sort by size when visible
sortSize.setVisible(mState.showSize);
- fileSize.setVisible(true);
- search.setVisible(true);
- createDir.setVisible(true);
- advanced.setVisible(true);
+ createDir.setVisible(cwd != null
+ && cwd.isCreateSupported()
+ && !mSearchManager.isSearching()
+ && !root.isDownloads());
+
+ fileSize.setVisible(cwd != null);
+ advanced.setVisible(cwd != null);
advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
? R.string.menu_advanced_hide : R.string.menu_advanced_show);
fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
? R.string.menu_file_size_hide : R.string.menu_file_size_show);
+ settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
return true;
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
- return true;
- }
-
- final int id = item.getItemId();
- if (id == android.R.id.home) {
- onBackPressed();
- return true;
- } else if (id == R.id.menu_create_dir) {
- CreateDirectoryFragment.show(getFragmentManager());
- return true;
- } else if (id == R.id.menu_search) {
- return false;
- } else if (id == R.id.menu_sort_name) {
- setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
- return true;
- } else if (id == R.id.menu_sort_date) {
- setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
- return true;
- } else if (id == R.id.menu_sort_size) {
- setUserSortOrder(State.SORT_ORDER_SIZE);
- return true;
- } else if (id == R.id.menu_grid) {
- setUserMode(State.MODE_GRID);
- return true;
- } else if (id == R.id.menu_list) {
- setUserMode(State.MODE_LIST);
- return true;
- } else if (id == R.id.menu_advanced) {
- setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
- return true;
- } else if (id == R.id.menu_file_size) {
- setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void setDisplayAdvancedDevices(boolean display) {
- LocalPreferences.setDisplayAdvancedDevices(this, display);
- mState.showAdvanced = mState.forceAdvanced | display;
- RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- private void setDisplayFileSize(boolean display) {
- LocalPreferences.setDisplayFileSize(this, display);
- mState.showSize = display;
- DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- @Override
- public void onStateChanged() {
- invalidateOptionsMenu();
- }
-
- /**
- * Set state sort order based on explicit user action.
- */
- private void setUserSortOrder(int sortOrder) {
- mState.userSortOrder = sortOrder;
- DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
- }
-
- /**
- * Set state mode based on explicit user action.
- */
- private void setUserMode(int mode) {
- mState.userMode = mode;
- DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
- }
-
- @Override
- public void setPending(boolean pending) {
- final SaveFragment save = SaveFragment.get(getFragmentManager());
- if (save != null) {
- save.setPending(pending);
- }
- }
-
- @Override
public void onBackPressed() {
if (!mState.stackTouched) {
super.onBackPressed();
@@ -507,130 +255,12 @@
}
@Override
- protected void onSaveInstanceState(Bundle state) {
- super.onSaveInstanceState(state);
- state.putParcelable(EXTRA_STATE, mState);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle state) {
- super.onRestoreInstanceState(state);
- }
-
- private BaseAdapter mStackAdapter = new BaseAdapter() {
- @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 = 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 ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final DocumentInfo doc = getItem(position);
-
- if (position == 0) {
- final RootInfo root = getCurrentRoot();
- title.setText(root.title);
- subdir.setVisibility(View.GONE);
- } else {
- title.setText(doc.displayName);
- subdir.setVisibility(View.VISIBLE);
- }
-
- return convertView;
- }
- };
-
- private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (mIgnoreNextNavigation) {
- mIgnoreNextNavigation = false;
- return;
- }
-
- while (mState.stack.size() > position + 1) {
- mState.stackTouched = true;
- mState.stack.pop();
- }
- onCurrentDirectoryChanged(ANIM_UP);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Ignored
- }
- };
-
- @Override
- public RootInfo getCurrentRoot() {
- if (mState.stack.root != null) {
- return mState.stack.root;
- } else {
- return mRoots.getRecentsRoot();
- }
- }
-
- public DocumentInfo getCurrentDirectory() {
- return mState.stack.peek();
- }
-
- private String getCallingPackageMaybeExtra() {
- final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
- return (extra != null) ? extra : getCallingPackage();
- }
-
- public Executor getCurrentExecutor() {
- final DocumentInfo cwd = getCurrentDirectory();
- if (cwd != null && cwd.authority != null) {
- return ProviderExecutor.forAuthority(cwd.authority);
- } else {
- return AsyncTask.THREAD_POOL_EXECUTOR;
- }
- }
-
- @Override
public State getDisplayState() {
return mState;
}
- private void onCurrentDirectoryChanged(int anim) {
+ @Override
+ void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
@@ -654,109 +284,10 @@
DirectoryFragment.showNormal(fm, root, cwd, anim);
}
}
-
- final RootsFragment roots = RootsFragment.get(fm);
- if (roots != null) {
- roots.onCurrentRootChanged();
- }
-
- updateActionBar();
- invalidateOptionsMenu();
- dumpStack();
- }
-
- @Override
- public void onStackPicked(DocumentStack stack) {
- try {
- // Update the restored stack to ensure we have freshest data
- stack.updateDocuments(getContentResolver());
-
- mState.stack = stack;
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
-
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- }
- }
-
- @Override
- public void onRootPicked(RootInfo root, boolean closeDrawer) {
- // Clear entire backstack and start in new root
- mState.stack.root = root;
- mState.stack.clear();
- mState.stackTouched = true;
-
- if (!mRoots.isRecentsRoot(root)) {
- new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
- } else {
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
- }
-
- private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
- private RootInfo mRoot;
-
- public PickRootTask(RootInfo root) {
- mRoot = root;
- }
-
- @Override
- protected DocumentInfo doInBackground(Void... params) {
- try {
- final Uri uri = DocumentsContract.buildDocumentUri(
- mRoot.authority, mRoot.documentId);
- return DocumentInfo.fromUri(getContentResolver(), uri);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to find root", e);
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(DocumentInfo result) {
- if (result != null) {
- mState.stack.push(result);
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
- }
- }
-
- @Override
- public void onAppPicked(ResolveInfo info) {
- final Intent intent = new Intent(getIntent());
- intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- intent.setComponent(new ComponentName(
- info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
- startActivityForResult(intent, CODE_FORWARD);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "onActivityResult() code=" + resultCode);
-
- // Only relay back results when not canceled; otherwise stick around to
- // let the user pick another app/backend.
- if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
- // Remember that we last picked via external app
- final String packageName = getCallingPackageMaybeExtra();
- final ContentValues values = new ContentValues();
- values.put(ResumeColumns.EXTERNAL, 1);
- getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
- // Pass back result to original caller
- setResult(resultCode, data);
- finish();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
}
@Override
public void onDocumentPicked(DocumentInfo doc) {
- final FragmentManager fm = getFragmentManager();
if (doc.isDirectory()) {
mState.stack.push(doc);
mState.stackTouched = true;
@@ -780,27 +311,12 @@
}
@Override
- public void onSaveRequested(DocumentInfo replaceTarget) {
- new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
- public void onSaveRequested(String mimeType, String displayName) {
- new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
- public void onPickRequested(DocumentInfo pickTarget) {
- final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
- pickTarget.documentId);
- new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor());
- }
-
- private void saveStackBlocking() {
+ void saveStackBlocking() {
final ContentResolver resolver = getContentResolver();
final ContentValues values = new ContentValues();
- final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
+ final byte[] rawStack = DurableUtils.writeToArrayOrNull(
+ getDisplayState().stack);
// Remember location for next app launch
final String packageName = getCallingPackageMaybeExtra();
@@ -810,7 +326,8 @@
resolver.insert(RecentsProvider.buildResume(packageName), values);
}
- private void onFinished(Uri... uris) {
+ @Override
+ void onTaskFinished(Uri... uris) {
Log.d(TAG, "onFinished() " + Arrays.toString(uris));
final Intent intent = new Intent();
@@ -832,106 +349,4 @@
setResult(Activity.RESULT_OK, intent);
finish();
}
-
- private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
- private final String mMimeType;
- private final String mDisplayName;
-
- public CreateFinishTask(String mimeType, String displayName) {
- mMimeType = mimeType;
- mDisplayName = displayName;
- }
-
- @Override
- protected void onPreExecute() {
- setPending(true);
- }
-
- @Override
- protected Uri doInBackground(Void... params) {
- final ContentResolver resolver = getContentResolver();
- final DocumentInfo cwd = getCurrentDirectory();
-
- ContentProviderClient client = null;
- Uri childUri = null;
- try {
- client = DocumentsApplication.acquireUnstableProviderOrThrow(
- resolver, cwd.derivedUri.getAuthority());
- childUri = DocumentsContract.createDocument(
- client, cwd.derivedUri, mMimeType, mDisplayName);
- } catch (Exception e) {
- Log.w(TAG, "Failed to create document", e);
- } finally {
- ContentProviderClient.releaseQuietly(client);
- }
-
- if (childUri != null) {
- saveStackBlocking();
- }
-
- return childUri;
- }
-
- @Override
- protected void onPostExecute(Uri result) {
- if (result != null) {
- onFinished(result);
- } else {
- Toast.makeText(StandaloneActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
- .show();
- }
-
- setPending(false);
- }
- }
-
- private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri[] mUris;
-
- public ExistingFinishTask(Uri... uris) {
- mUris = uris;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUris);
- }
- }
-
- private class PickFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri mUri;
-
- public PickFinishTask(Uri uri) {
- mUri = uri;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUri);
- }
- }
-
- private void dumpStack() {
- Log.d(TAG, "Current stack: ");
- Log.d(TAG, " * " + mState.stack.root);
- for (DocumentInfo doc : mState.stack) {
- Log.d(TAG, " +-- " + doc);
- }
- }
-
- public static BaseActivity get(Fragment fragment) {
- return (BaseActivity) fragment.getActivity();
- }
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9f80fd8..5d02576 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -460,6 +460,7 @@
// set to null if in idle mode; while in this mode, any alarms we don't want
// to run during this time are placed in mPendingWhileIdleAlarms
Alarm mPendingIdleUntil = null;
+ Alarm mNextWakeFromIdle = null;
final ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
public AlarmManagerService(Context context) {
@@ -583,7 +584,7 @@
final int mAlarmType;
InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
- int alarmType, String tag) {
+ int alarmType, String tag, long nowELAPSED) {
mPendingIntent = pendingIntent;
mWorkSource = workSource;
mTag = tag;
@@ -593,6 +594,7 @@
fs = new FilterStats(mBroadcastStats, mTag);
mBroadcastStats.filterStats.put(mTag, fs);
}
+ fs.lastTime = nowELAPSED;
mFilterStats = fs;
mAlarmType = alarmType;
}
@@ -602,6 +604,7 @@
final BroadcastStats mBroadcastStats;
final String mTag;
+ long lastTime;
long aggregateTime;
int count;
int numWakeup;
@@ -806,33 +809,38 @@
setImplLocked(a, false, doValidate);
}
+ private void updateNextWakeFromIdleFuzzLocked() {
+ if (mNextWakeFromIdle != null) {
+
+ }
+ }
+
private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
- // This is a special alarm that will put the system idle until it goes off.
+ // This is a special alarm that will put the system into idle until it goes off.
// The caller has given the time they want this to happen at, however we need
// to pull that earlier if there are existing alarms that have requested to
// bring us out of idle.
- final int N = mAlarmBatches.size();
- for (int i = 0; i < N; i++) {
- Batch b = mAlarmBatches.get(i);
- if (a.whenElapsed > b.end) {
- // There are no interesting things happening before our idle until,
- // so keep the requested time.
- break;
- }
- if ((b.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
- a.when = a.whenElapsed = a.maxWhenElapsed = b.end;
- break;
- }
+ if (mNextWakeFromIdle != null) {
+ a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
}
// Add fuzz to make the alarm go off some time before the actual desired time.
final long nowElapsed = SystemClock.elapsedRealtime();
- long fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
+ final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
if (fuzz > 0) {
if (mRandom == null) {
mRandom = new Random();
}
- a.whenElapsed -= mRandom.nextLong() % fuzz;
+ final int delta = mRandom.nextInt(fuzz);
+ a.whenElapsed -= delta;
+ if (false) {
+ Slog.d(TAG, "Alarm when: " + a.whenElapsed);
+ Slog.d(TAG, "Delta until alarm: " + (a.whenElapsed-nowElapsed));
+ Slog.d(TAG, "Applied fuzz: " + fuzz);
+ Slog.d(TAG, "Final delta: " + delta);
+ Slog.d(TAG, "Final when: " + a.whenElapsed);
+ }
+ a.when = a.maxWhenElapsed = a.whenElapsed;
}
} else if (mPendingIdleUntil != null) {
@@ -869,11 +877,16 @@
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
mPendingIdleUntil = a;
needRebatch = true;
- } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0 && mPendingIdleUntil != null) {
- // If we are adding an alarm that asks to wake from idle, and we are currently
- // idling, then we need to rebatch alarms in case the idle until time needs to
- // be updated.
- needRebatch = true;
+ } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
+ mNextWakeFromIdle = a;
+ // If this wake from idle is earlier than whatever was previously scheduled,
+ // and we are currently idling, then we need to rebatch alarms in case the idle
+ // until time needs to be updated.
+ if (mPendingIdleUntil != null) {
+ needRebatch = true;
+ }
+ }
}
if (!rebatching) {
@@ -960,6 +973,11 @@
}
@Override
+ public long getNextWakeFromIdleTime() {
+ return getNextWakeFromIdleTimeImpl();
+ }
+
+ @Override
public AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false /* allowAll */, false /* requireFull */,
@@ -1030,10 +1048,15 @@
pw.println();
pw.println("Idle mode state:");
pw.print(" Idling until: "); pw.println(mPendingIdleUntil);
- mPendingIdleUntil.dump(pw, " ", nowELAPSED, nowRTC, sdf);
+ mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf);
pw.println(" Pending alarms:");
dumpAlarmList(pw, mPendingWhileIdleAlarms, " ", nowELAPSED, nowRTC, sdf);
}
+ if (mNextWakeFromIdle != null) {
+ pw.println();
+ pw.print(" Next wake from idle: "); pw.println(mNextWakeFromIdle);
+ mNextWakeFromIdle.dump(pw, " ", nowRTC, nowELAPSED, sdf);
+ }
pw.println();
pw.print("Past-due non-wakeup alarms: ");
@@ -1140,7 +1163,10 @@
TimeUtils.formatDuration(fs.aggregateTime, pw);
pw.print(" "); pw.print(fs.numWakeup);
pw.print(" wakes " ); pw.print(fs.count);
- pw.print(" alarms: ");
+ pw.print(" alarms, last ");
+ TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
+ pw.println(":");
+ pw.print(" ");
pw.print(fs.mTag);
pw.println();
}
@@ -1216,7 +1242,13 @@
return null;
}
- private AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
+ long getNextWakeFromIdleTimeImpl() {
+ synchronized (mLock) {
+ return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
+ }
+ }
+
+ AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
synchronized (mLock) {
return mNextAlarmClockForUser.get(userId);
}
@@ -1398,6 +1430,9 @@
mPendingIdleUntil = null;
restorePending = true;
}
+ if (mNextWakeFromIdle != null && mNextWakeFromIdle.operation.equals(operation)) {
+ mNextWakeFromIdle = null;
+ }
rebatchAllAlarmsLocked(true);
if (restorePending) {
restorePendingWhileIdleAlarmsLocked();
@@ -1585,11 +1620,19 @@
Alarm alarm = batch.get(i);
alarm.count = 1;
triggerList.add(alarm);
+ if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
+ alarm.tag);
+ }
if (mPendingIdleUntil == alarm) {
mPendingIdleUntil = null;
rebatchAllAlarmsLocked(false);
restorePendingWhileIdleAlarmsLocked();
}
+ if (mNextWakeFromIdle == alarm) {
+ mNextWakeFromIdle = null;
+ rebatchAllAlarmsLocked(false);
+ }
// Recurring alarms may have passed several alarm intervals while the
// phone was asleep or off, so pass a trigger count when sending them.
@@ -1655,7 +1698,7 @@
public final long origWhen;
public final boolean wakeup;
public final PendingIntent operation;
- public final String tag;
+ public final String tag;
public final WorkSource workSource;
public final int flags;
public int count;
@@ -1725,6 +1768,12 @@
pw.print(" repeatInterval="); pw.print(repeatInterval);
pw.print(" count="); pw.print(count);
pw.print(" flags=0x"); pw.println(Integer.toHexString(flags));
+ if (alarmClock != null) {
+ pw.print(prefix); pw.println("Alarm clock:");
+ pw.print(prefix); pw.print(" triggerTime=");
+ pw.println(sdf.format(new Date(alarmClock.getTriggerTime())));
+ pw.print(prefix); pw.print(" showIntent="); pw.println(alarmClock.getShowIntent());
+ }
pw.print(prefix); pw.print("operation="); pw.println(operation);
}
}
@@ -1762,11 +1811,11 @@
}
}
- static long fuzzForDuration(long duration) {
+ static int fuzzForDuration(long duration) {
if (duration < 15*60*1000) {
// If the duration until the time is less than 15 minutes, the maximum fuzz
// is the duration.
- return duration;
+ return (int)duration;
} else if (duration < 90*60*1000) {
// If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes,
return 15*60*1000;
@@ -1824,7 +1873,7 @@
mWakeLock.acquire();
}
final InFlight inflight = new InFlight(AlarmManagerService.this,
- alarm.operation, alarm.workSource, alarm.type, alarm.tag);
+ alarm.operation, alarm.workSource, alarm.type, alarm.tag, nowELAPSED);
mInFlight.add(inflight);
mBroadcastRefCount++;
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index a227341..abd2ca0 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -173,6 +173,13 @@
# ---------------------------
33000 wp_wallpaper_crashed (component|3)
+# ---------------------------
+# Device idle
+# ---------------------------
+34000 device_idle (state|1|5), (reason|3)
+34001 device_idle_step
+34002 device_idle_wake_from_idle (is_idle|1|5), (reason|3)
+
# ---------------------------
# ConnectivityService.java
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index a72c77e..d3240ec 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -550,14 +550,19 @@
}
}
- try {
- // Convert properties to string contents and send it to HAL.
- ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
- properties.store(baos, null);
- native_configuration_update(baos.toString());
- Log.d(TAG, "final config = " + baos.toString());
- } catch (IOException ex) {
- Log.w(TAG, "failed to dump properties contents");
+ if (native_is_gnss_configuration_supported()) {
+ try {
+ // Convert properties to string contents and send it to HAL.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+ properties.store(baos, null);
+ native_configuration_update(baos.toString());
+ Log.d(TAG, "final config = " + baos.toString());
+ } catch (IOException ex) {
+ Log.w(TAG, "failed to dump properties contents");
+ }
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
+ + " supported");
}
// SUPL_ES configuration.
@@ -732,16 +737,21 @@
}
if (info != null) {
- boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
- boolean networkAvailable = info.isAvailable() && dataEnabled;
- String defaultApn = getSelectedApn();
- if (defaultApn == null) {
- defaultApn = "dummy-apn";
- }
+ if (native_is_agps_ril_supported()) {
+ boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
+ boolean networkAvailable = info.isAvailable() && dataEnabled;
+ String defaultApn = getSelectedApn();
+ if (defaultApn == null) {
+ defaultApn = "dummy-apn";
+ }
- native_update_network_state(info.isConnected(), info.getType(),
- info.isRoaming(), networkAvailable,
- info.getExtraInfo(), defaultApn);
+ native_update_network_state(info.isConnected(), info.getType(),
+ info.isRoaming(), networkAvailable,
+ info.getExtraInfo(), defaultApn);
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped network state update because AGPS-RIL in GPS HAL is not"
+ + " supported");
+ }
}
if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
@@ -1752,7 +1762,7 @@
// NI Client support
//=============================================================
private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
- // Sends a response for an NI reqeust to HAL.
+ // Sends a response for an NI request to HAL.
@Override
public boolean sendNiResponse(int notificationId, int userResponse)
{
@@ -1843,7 +1853,7 @@
private void requestSetID(int flags) {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
- int type = AGPS_SETID_TYPE_NONE;
+ int type = AGPS_SETID_TYPE_NONE;
String data = "";
if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
@@ -1994,20 +2004,26 @@
.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
// listen for events
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
- intentFilter.addDataScheme("sms");
- intentFilter.addDataAuthority("localhost","7275");
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+ IntentFilter intentFilter;
+ if (native_is_agps_ril_supported()) {
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+ intentFilter.addDataScheme("sms");
+ intentFilter.addDataAuthority("localhost", "7275");
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
- try {
- intentFilter.addDataType("application/vnd.omaloc-supl-init");
- } catch (IntentFilter.MalformedMimeTypeException e) {
- Log.w(TAG, "Malformed SUPL init mime type");
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+ try {
+ intentFilter.addDataType("application/vnd.omaloc-supl-init");
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.w(TAG, "Malformed SUPL init mime type");
+ }
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped registration for SMS/WAP-PUSH messages because AGPS Ril in GPS"
+ + " HAL is not supported");
}
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
@@ -2187,6 +2203,8 @@
static { class_init_native(); }
private static native void class_init_native();
private static native boolean native_is_supported();
+ private static native boolean native_is_agps_ril_supported();
+ private static native boolean native_is_gnss_configuration_supported();
private native boolean native_init();
private native void native_cleanup();
diff --git a/services/core/java/com/android/server/power/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
index a23a87b..6b29b9a 100644
--- a/services/core/java/com/android/server/power/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -41,6 +41,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.server.SystemService;
import com.android.server.am.BatteryStatsService;
+import com.android.server.EventLogTags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -100,6 +101,11 @@
* Scaling factor to apply to current idle timeout each time we cycle through that state.
*/
private static final float DEFAULT_IDLE_FACTOR = 2f;
+ /**
+ * This is the minimum time we will allow until the next upcoming alarm for us to
+ * actually go in to idle mode.
+ */
+ private static final long DEFAULT_MIN_TIME_TO_ALARM = 60*60*1000L;
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
@@ -239,7 +245,7 @@
becomeInactiveIfAppropriateLocked();
} else if (screenOn) {
mScreenOn = true;
- becomeActiveLocked();
+ becomeActiveLocked("screen");
}
}
@@ -249,12 +255,13 @@
becomeInactiveIfAppropriateLocked();
} else if (charging) {
mCharging = charging;
- becomeActiveLocked();
+ becomeActiveLocked("charging");
}
}
- void becomeActiveLocked() {
+ void becomeActiveLocked(String reason) {
if (mState != STATE_ACTIVE) {
+ EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason);
mLocalPowerManager.setDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -281,10 +288,22 @@
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
scheduleAlarmLocked(mInactiveTimeout, false);
+ EventLogTags.writeDeviceIdle(mState, "no activity");
}
}
void stepIdleStateLocked() {
+ EventLogTags.writeDeviceIdleStep();
+
+ final long now = SystemClock.elapsedRealtime();
+ if ((now+DEFAULT_MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
+ // Whoops, there is an upcoming alarm. We don't actually want to go idle.
+ if (mState != STATE_ACTIVE) {
+ becomeActiveLocked("alarm");
+ }
+ return;
+ }
+
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
@@ -295,6 +314,7 @@
mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT;
mNextIdleDelay = DEFAULT_IDLE_TIMEOUT;
mState = STATE_IDLE_PENDING;
+ EventLogTags.writeDeviceIdle(mState, "step");
break;
case STATE_IDLE_PENDING:
// We have been waiting to become idle, and now it is time! This is the
@@ -307,6 +327,7 @@
mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
}
mState = STATE_IDLE;
+ EventLogTags.writeDeviceIdle(mState, "step");
mLocalPowerManager.setDeviceIdleMode(true);
try {
mNetworkPolicyManager.setDeviceIdleMode(true);
@@ -323,6 +344,7 @@
mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
}
mState = STATE_IDLE_PENDING;
+ EventLogTags.writeDeviceIdle(mState, "step");
mLocalPowerManager.setDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -352,6 +374,7 @@
}
mState = STATE_ACTIVE;
mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+ EventLogTags.writeDeviceIdle(mState, "motion");
becomeInactiveIfAppropriateLocked();
}
}
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 0cd6eb5..3804e1d 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -509,13 +509,22 @@
}
}
-static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* /* env */,
- jclass /* clazz */) {
- if (sGpsInterface != NULL) {
- return JNI_TRUE;
- } else {
- return JNI_FALSE;
- }
+static jboolean android_location_GpsLocationProvider_is_supported(
+ JNIEnv* /* env */, jclass /* clazz */)
+{
+ return (sGpsInterface != NULL) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_is_agps_ril_supported(
+ JNIEnv* /* env */, jclass /* clazz */)
+{
+ return (sAGpsRilInterface != NULL) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_gpsLocationProvider_is_gnss_configuration_supported(
+ JNIEnv* /* env */, jclass /* jclazz */)
+{
+ return (sGnssConfigurationInterface != NULL) ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
@@ -715,14 +724,10 @@
sGpsInterface->inject_location(latitude, longitude, accuracy);
}
-static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* /* env */,
- jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_supports_xtra(
+ JNIEnv* /* env */, jobject /* obj */)
{
- if (sGpsXtraInterface != NULL) {
- return JNI_TRUE;
- } else {
- return JNI_FALSE;
- }
+ return (sGpsXtraInterface != NULL) ? JNI_TRUE : JNI_FALSE;
}
static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject /* obj */,
@@ -844,13 +849,10 @@
}
}
-static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* /* env */,
- jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_is_geofence_supported(
+ JNIEnv* /* env */, jobject /* obj */)
{
- if (sGpsGeofencingInterface != NULL) {
- return JNI_TRUE;
- }
- return JNI_FALSE;
+ return (sGpsGeofencingInterface != NULL) ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* /* env */,
@@ -1436,6 +1438,10 @@
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
{"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
+ {"native_is_agps_ril_supported", "()Z",
+ (void*)android_location_GpsLocationProvider_is_agps_ril_supported},
+ {"native_is_gnss_configuration_supported", "()Z",
+ (void*)android_location_gpsLocationProvider_is_gnss_configuration_supported},
{"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},
{"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup},
{"native_set_position_mode",
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 17db3fb..7b58755 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -470,8 +470,6 @@
/**
* Get signal level as an int from 0..4
- *
- * @hide
*/
public int getLevel() {
int level;