diff --git a/api/current.xml b/api/current.xml index 50546d3629a0f..dc4ba2c9bc5da 100644 --- a/api/current.xml +++ b/api/current.xml @@ -28030,6 +28030,19 @@ + + + + + + + + + + @@ -223896,7 +223924,7 @@ deprecated="not deprecated" visibility="public" > - + @@ -247337,7 +247365,7 @@ 0) { + op.removed = new ArrayList(N); + for (int i=0; i mPendingActions; + Runnable[] mTmpActions; + boolean mExecutingActions; + ArrayList mActive; ArrayList mAdded; ArrayList mAvailIndices; @@ -91,6 +95,13 @@ public class FragmentManager { Bundle mStateBundle = null; SparseArray 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(); + } + 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(); @@ -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; imust 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); } @@ -246,6 +259,62 @@ public class ListFragment extends Fragment { return mList; } + /** + * 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. */ @@ -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 a8a5ca4674106..5d417a0ece944 100644 --- a/core/java/android/app/LoaderManagingFragment.java +++ b/core/java/android/app/LoaderManagingFragment.java @@ -44,7 +44,7 @@ public abstract class LoaderManagingFragment extends Fragment * when the new loader completes it's work. The callback will be delivered before the old loader * is destroyed. */ - protected Loader startLoading(int id, Bundle args) { + public Loader startLoading(int id, Bundle args) { LoaderInfo 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 0000000000000..14140322b7cd4 --- /dev/null +++ b/core/res/res/layout/list_content_rich.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6b371df717a64..de1ea68a71e59 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) --> Attention + + Loading... + ON