am 54a3bc88: am 0972ae15: Merge "Save directory state and animate." into klp-dev

* commit '54a3bc8866ab5b3d324fe4efe08fa379af3249b3':
  Save directory state and animate.
This commit is contained in:
Jeff Sharkey
2013-09-18 18:19:14 -07:00
committed by Android Git Automerger
10 changed files with 290 additions and 29 deletions

View File

@@ -0,0 +1,22 @@
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="1"
android:valueTo="0"
android:propertyName="position"
android:valueType="floatType"
android:duration="@android:integer/config_mediumAnimTime"
android:interpolator="@android:interpolator/decelerate_quad" />

View File

@@ -0,0 +1,21 @@
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="0"
android:propertyName="position"
android:valueType="floatType"
android:duration="@android:integer/config_mediumAnimTime" />

View File

@@ -0,0 +1,22 @@
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="1"
android:propertyName="position"
android:valueType="floatType"
android:duration="@android:integer/config_mediumAnimTime"
android:interpolator="@android:interpolator/accelerate_quad" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

View File

@@ -24,7 +24,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
<com.android.documentsui.DirectoryContainerView
android:id="@+id/container_directory"
android:layout_width="match_parent"
android:layout_height="0dip"

View File

@@ -14,9 +14,10 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="@drawable/ic_dir_shadow">
<TextView
android:id="@android:id/empty"
@@ -40,4 +41,4 @@
android:listSelector="@android:color/transparent"
android:visibility="gone" />
</FrameLayout>
</com.android.documentsui.DirectoryView>

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.documentsui;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import java.util.ArrayList;
public class DirectoryContainerView extends FrameLayout {
private boolean mDisappearingFirst = false;
public DirectoryContainerView(Context context) {
super(context);
setClipChildren(false);
}
public DirectoryContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
setClipChildren(false);
}
@Override
protected void dispatchDraw(Canvas canvas) {
final ArrayList<View> disappearing = mDisappearingChildren;
if (mDisappearingFirst && disappearing != null) {
for (int i = 0; i < disappearing.size(); i++) {
super.drawChild(canvas, disappearing.get(i), getDrawingTime());
}
}
super.dispatchDraw(canvas);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (mDisappearingFirst && mDisappearingChildren != null
&& mDisappearingChildren.contains(child)) {
return false;
}
return super.drawChild(canvas, child, drawingTime);
}
public void setDrawDisappearingFirst(boolean disappearingFirst) {
mDisappearingFirst = disappearingFirst;
}
}

View File

@@ -43,12 +43,14 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.text.format.Time;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater;
@@ -96,7 +98,13 @@ public class DirectoryFragment extends Fragment {
public static final int TYPE_SEARCH = 2;
public static final int TYPE_RECENT_OPEN = 3;
public static final int ANIM_NONE = 1;
public static final int ANIM_SIDE = 2;
public static final int ANIM_DOWN = 3;
public static final int ANIM_UP = 4;
private int mType = TYPE_NORMAL;
private String mStateKey;
private int mLastMode = MODE_UNKNOWN;
private int mLastSortOrder = SORT_ORDER_UNKNOWN;
@@ -113,39 +121,61 @@ public class DirectoryFragment extends Fragment {
private static final String EXTRA_ROOT = "root";
private static final String EXTRA_DOC = "doc";
private static final String EXTRA_QUERY = "query";
private static final String EXTRA_IGNORE_STATE = "ignoreState";
private static AtomicInteger sLoaderId = new AtomicInteger(4000);
private final int mLoaderId = sLoaderId.incrementAndGet();
public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc) {
show(fm, TYPE_NORMAL, root, doc, null);
public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
show(fm, TYPE_NORMAL, root, doc, null, anim);
}
public static void showSearch(FragmentManager fm, RootInfo root, String query) {
show(fm, TYPE_SEARCH, root, null, query);
public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) {
show(fm, TYPE_SEARCH, root, null, query, anim);
}
public static void showRecentsOpen(FragmentManager fm) {
show(fm, TYPE_RECENT_OPEN, null, null, null);
public static void showRecentsOpen(FragmentManager fm, int anim) {
show(fm, TYPE_RECENT_OPEN, null, null, null, anim);
}
private static void show(
FragmentManager fm, int type, RootInfo root, DocumentInfo doc, String query) {
private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
String query, int anim) {
final Bundle args = new Bundle();
args.putInt(EXTRA_TYPE, type);
args.putParcelable(EXTRA_ROOT, root);
args.putParcelable(EXTRA_DOC, doc);
args.putString(EXTRA_QUERY, query);
final FragmentTransaction ft = fm.beginTransaction();
switch (anim) {
case ANIM_SIDE:
args.putBoolean(EXTRA_IGNORE_STATE, true);
break;
case ANIM_DOWN:
args.putBoolean(EXTRA_IGNORE_STATE, true);
ft.setCustomAnimations(R.animator.dir_down, R.animator.dir_frozen);
break;
case ANIM_UP:
ft.setCustomAnimations(R.animator.dir_frozen, R.animator.dir_up);
break;
}
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args);
final FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container_directory, fragment);
ft.commitAllowingStateLoss();
}
private static String buildStateKey(RootInfo root, DocumentInfo doc) {
final StringBuilder builder = new StringBuilder();
builder.append(root != null ? root.authority : "null").append(';');
builder.append(root != null ? root.rootId : "null").append(';');
builder.append(doc != null ? doc.documentId : "null");
return builder.toString();
}
public static DirectoryFragment get(FragmentManager fm) {
// TODO: deal with multiple directories shown at once
return (DirectoryFragment) fm.findFragmentById(R.id.container_directory);
@@ -184,6 +214,7 @@ public class DirectoryFragment extends Fragment {
mAdapter = new DocumentsAdapter();
mType = getArguments().getInt(EXTRA_TYPE);
mStateKey = buildStateKey(root, doc);
if (mType == TYPE_RECENT_OPEN) {
// Hide titles when showing recents for picking images/videos
@@ -241,11 +272,16 @@ public class DirectoryFragment extends Fragment {
updateDisplayState();
if (mLastSortOrder != state.derivedSortOrder) {
mLastSortOrder = state.derivedSortOrder;
// Restore any previous instance state
final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
getView().restoreHierarchyState(container);
} else if (mLastSortOrder != state.derivedSortOrder) {
mListView.smoothScrollToPosition(0);
mGridView.smoothScrollToPosition(0);
}
mLastSortOrder = state.derivedSortOrder;
}
@Override
@@ -260,6 +296,17 @@ public class DirectoryFragment extends Fragment {
updateDisplayState();
}
@Override
public void onStop() {
super.onStop();
// Remember last scroll location
final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
getView().saveHierarchyState(container);
final State state = getDisplayState(this);
state.dirState.put(mStateKey, container);
}
@Override
public void onResume() {
super.onResume();

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.documentsui;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
public class DirectoryView extends FrameLayout {
private float mPosition = 0f;
private int mWidth;
public DirectoryView(Context context) {
super(context);
}
public DirectoryView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setBackground(Drawable background) {
final Rect rect = new Rect();
background.getPadding(rect);
final InsetDrawable inset = new InsetDrawable(background, -rect.left, 0, 0, 0);
super.setBackground(inset);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
setPosition(mPosition);
}
public float getPosition() {
return mPosition;
}
public void setPosition(float position) {
mPosition = position;
setX((mWidth > 0) ? (mPosition * mWidth) : 0);
setAlpha(1f - position);
}
}

View File

@@ -16,6 +16,10 @@
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 static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE;
import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
@@ -44,6 +48,7 @@ import android.graphics.drawable.InsetDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.support.v4.app.ActionBarDrawerToggle;
@@ -51,6 +56,7 @@ import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -73,12 +79,14 @@ 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 com.google.common.collect.Maps;
import libcore.io.IoUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class DocumentsActivity extends Activity {
@@ -94,6 +102,8 @@ public class DocumentsActivity extends Activity {
private ActionBarDrawerToggle mDrawerToggle;
private View mRootsContainer;
private DirectoryContainerView mDirectoryContainer;
private boolean mIgnoreNextNavigation;
private boolean mIgnoreNextClose;
private boolean mIgnoreNextCollapse;
@@ -165,6 +175,8 @@ public class DocumentsActivity extends Activity {
mRootsContainer = findViewById(R.id.container_roots);
}
mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
if (icicle != null) {
mState = icicle.getParcelable(EXTRA_STATE);
} else {
@@ -195,7 +207,7 @@ public class DocumentsActivity extends Activity {
RootsFragment.show(getFragmentManager(), null);
}
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_NONE);
}
private void buildDefaultState() {
@@ -397,7 +409,7 @@ public class DocumentsActivity extends Activity {
public boolean onQueryTextSubmit(String query) {
mState.currentSearch = query;
mSearchView.clearFocus();
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_NONE);
return true;
}
@@ -421,7 +433,7 @@ public class DocumentsActivity extends Activity {
}
mState.currentSearch = null;
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_NONE);
return true;
}
});
@@ -435,7 +447,7 @@ public class DocumentsActivity extends Activity {
}
mState.currentSearch = null;
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_NONE);
return false;
}
});
@@ -595,7 +607,7 @@ public class DocumentsActivity extends Activity {
final int size = mState.stack.size();
if (size > 1) {
mState.stack.pop();
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_UP);
} else if (size == 1 && !isRootsDrawerOpen()) {
// TODO: open root drawer once we can capture back key
super.onBackPressed();
@@ -690,7 +702,7 @@ public class DocumentsActivity extends Activity {
mState.stackTouched = true;
mState.stack.pop();
}
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_UP);
return true;
}
};
@@ -711,17 +723,19 @@ public class DocumentsActivity extends Activity {
return mState;
}
private void onCurrentDirectoryChanged() {
private void onCurrentDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
if (cwd == null) {
// No directory means recents
if (mState.action == ACTION_CREATE) {
RecentsCreateFragment.show(fm);
} else {
DirectoryFragment.showRecentsOpen(fm);
DirectoryFragment.showRecentsOpen(fm, anim);
// Start recents in relevant mode
final boolean acceptImages = MimePredicate.mimeMatches(
@@ -732,10 +746,10 @@ public class DocumentsActivity extends Activity {
} else {
if (mState.currentSearch != null) {
// Ongoing search
DirectoryFragment.showSearch(fm, root, mState.currentSearch);
DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
} else {
// Normal boring directory
DirectoryFragment.showNormal(fm, root, cwd);
DirectoryFragment.showNormal(fm, root, cwd, anim);
}
}
@@ -760,7 +774,7 @@ public class DocumentsActivity extends Activity {
public void onStackPicked(DocumentStack stack) {
mState.stack = stack;
mState.stackTouched = true;
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_SIDE);
}
public void onRootPicked(RootInfo root, boolean closeDrawer) {
@@ -772,11 +786,14 @@ public class DocumentsActivity extends Activity {
if (!mRoots.isRecentsRoot(root)) {
try {
final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId);
onDocumentPicked(DocumentInfo.fromUri(getContentResolver(), uri));
final DocumentInfo doc = DocumentInfo.fromUri(getContentResolver(), uri);
mState.stack.push(doc);
mState.stackTouched = true;
onCurrentDirectoryChanged(ANIM_SIDE);
} catch (FileNotFoundException e) {
}
} else {
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_SIDE);
}
if (closeDrawer) {
@@ -798,7 +815,7 @@ public class DocumentsActivity extends Activity {
if (doc.isDirectory()) {
mState.stack.push(doc);
mState.stackTouched = true;
onCurrentDirectoryChanged();
onCurrentDirectoryChanged(ANIM_DOWN);
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
// Explicit file picked, return
onFinished(doc.derivedUri);
@@ -924,6 +941,9 @@ public class DocumentsActivity extends Activity {
/** Currently active search, overriding any stack. */
public String currentSearch;
/** Instance state for every shown directory */
public HashMap<String, SparseArray<Parcelable>> dirState = Maps.newHashMap();
public static final int ACTION_OPEN = 1;
public static final int ACTION_CREATE = 2;
public static final int ACTION_GET_CONTENT = 3;
@@ -956,6 +976,7 @@ public class DocumentsActivity extends Activity {
out.writeInt(stackTouched ? 1 : 0);
DurableUtils.writeToParcel(out, stack);
out.writeString(currentSearch);
out.writeMap(dirState);
}
public static final Creator<State> CREATOR = new Creator<State>() {
@@ -973,6 +994,7 @@ public class DocumentsActivity extends Activity {
state.stackTouched = in.readInt() != 0;
DurableUtils.readFromParcel(in, state.stack);
state.currentSearch = in.readString();
in.readMap(state.dirState, null);
return state;
}