summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml34
-rw-r--r--core/java/android/app/Activity.java4
-rw-r--r--core/java/android/app/BackStackEntry.java37
-rw-r--r--core/java/android/app/FragmentManager.java63
-rw-r--r--core/java/android/app/ListFragment.java108
-rw-r--r--core/java/android/app/LoaderManagingFragment.java2
-rw-r--r--core/res/res/layout/list_content_rich.xml56
-rw-r--r--core/res/res/values/strings.xml3
8 files changed, 288 insertions, 19 deletions
diff --git a/api/current.xml b/api/current.xml
index 50546d3..dc4ba2c 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -28030,6 +28030,19 @@
<parameter name="id" type="long">
</parameter>
</method>
+<method name="setEmptyText"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
<method name="setListAdapter"
return="void"
abstract="false"
@@ -28043,6 +28056,21 @@
<parameter name="adapter" type="android.widget.ListAdapter">
</parameter>
</method>
+<method name="setListShown"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="shown" type="boolean">
+</parameter>
+<parameter name="animate" type="boolean">
+</parameter>
+</method>
<method name="setSelection"
return="void"
abstract="false"
@@ -28152,7 +28180,7 @@
static="false"
final="false"
deprecated="not deprecated"
- visibility="protected"
+ visibility="public"
>
<parameter name="id" type="int">
</parameter>
@@ -223896,7 +223924,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
@@ -247337,7 +247365,7 @@
<method name="availableProcessors"
return="int"
abstract="false"
- native="false"
+ native="true"
synchronized="false"
static="false"
final="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4a47b66..91ff0a5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4036,6 +4036,7 @@ public class Activity extends ContextThemeWrapper
final void performStart() {
mCalled = false;
+ mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
if (!mCalled) {
throw new SuperNotCalledException(
@@ -4074,6 +4075,8 @@ public class Activity extends ContextThemeWrapper
final void performResume() {
performRestart();
+ mFragments.execPendingActions();
+
mLastNonConfigurationInstances = null;
// First call onResume() -before- setting mResumed, so we don't
@@ -4091,6 +4094,7 @@ public class Activity extends ContextThemeWrapper
mCalled = false;
mFragments.dispatchResume();
+ mFragments.execPendingActions();
onPostResume();
if (!mCalled) {
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java
index 5e9aea5..c958e26 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackEntry.java
@@ -18,6 +18,7 @@ package android.app;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
import java.util.ArrayList;
@@ -28,14 +29,30 @@ final class BackStackState implements Parcelable {
final String mName;
public BackStackState(FragmentManager fm, BackStackEntry bse) {
- mOps = new int[bse.mNumOp*4];
+ int numRemoved = 0;
BackStackEntry.Op op = bse.mHead;
+ while (op != null) {
+ if (op.removed != null) numRemoved += op.removed.size();
+ op = op.next;
+ }
+ mOps = new int[bse.mNumOp*5 + numRemoved];
+
+ op = bse.mHead;
int pos = 0;
while (op != null) {
mOps[pos++] = op.cmd;
mOps[pos++] = op.fragment.mIndex;
mOps[pos++] = op.enterAnim;
mOps[pos++] = op.exitAnim;
+ if (op.removed != null) {
+ final int N = op.removed.size();
+ mOps[pos++] = N;
+ for (int i=0; i<N; i++) {
+ mOps[pos++] = op.removed.get(i).mIndex;
+ }
+ } else {
+ mOps[pos++] = 0;
+ }
op = op.next;
}
mTransition = bse.mTransition;
@@ -61,6 +78,13 @@ final class BackStackState implements Parcelable {
op.fragment = f;
op.enterAnim = mOps[pos++];
op.exitAnim = mOps[pos++];
+ final int N = mOps[pos++];
+ if (N > 0) {
+ op.removed = new ArrayList<Fragment>(N);
+ for (int i=0; i<N; i++) {
+ op.removed.add(fm.mActive.get(mOps[pos++]));
+ }
+ }
bse.addOp(op);
}
bse.mTransition = mTransition;
@@ -96,6 +120,8 @@ final class BackStackState implements Parcelable {
* @hide Entry of an operation on the fragment back stack.
*/
final class BackStackEntry implements FragmentTransaction, Runnable {
+ static final String TAG = "BackStackEntry";
+
final FragmentManager mManager;
static final int OP_NULL = 0;
@@ -265,11 +291,14 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
public void commit() {
if (mCommitted) throw new IllegalStateException("commit already called");
+ if (FragmentManager.DEBUG) Log.v(TAG, "Commit: " + this);
mCommitted = true;
- mManager.mActivity.mHandler.post(this);
+ mManager.enqueueAction(this);
}
public void run() {
+ if (FragmentManager.DEBUG) Log.v(TAG, "Run: " + this);
+
Op op = mHead;
while (op != null) {
switch (op.cmd) {
@@ -286,6 +315,8 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
if (mManager.mAdded != null) {
for (int i=0; i<mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
+ if (FragmentManager.DEBUG) Log.v(TAG,
+ "OP_REPLACE: adding=" + f + " old=" + old);
if (old.mContainerId == f.mContainerId) {
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
@@ -350,6 +381,8 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
}
public void popFromBackStack() {
+ if (FragmentManager.DEBUG) Log.v(TAG, "popFromBackStack: " + this);
+
Op op = mTail;
while (op != null) {
switch (op.cmd) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index c0ab0b5..b8eeb09 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -77,6 +77,10 @@ public class FragmentManager {
static final boolean DEBUG = true;
static final String TAG = "FragmentManager";
+ ArrayList<Runnable> mPendingActions;
+ Runnable[] mTmpActions;
+ boolean mExecutingActions;
+
ArrayList<Fragment> mActive;
ArrayList<Fragment> mAdded;
ArrayList<Integer> mAvailIndices;
@@ -91,6 +95,13 @@ public class FragmentManager {
Bundle mStateBundle = null;
SparseArray<Parcelable> mStateArray = null;
+ Runnable mExecCommit = new Runnable() {
+ @Override
+ public void run() {
+ execPendingActions();
+ }
+ };
+
Animation loadAnimation(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animation animObj = fragment.onCreateAnimation(transitionStyle, enter,
@@ -486,6 +497,52 @@ public class FragmentManager {
return null;
}
+ public void enqueueAction(Runnable action) {
+ synchronized (this) {
+ if (mPendingActions == null) {
+ mPendingActions = new ArrayList<Runnable>();
+ }
+ mPendingActions.add(action);
+ if (mPendingActions.size() == 1) {
+ mActivity.mHandler.removeCallbacks(mExecCommit);
+ mActivity.mHandler.post(mExecCommit);
+ }
+ }
+ }
+
+ /**
+ * Only call from main thread!
+ */
+ public void execPendingActions() {
+ if (mExecutingActions) {
+ throw new IllegalStateException("Recursive entry to execPendingActions");
+ }
+
+ while (true) {
+ int numActions;
+
+ synchronized (this) {
+ if (mPendingActions == null || mPendingActions.size() == 0) {
+ return;
+ }
+
+ numActions = mPendingActions.size();
+ if (mTmpActions == null || mTmpActions.length < numActions) {
+ mTmpActions = new Runnable[numActions];
+ }
+ mPendingActions.toArray(mTmpActions);
+ mPendingActions.clear();
+ mActivity.mHandler.removeCallbacks(mExecCommit);
+ }
+
+ mExecutingActions = true;
+ for (int i=0; i<numActions; i++) {
+ mTmpActions[i].run();
+ }
+ mExecutingActions = false;
+ }
+ }
+
public void addBackStackState(BackStackEntry state) {
if (mBackStack == null) {
mBackStack = new ArrayList<BackStackEntry>();
@@ -503,8 +560,9 @@ public class FragmentManager {
return false;
}
final BackStackEntry bss = mBackStack.remove(last);
- handler.post(new Runnable() {
+ enqueueAction(new Runnable() {
public void run() {
+ if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
bss.popFromBackStack();
moveToState(mCurState, reverseTransit(bss.getTransition()),
bss.getTransitionStyle(), true);
@@ -526,9 +584,10 @@ public class FragmentManager {
for (int i=mBackStack.size()-1; i>index; i--) {
states.add(mBackStack.remove(i));
}
- handler.post(new Runnable() {
+ enqueueAction(new Runnable() {
public void run() {
for (int i=0; i<states.size(); i++) {
+ if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
states.get(i).popFromBackStack();
}
moveToState(mCurState, true);
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index 0d8ec98..548dd61 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -21,9 +21,11 @@ import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
+import android.widget.TextView;
/**
* An fragment that displays a list of items by binding to a data source such as
@@ -154,17 +156,28 @@ public class ListFragment extends Fragment {
ListAdapter mAdapter;
ListView mList;
+ View mEmptyView;
+ TextView mStandardEmptyView;
+ View mProgressContainer;
+ View mListContainer;
+ boolean mSetEmptyView;
+ boolean mListShown;
public ListFragment() {
}
/**
- * Provide default implementation to return a simple list view.
+ * Provide default implementation to return a simple list view. Subclasses
+ * can override to replace with their own layout. If doing so, the
+ * returned view hierarchy <em>must</em> have a ListView whose id
+ * is {@link android.R.id.list android.R.id.list} and can optionally
+ * have a sibling view id {@link android.R.id.empty android.R.id.empty}
+ * that is to be shown when the list is empty.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- return inflater.inflate(com.android.internal.R.layout.list_content,
+ return inflater.inflate(com.android.internal.R.layout.list_content_rich,
container, false);
}
@@ -247,6 +260,62 @@ public class ListFragment extends Fragment {
}
/**
+ * The default content for a ListFragment has a TextView that can
+ * be shown when the list is empty. If you would like to have it
+ * shown, call this method to supply the text it should use.
+ */
+ public void setEmptyText(CharSequence text) {
+ ensureList();
+ if (mStandardEmptyView == null) {
+ throw new IllegalStateException("Can't be used with a custom content view");
+ }
+ if (!mSetEmptyView) {
+ mSetEmptyView = true;
+ mList.setEmptyView(mStandardEmptyView);
+ }
+ }
+
+ /**
+ * Control whether the list is being displayed. You can make it not
+ * displayed if you are waiting for the initial data to show in it. During
+ * this time an indeterminant progress indicator will be shown instead.
+ *
+ * @param shown If true, the list view is shown; if false, the progress
+ * indicator. The initial value is true.
+ * @param animate If true, an animation will be used to transition to the
+ * new state.
+ */
+ public void setListShown(boolean shown, boolean animate) {
+ ensureList();
+ if (mProgressContainer == null) {
+ throw new IllegalStateException("Can't be used with a custom content view");
+ }
+ if (mListShown == shown) {
+ return;
+ }
+ mListShown = shown;
+ if (shown) {
+ if (animate) {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_out));
+ mListContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_in));
+ }
+ mProgressContainer.setVisibility(View.GONE);
+ mListContainer.setVisibility(View.VISIBLE);
+ } else {
+ if (animate) {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_in));
+ mListContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_out));
+ }
+ mProgressContainer.setVisibility(View.VISIBLE);
+ mListContainer.setVisibility(View.GONE);
+ }
+ }
+
+ /**
* Get the ListAdapter associated with this activity's ListView.
*/
public ListAdapter getListAdapter() {
@@ -261,16 +330,33 @@ public class ListFragment extends Fragment {
if (root == null) {
throw new IllegalStateException("Content view not yet created");
}
- View emptyView = root.findViewById(com.android.internal.R.id.empty);
- mList = (ListView)root.findViewById(com.android.internal.R.id.list);
- if (mList == null) {
- throw new RuntimeException(
- "Your content must have a ListView whose id attribute is " +
- "'android.R.id.list'");
- }
- if (emptyView != null) {
- mList.setEmptyView(emptyView);
+ if (root instanceof ListView) {
+ mList = (ListView)root;
+ } else {
+ mStandardEmptyView = (TextView)root.findViewById(
+ com.android.internal.R.id.internalEmpty);
+ if (mStandardEmptyView == null) {
+ mEmptyView = root.findViewById(android.R.id.empty);
+ }
+ mProgressContainer = root.findViewById(com.android.internal.R.id.progressContainer);
+ mListContainer = root.findViewById(com.android.internal.R.id.listContainer);
+ View rawListView = root.findViewById(android.R.id.list);
+ if (!(rawListView instanceof ListView)) {
+ throw new RuntimeException(
+ "Content has view with id attribute 'android.R.id.list' "
+ + "that is not a ListView class");
+ }
+ mList = (ListView)rawListView;
+ if (mList == null) {
+ throw new RuntimeException(
+ "Your content must have a ListView whose id attribute is " +
+ "'android.R.id.list'");
+ }
+ if (mEmptyView != null) {
+ mList.setEmptyView(mEmptyView);
+ }
}
+ mListShown = true;
mList.setOnItemClickListener(mOnClickListener);
if (mAdapter != null) {
setListAdapter(mAdapter);
diff --git a/core/java/android/app/LoaderManagingFragment.java b/core/java/android/app/LoaderManagingFragment.java
index a8a5ca4..5d417a0 100644
--- a/core/java/android/app/LoaderManagingFragment.java
+++ b/core/java/android/app/LoaderManagingFragment.java
@@ -44,7 +44,7 @@ public abstract class LoaderManagingFragment<D> extends Fragment
* when the new loader completes it's work. The callback will be delivered before the old loader
* is destroyed.
*/
- protected Loader<D> startLoading(int id, Bundle args) {
+ public Loader<D> startLoading(int id, Bundle args) {
LoaderInfo<D> info = mLoaders.get(id);
if (info != null) {
// Keep track of the previous instance of this loader so we can destroy
diff --git a/core/res/res/layout/list_content_rich.xml b/core/res/res/layout/list_content_rich.xml
new file mode 100644
index 0000000..1414032
--- /dev/null
+++ b/core/res/res/layout/list_content_rich.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:id="@+id/progressContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:gravity="center">
+
+ <ProgressBar style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/loading"
+ android:paddingTop="4dip"
+ android:singleLine="true" />
+
+ </LinearLayout>
+
+ <FrameLayout android:id="@+id/listContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false" />
+ <TextView android:id="@+android:id/internalEmpty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+ </FrameLayout>
+
+</FrameLayout>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6b371df..de1ea68 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1919,6 +1919,9 @@
combined with setIcon(android.R.drawable.ic_dialog_alert) -->
<string name="dialog_alert_title">Attention</string>
+ <!-- Text shown by list fragment when waiting for data to display. -->
+ <string name="loading">Loading...</string>
+
<!-- Default text for a button that can be toggled on and off. -->
<string name="capital_on">ON</string>
<!-- Default text for a button that can be toggled on and off. -->