cancel() causes the animation to
+ * stop in its tracks, sending an {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
+ * its listeners, followed by an {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
+ */
+ public void cancel() {
+ }
+
+ /**
+ * Ends the animation. This causes the animation to assign the end value of the property being
+ * animated, then calling the {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
+ * its listeners.
+ */
+ public void end() {
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ public abstract long getStartDelay();
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ public abstract void setStartDelay(long startDelay);
+
+
+ /**
+ * Sets the length of the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ */
+ public abstract void setDuration(long duration);
+
+ /**
+ * Gets the length of the animation.
+ *
+ * @return The length of the animation, in milliseconds.
+ */
+ public abstract long getDuration();
+
+ /**
+ * The time interpolator used in calculating the elapsed fraction of this animation. The
+ * interpolator determines whether the animation runs with linear or non-linear motion,
+ * such as acceleration and deceleration. The default value is
+ * {@link android.view.animation.AccelerateDecelerateInterpolator}
+ *
+ * @param value the interpolator to be used by this animation
+ */
+ public abstract void setInterpolator(Interpolator value);
+
+ /**
+ * Returns whether this Animator is currently running (having been started and not yet ended).
+ * @return Whether the Animator is running.
+ */
+ public abstract boolean isRunning();
+
+ /**
+ * Adds a listener to the set of listeners that are sent events through the life of an
+ * animation, such as start, repeat, and end.
+ *
+ * @param listener the listener to be added to the current set of listeners for this animation.
+ */
+ public void addListener(AnimatorListener listener) {
+ if (mListeners == null) {
+ mListeners = new ArrayListAnimator object.
+ *
+ * @return ArrayListgetListeners() followed by calling clear() on the
+ * returned list of listeners.
+ */
+ public void removeAllListeners() {
+ if (mListeners != null) {
+ mListeners.clear();
+ mListeners = null;
+ }
+ }
+
+ @Override
+ public Animator clone() {
+ try {
+ final Animator anim = (Animator) super.clone();
+ if (mListeners != null) {
+ ArrayListAn animation listener receives notifications from an animation. + * Notifications indicate animation related events, such as the end or the + * repetition of the animation.
+ */ + public static interface AnimatorListener { + /** + *Notifies the start of the animation.
+ * + * @param animation The started animation. + */ + void onAnimationStart(Animator animation); + + /** + *Notifies the end of the animation. This callback is not invoked + * for animations with repeat count set to INFINITE.
+ * + * @param animation The animation which reached its end. + */ + void onAnimationEnd(Animator animation); + + /** + *Notifies the cancellation of the animation. This callback is not invoked + * for animations with repeat count set to INFINITE.
+ * + * @param animation The animation which was canceled. + */ + void onAnimationCancel(Animator animation); + + /** + *Notifies the repetition of the animation.
+ * + * @param animation The animation which was repeated. + */ + void onAnimationRepeat(Animator animation); + } +} diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java new file mode 100644 index 0000000000000000000000000000000000000000..0016459cda1059330d9472f4cb3c6dc6d221bfb5 --- /dev/null +++ b/core/java/android/animation/AnimatorInflater.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 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. + */ +package android.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.content.res.Resources.NotFoundException; +import android.util.AttributeSet; +import android.util.Xml; +import android.view.animation.AnimationUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * This class is used to instantiate menu XML files into Animator objects. + *
+ * For performance reasons, menu inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource (R.
+ * something file.)
+ */
+public class AnimatorInflater {
+
+ /**
+ * These flags are used when parsing AnimatorSet objects
+ */
+ private static final int TOGETHER = 0;
+ private static final int SEQUENTIALLY = 1;
+
+ /**
+ * Enum values used in XML attributes to indicate the value for mValueType
+ */
+ private static final int VALUE_TYPE_FLOAT = 0;
+ private static final int VALUE_TYPE_INT = 1;
+ private static final int VALUE_TYPE_DOUBLE = 2;
+ private static final int VALUE_TYPE_COLOR = 3;
+ private static final int VALUE_TYPE_CUSTOM = 4;
+
+ /**
+ * Loads an {@link Animator} object from a resource
+ *
+ * @param context Application context used to access resources
+ * @param id The resource id of the animation to load
+ * @return The animator object reference by the specified id
+ * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
+ */
+ public static Animator loadAnimator(Context context, int id)
+ throws NotFoundException {
+
+ XmlResourceParser parser = null;
+ try {
+ parser = context.getResources().getAnimation(id);
+ return createAnimatorFromXml(context, parser);
+ } catch (XmlPullParserException ex) {
+ Resources.NotFoundException rnf =
+ new Resources.NotFoundException("Can't load animation resource ID #0x" +
+ Integer.toHexString(id));
+ rnf.initCause(ex);
+ throw rnf;
+ } catch (IOException ex) {
+ Resources.NotFoundException rnf =
+ new Resources.NotFoundException("Can't load animation resource ID #0x" +
+ Integer.toHexString(id));
+ rnf.initCause(ex);
+ throw rnf;
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+
+ return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
+ }
+
+ private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
+ AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
+ throws XmlPullParserException, IOException {
+
+ Animator anim = null;
+ ArrayList There are two different approaches to adding animations to a It is possible to set up a Note that Note that ending a Starting this The For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
+ * play when anim2 finishes, and anim4 to play when anim3 finishes: Note in the example that both {@link Builder#before(Animator)} and {@link
+ * Builder#after(Animator)} are used. These are just different ways of expressing the same
+ * relationship and are provided to make it easier to say things in a way that is more natural,
+ * depending on the situation. It is possible to make several calls into the same Note that it is possible to express relationships that cannot be resolved and will not
+ * result in sensible results. For example, One of the core concepts of these transition animations is that there are two core
+ * changes that cause the transition and four different animations that run because of
+ * those changes. The changes that trigger the transition are items being added to a container
+ * (referred to as an "appearing" transition) or removed from a container (also known as
+ * "disappearing"). The animations that run due to those events are one that animates
+ * items being added, one that animates items being removed, and two that animate the other
+ * items in the container that change due to the add/remove occurrence. Users of
+ * the transition may want different animations for the changing items depending on whether
+ * they are changing due to anappearing or disappearing event, so there is one animation for
+ * each of these variations of the changing event. Most of the API of this class is concerned
+ * with setting up the basic properties of the animations used in these four situations,
+ * or with setting up custom animations for any or all of the four. The animations specified for the transition, both the defaults and any custom animations
+ * set on the transition object, are templates only. That is, these animations exist to hold the
+ * basic animation properties, such as the duration, start delay, and properties being animated.
+ * But the actual target object, as well as the start and end values for those properties, are
+ * set automatically in the process of setting up the transition each time it runs. Each of the
+ * animations is cloned from the original copy and the clone is then populated with the dynamic
+ * values of the target being animated (such as one of the items in a layout container that is
+ * moving as a result of the layout event) as well as the values that are changing (such as the
+ * position and size of that object). The actual values that are pushed to each animation
+ * depends on what properties are specified for the animation. For example, the default
+ * CHANGE_APPEARING animation animates It is also worth noting that any and all animations (and their underlying
+ * PropertyValuesHolder objects) will have their start and end values set according
+ * to the pre- and post-layout values. So, for example, a custom animation on "alpha"
+ * as the CHANGE_APPEARING animation will inherit the real value of alpha on the target
+ * object (presumably 1) as its starting and ending value when the animation begins.
+ * Animations which need to use values at the beginning and end that may not match the
+ * values queried when the transition begins may need to use a different mechanism
+ * than a standard ObjectAnimator object. Note that the setter function derived from this property name
+ * must take the same parameter type as the
+ * If this ObjectAnimator has been set up to animate several properties together,
+ * using more than one PropertyValuesHolder objects, then setting the propertyName simply
+ * sets the propertyName in the first of those PropertyValuesHolder objects. Overriders of this method should call the superclass method to cause
+ * internal mechanisms to be set up correctly. Overrides of this method must call the superclass to perform the calculation
+ * of the animated value. If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function either
+ * derived automatically from Note that the setter function must take the same parameter type as the
+ * Note that the getter method is only called whether supplied here or derived
+ * from the property name, if one of Note that the getter function must return the same parameter type as the
+ * Note that the setter function derived from this property name
+ * must take the same parameter type as the
+ * There is a single timing pulse that all animations use. It runs in a
+ * custom handler to ensure that property changes happen on the UI thread. By default, ValueAnimator uses non-linear time interpolation, via the
+ * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
+ * out of an animation. This behavior can be changed by calling
+ * {@link ValueAnimator#setInterpolator(Interpolator)}. Overrides of this method should call the superclass method to ensure
+ * that internal mechanisms for the animation are set up correctly. If this ValueAnimator has only one set of values being animated between, this evaluator
+ * will be used for that set. If there are several sets of values being animated, which is
+ * the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator
+ * is assigned just to the first PropertyValuesHolder object. Overrides of this method must call the superclass to perform the calculation
+ * of the animated value. Notifies the occurrence of another frame of the animation. Example: setDisplayOptions(0, DISPLAY_HIDE_HOME) will disable the
+ * {@link #DISPLAY_HIDE_HOME} option.
+ * setDisplayOptions(DISPLAY_HIDE_HOME, DISPLAY_HIDE_HOME | DISPLAY_USE_LOGO)
+ * will enable {@link #DISPLAY_HIDE_HOME} and disable {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param options A combination of the bits defined by the DISPLAY_ constants
+ * defined in ActionBar.
+ * @param mask A bit mask declaring which display options should be changed.
+ */
+ public abstract void setDisplayOptions(int options, int mask);
+
+ /**
+ * Set the ActionBar's background.
+ *
+ * @param d Background drawable
+ */
+ public abstract void setBackgroundDrawable(Drawable d);
+
+ /**
+ * @return The current custom navigation view.
+ */
+ public abstract View getCustomNavigationView();
+
+ /**
+ * Returns the current ActionBar title in standard mode.
+ * Returns null if {@link #getNavigationMode()} would not return
+ * {@link #NAVIGATION_MODE_STANDARD}.
+ *
+ * @return The current ActionBar title or null.
+ */
+ public abstract CharSequence getTitle();
+
+ /**
+ * Returns the current ActionBar subtitle in standard mode.
+ * Returns null if {@link #getNavigationMode()} would not return
+ * {@link #NAVIGATION_MODE_STANDARD}.
+ *
+ * @return The current ActionBar subtitle or null.
+ */
+ public abstract CharSequence getSubtitle();
+
+ /**
+ * Returns the current navigation mode. The result will be one of:
+ * Tabs manage the hiding and showing of {@link Fragment}s.
+ */
+ public static abstract class Tab {
+ /**
+ * An invalid position for a tab.
+ *
+ * @see #getPosition()
+ */
+ public static final int INVALID_POSITION = -1;
+
+ /**
+ * Return the current position of this tab in the action bar.
+ *
+ * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
+ * the action bar.
+ */
+ public abstract int getPosition();
+
+ /**
+ * Return the icon associated with this tab.
+ *
+ * @return The tab's icon
+ */
+ public abstract Drawable getIcon();
+
+ /**
+ * Return the text of this tab.
+ *
+ * @return The tab's text
+ */
+ public abstract CharSequence getText();
+
+ /**
+ * Set the icon displayed on this tab.
+ *
+ * @param icon The drawable to use as an icon
+ */
+ public abstract void setIcon(Drawable icon);
+
+ /**
+ * Set the text displayed on this tab. Text may be truncated if there is not
+ * room to display the entire string.
+ *
+ * @param text The text to display
+ */
+ public abstract void setText(CharSequence text);
+
+ /**
+ * Set a custom view to be used for this tab. This overrides values set by
+ * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+ *
+ * @param view Custom view to be used as a tab.
+ */
+ public abstract void setCustomView(View view);
+
+ /**
+ * Retrieve a previously set custom view for this tab.
+ *
+ * @return The custom view set by {@link #setCustomView(View)}.
+ */
+ public abstract View getCustomView();
+
+ /**
+ * Give this Tab an arbitrary object to hold for later use.
+ *
+ * @param obj Object to store
+ */
+ public abstract void setTag(Object obj);
+
+ /**
+ * @return This Tab's tag object.
+ */
+ public abstract Object getTag();
+
+ /**
+ * Set the {@link TabListener} that will handle switching to and from this tab.
+ * All tabs must have a TabListener set before being added to the ActionBar.
+ *
+ * @param listener Listener to handle tab selection events
+ */
+ public abstract void setTabListener(TabListener listener);
+
+ /**
+ * Select this tab. Only valid if the tab has been added to the action bar.
+ */
+ public abstract void select();
+ }
+
+ /**
+ * Callback interface invoked when a tab is focused, unfocused, added, or removed.
+ */
+ public interface TabListener {
+ /**
+ * Called when a tab enters the selected state.
+ *
+ * @param tab The tab that was selected
+ * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+ * during a tab switch. The previous tab's unselect and this tab's select will be
+ * executed in a single transaction.
+ */
+ public void onTabSelected(Tab tab, FragmentTransaction ft);
+
+ /**
+ * Called when a tab exits the selected state.
+ *
+ * @param tab The tab that was unselected
+ * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+ * during a tab switch. This tab's unselect and the newly selected tab's select
+ * will be executed in a single transaction.
+ */
+ public void onTabUnselected(Tab tab, FragmentTransaction ft);
+ }
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 72bf825ea2c3659c07439a9692532482e939a048..ee49d97bea836cb4dbe8366992f20d75b25039de 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,19 +16,22 @@
package android.app;
+import com.android.internal.app.ActionBarImpl;
import com.android.internal.policy.PolicyManager;
import android.content.ComponentCallbacks;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
+import android.content.CursorLoader;
import android.content.IIntentSender;
+import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -39,6 +42,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -49,7 +53,9 @@ import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
+import android.view.ActionMode;
import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -58,18 +64,18 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.ViewManager;
import android.view.Window;
import android.view.WindowManager;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnCreateContextMenuListener;
-import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -110,6 +116,7 @@ import java.util.HashMap;
*
* Topics covered here:
* Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}, Activity
+ * implementations can make use of the {@link Fragment} class to better
+ * modularize their code, build more sophisticated user interfaces for larger
+ * screens, and help scale their application between small and large screens.
+ *
*
* This function is called purely as an optimization, and you must
* not rely on it being called. When it is called, a number of guarantees
* will be made to help optimize configuration switching:
@@ -1475,8 +1538,9 @@ public class Activity extends ContextThemeWrapper
* @return Returns the object previously returned by
* {@link #onRetainNonConfigurationChildInstances()}
*/
- HashMap If you use {@link #showDialog(int)}, the activity will call through to
* this method the first time, and hang onto it thereafter. Any dialog
* that is created by this method will automatically be saved and restored
@@ -2554,6 +2794,9 @@ public class Activity extends ContextThemeWrapper
* will be made with the same id the first time this is called for a given
* id. From thereafter, the dialog will be automatically saved and restored.
*
+ * If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * or later, consider instead using a {@link DialogFragment} instead.
+ *
* Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog, Bundle)} will
* be made to provide an opportunity to do any timely preparation.
*
@@ -3100,6 +3343,36 @@ public class Activity extends ContextThemeWrapper
}
}
+ /**
+ * This is called when a Fragment in this activity calls its
+ * {@link Fragment#startActivity} or {@link Fragment#startActivityForResult}
+ * method.
+ *
+ * This method throws {@link android.content.ActivityNotFoundException}
+ * if there was no Activity found to run the given Intent.
+ *
+ * @param fragment The fragment making the call.
+ * @param intent The intent to start.
+ * @param requestCode Reply request code. < 0 if reply is not requested.
+ *
+ * @throws android.content.ActivityNotFoundException
+ *
+ * @see Fragment#startActivity
+ * @see Fragment#startActivityForResult
+ */
+ public void startActivityFromFragment(Fragment fragment, Intent intent,
+ int requestCode) {
+ Instrumentation.ActivityResult ar =
+ mInstrumentation.execStartActivity(
+ this, mMainThread.getApplicationThread(), mToken, fragment,
+ intent, requestCode);
+ if (ar != null) {
+ mMainThread.sendActivityResult(
+ mToken, fragment.mWho, requestCode,
+ ar.getResultCode(), ar.getResultData());
+ }
+ }
+
/**
* Like {@link #startActivityFromChild(Activity, Intent, int)}, but
* taking a IntentSender; see
@@ -3258,6 +3531,19 @@ public class Activity extends ContextThemeWrapper
return mFinished;
}
+ /**
+ * Check to see whether this activity is in the process of being destroyed in order to be
+ * recreated with a new configuration. This is often used in
+ * {@link #onStop} to determine whether the state needs to be cleaned up or will be passed
+ * on to the next instance of the activity via {@link #onRetainNonConfigurationInstance()}.
+ *
+ * @return If the activity is being torn down in order to be recreated with a new configuration,
+ * returns true; else returns false.
+ */
+ public boolean isChangingConfigurations() {
+ return mChangingConfigurations;
+ }
+
/**
* Call this when your activity is done and should be closed. The
* ActivityResult is propagated back to whoever launched you via
@@ -3359,8 +3645,7 @@ public class Activity extends ContextThemeWrapper
* @see #createPendingResult
* @see #setResult(int)
*/
- protected void onActivityResult(int requestCode, int resultCode,
- Intent data) {
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
}
/**
@@ -3744,9 +4029,12 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Stub implementation of {@link android.view.LayoutInflater.Factory#onCreateView} used when
- * inflating with the LayoutInflater returned by {@link #getSystemService}. This
- * implementation simply returns null for all view names.
+ * Standard implementation of
+ * {@link android.view.LayoutInflater.Factory#onCreateView} used when
+ * inflating with the LayoutInflater returned by {@link #getSystemService}.
+ * This implementation does nothing and is for
+ * pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} apps. Newer apps
+ * should use {@link #onCreateView(View, String, Context, AttributeSet)}.
*
* @see android.view.LayoutInflater#createView
* @see android.view.Window#getLayoutInflater
@@ -3755,6 +4043,172 @@ public class Activity extends ContextThemeWrapper
return null;
}
+ /**
+ * Standard implementation of
+ * {@link android.view.LayoutInflater.Factory2#onCreateView(View, String, Context, AttributeSet)}
+ * used when inflating with the LayoutInflater returned by {@link #getSystemService}.
+ * This implementation handles See the Date Picker
* tutorial. Implementations should override this class and implement
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} to supply the
+ * content of the dialog. Alternatively, they can override
+ * {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such
+ * as an AlertDialog, with its own content.
+ *
+ * Topics covered here:
+ * DialogFragment does various things to keep the fragment's lifecycle
+ * driving it, instead of the Dialog. Note that dialogs are generally
+ * autonomous entities -- they are their own window, receiving their own
+ * input events, and often deciding on their own when to disappear (by
+ * receiving a back key event or the user clicking on a button).
+ *
+ * DialogFragment needs to ensure that what is happening with the Fragment
+ * and Dialog states remains consistent. To do this, it watches for dismiss
+ * events from the dialog and takes are of removing its own state when they
+ * happen. This means you should use {@link #show(FragmentManager, String)}
+ * or {@link #show(FragmentTransaction, String)} to add an instance of
+ * DialogFragment to your UI, as these keep track of how DialogFragment should
+ * remove itself when the dialog is dismissed.
+ *
+ *
+ * The simplest use of DialogFragment is as a floating container for the
+ * fragment's view hierarchy. A simple implementation may look like this:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ * dialog}
+ *
+ * An example showDialog() method on the Activity could be:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ * add_dialog}
+ *
+ * This removes any currently shown dialog, creates a new DialogFragment
+ * with an argument, and shows it as a new state on the back stack. When the
+ * transaction is popped, the current DialogFragment and its Dialog will be
+ * destroyed, and the previous one (if any) re-shown. Note that in this case
+ * DialogFragment will take care of popping the transaction of the Dialog
+ * is dismissed separately from it.
+ *
+ *
+ * Instead of (or in addition to) implementing {@link #onCreateView} to
+ * generate the view hierarchy inside of a dialog, you may implement
+ * {@link #onCreateDialog(Bundle)} to create your own custom Dialog object.
+ *
+ * This is most useful for creating an {@link AlertDialog}, allowing you
+ * to display standard alerts to the user that are managed by a fragment.
+ * A simple example implementation of this is:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ * dialog}
+ *
+ * The activity creating this fragment may have the following methods to
+ * show the dialog and receive results from it:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ * activity}
+ *
+ * Note that in this case the fragment is not placed on the back stack, it
+ * is just added as an indefinitely running fragment. Because dialogs normally
+ * are modal, this will still operate as a back stack, since the dialog will
+ * capture user input until it is dismissed. When it is dismissed, DialogFragment
+ * will take care of removing itself from its fragment manager.
+ *
+ *
+ * A DialogFragment can still optionally be used as a normal fragment, if
+ * desired. This is useful if you have a fragment that in some cases should
+ * be shown as a dialog and others embedded in a larger UI. This behavior
+ * will normally be automatically selected for you based on how you are using
+ * the fragment, but can be customized with {@link #setShowsDialog(boolean)}.
+ *
+ * For example, here is a simple dialog fragment:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ * dialog}
+ *
+ * An instance of this fragment can be created and shown as a dialog:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ * show_dialog}
+ *
+ * It can also be added as content in a view hierarchy:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ * embed}
+ */
+public class DialogFragment extends Fragment
+ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
+
+ /**
+ * Style for {@link #setStyle(int, int)}: a basic,
+ * normal dialog.
+ */
+ public static final int STYLE_NORMAL = 0;
+
+ /**
+ * Style for {@link #setStyle(int, int)}: don't include
+ * a title area.
+ */
+ public static final int STYLE_NO_TITLE = 1;
+
+ /**
+ * Style for {@link #setStyle(int, int)}: don't draw
+ * any frame at all; the view hierarchy returned by {@link #onCreateView}
+ * is entirely responsible for drawing the dialog.
+ */
+ public static final int STYLE_NO_FRAME = 2;
+
+ /**
+ * Style for {@link #setStyle(int, int)}: like
+ * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
+ * The user can not touch it, and its window will not receive input focus.
+ */
+ public static final int STYLE_NO_INPUT = 3;
+
+ private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
+ private static final String SAVED_STYLE = "android:style";
+ private static final String SAVED_THEME = "android:theme";
+ private static final String SAVED_CANCELABLE = "android:cancelable";
+ private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
+ private static final String SAVED_BACK_STACK_ID = "android:backStackId";
+
+ int mStyle = STYLE_NORMAL;
+ int mTheme = 0;
+ boolean mCancelable = true;
+ boolean mShowsDialog = true;
+ int mBackStackId = -1;
+
+ Dialog mDialog;
+ boolean mDestroyed;
+ boolean mRemoved;
+
+ public DialogFragment() {
+ }
+
+ /**
+ * Call to customize the basic appearance and behavior of the
+ * fragment's dialog. This can be used for some common dialog behaviors,
+ * taking care of selecting flags, theme, and other options for you. The
+ * same effect can be achieve by manually setting Dialog and Window
+ * attributes yourself. Calling this after the fragment's Dialog is
+ * created will have no effect.
+ *
+ * @param style Selects a standard style: may be {@link #STYLE_NORMAL},
+ * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
+ * {@link #STYLE_NO_INPUT}.
+ * @param theme Optional custom theme. If 0, an appropriate theme (based
+ * on the style) will be selected for you.
+ */
+ public void setStyle(int style, int theme) {
+ mStyle = style;
+ if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
+ mTheme = android.R.style.Theme_Dialog_NoFrame;
+ }
+ if (theme != 0) {
+ mTheme = theme;
+ }
+ }
+
+ /**
+ * @deprecated Please use {@link #show(FragmentManager, String)}.
+ */
+ @Deprecated
+ public void show(Activity activity, String tag) {
+ FragmentTransaction ft = activity.openFragmentTransaction();
+ ft.add(this, tag);
+ ft.commit();
+ }
+
+ /**
+ * Display the dialog, adding the fragment to the given FragmentManager. This
+ * is a convenience for explicitly creating a transaction, adding the
+ * fragment to it with the given tag, and committing it. This does
+ * not add the transaction to the back stack. When the fragment
+ * is dismissed, a new transaction will be executed to remove it from
+ * the activity.
+ * @param manager The FragmentManager this fragment will be added to.
+ * @param tag The tag for this fragment, as per
+ * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+ */
+ public void show(FragmentManager manager, String tag) {
+ FragmentTransaction ft = manager.openTransaction();
+ ft.add(this, tag);
+ ft.commit();
+ }
+
+ /**
+ * Display the dialog, adding the fragment using an existing transaction
+ * and then committing the transaction.
+ * @param transaction An existing transaction in which to add the fragment.
+ * @param tag The tag for this fragment, as per
+ * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+ * @return Returns the identifier of the committed transaction, as per
+ * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
+ */
+ public int show(FragmentTransaction transaction, String tag) {
+ transaction.add(this, tag);
+ mRemoved = false;
+ mBackStackId = transaction.commit();
+ return mBackStackId;
+ }
+
+ /**
+ * Dismiss the fragment and its dialog. If the fragment was added to the
+ * back stack, all back stack state up to and including this entry will
+ * be popped. Otherwise, a new transaction will be committed to remove
+ * the fragment.
+ */
+ public void dismiss() {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ mRemoved = true;
+ if (mBackStackId >= 0) {
+ getFragmentManager().popBackStack(mBackStackId,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ mBackStackId = -1;
+ } else {
+ FragmentTransaction ft = getFragmentManager().openTransaction();
+ ft.remove(this);
+ ft.commit();
+ }
+ }
+
+ public Dialog getDialog() {
+ return mDialog;
+ }
+
+ public int getTheme() {
+ return mTheme;
+ }
+
+ /**
+ * Control whether the shown Dialog is cancelable. Use this instead of
+ * directly calling {@link Dialog#setCancelable(boolean)
+ * Dialog.setCancelable(boolean)}, because DialogFragment needs to change
+ * its behavior based on this.
+ *
+ * @param cancelable If true, the dialog is cancelable. The default
+ * is true.
+ */
+ public void setCancelable(boolean cancelable) {
+ mCancelable = cancelable;
+ if (mDialog != null) mDialog.setCancelable(cancelable);
+ }
+
+ /**
+ * Return the current value of {@link #setCancelable(boolean)}.
+ */
+ public boolean getCancelable() {
+ return mCancelable;
+ }
+
+ /**
+ * Controls whether this fragment should be shown in a dialog. If not
+ * set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
+ * and the fragment's view hierarchy will thus not be added to it. This
+ * allows you to instead use it as a normal fragment (embedded inside of
+ * its activity).
+ *
+ * This is normally set for you based on whether the fragment is
+ * associated with a container view ID passed to
+ * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
+ * If the fragment was added with a container, setShowsDialog will be
+ * initialized to false; otherwise, it will be true.
+ *
+ * @param showsDialog If true, the fragment will be displayed in a Dialog.
+ * If false, no Dialog will be created and the fragment's view hierarchly
+ * left undisturbed.
+ */
+ public void setShowsDialog(boolean showsDialog) {
+ mShowsDialog = showsDialog;
+ }
+
+ /**
+ * Return the current value of {@link #setShowsDialog(boolean)}.
+ */
+ public boolean getShowsDialog() {
+ return mShowsDialog;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mShowsDialog = mContainerId == 0;
+
+ if (savedInstanceState != null) {
+ mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
+ mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
+ mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
+ mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+ mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
+ }
+ }
+
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new Dialog(getActivity(), getTheme());
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ }
+
+ public void onDismiss(DialogInterface dialog) {
+ if (!mRemoved) {
+ dismiss();
+ }
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (!mShowsDialog) {
+ return;
+ }
+
+ mDialog = onCreateDialog(savedInstanceState);
+ mDestroyed = false;
+ switch (mStyle) {
+ case STYLE_NO_INPUT:
+ mDialog.getWindow().addFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+ // fall through...
+ case STYLE_NO_FRAME:
+ case STYLE_NO_TITLE:
+ mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+ View view = getView();
+ if (view != null) {
+ if (view.getParent() != null) {
+ throw new IllegalStateException("DialogFragment can not be attached to a container view");
+ }
+ mDialog.setContentView(view);
+ }
+ mDialog.setOwnerActivity(getActivity());
+ mDialog.setCancelable(mCancelable);
+ mDialog.setOnCancelListener(this);
+ mDialog.setOnDismissListener(this);
+ if (savedInstanceState != null) {
+ Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
+ if (dialogState != null) {
+ mDialog.onRestoreInstanceState(dialogState);
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (mDialog != null) {
+ mRemoved = false;
+ mDialog.show();
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mDialog != null) {
+ Bundle dialogState = mDialog.onSaveInstanceState();
+ if (dialogState != null) {
+ outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
+ }
+ }
+ if (mStyle != STYLE_NORMAL) {
+ outState.putInt(SAVED_STYLE, mStyle);
+ }
+ if (mTheme != 0) {
+ outState.putInt(SAVED_THEME, mTheme);
+ }
+ if (!mCancelable) {
+ outState.putBoolean(SAVED_CANCELABLE, mCancelable);
+ }
+ if (!mShowsDialog) {
+ outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+ }
+ if (mBackStackId != -1) {
+ outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mDialog != null) {
+ mDialog.hide();
+ }
+ }
+
+ /**
+ * Remove dialog.
+ */
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mDestroyed = true;
+ if (mDialog != null) {
+ // Set removed here because this dismissal is just to hide
+ // the dialog -- we don't want this to cause the fragment to
+ // actually be removed.
+ mRemoved = true;
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..12bf7e5c5a0d30e0aebdbed709729d46aa13fa61
--- /dev/null
+++ b/core/java/android/app/Fragment.java
@@ -0,0 +1,1208 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app;
+
+import android.animation.Animator;
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AndroidRuntimeException;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnCreateContextMenuListener;
+import android.widget.AdapterView;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+final class FragmentState implements Parcelable {
+ final String mClassName;
+ final int mIndex;
+ final boolean mFromLayout;
+ final int mFragmentId;
+ final int mContainerId;
+ final String mTag;
+ final boolean mRetainInstance;
+ final Bundle mArguments;
+
+ Bundle mSavedFragmentState;
+
+ Fragment mInstance;
+
+ public FragmentState(Fragment frag) {
+ mClassName = frag.getClass().getName();
+ mIndex = frag.mIndex;
+ mFromLayout = frag.mFromLayout;
+ mFragmentId = frag.mFragmentId;
+ mContainerId = frag.mContainerId;
+ mTag = frag.mTag;
+ mRetainInstance = frag.mRetainInstance;
+ mArguments = frag.mArguments;
+ }
+
+ public FragmentState(Parcel in) {
+ mClassName = in.readString();
+ mIndex = in.readInt();
+ mFromLayout = in.readInt() != 0;
+ mFragmentId = in.readInt();
+ mContainerId = in.readInt();
+ mTag = in.readString();
+ mRetainInstance = in.readInt() != 0;
+ mArguments = in.readBundle();
+ mSavedFragmentState = in.readBundle();
+ }
+
+ public Fragment instantiate(Activity activity) {
+ if (mInstance != null) {
+ return mInstance;
+ }
+
+ mInstance = Fragment.instantiate(activity, mClassName, mArguments);
+
+ if (mSavedFragmentState != null) {
+ mSavedFragmentState.setClassLoader(activity.getClassLoader());
+ mInstance.mSavedFragmentState = mSavedFragmentState;
+ }
+ mInstance.setIndex(mIndex);
+ mInstance.mFromLayout = mFromLayout;
+ mInstance.mFragmentId = mFragmentId;
+ mInstance.mContainerId = mContainerId;
+ mInstance.mTag = mTag;
+ mInstance.mRetainInstance = mRetainInstance;
+ mInstance.mFragmentManager = activity.mFragments;
+
+ return mInstance;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mClassName);
+ dest.writeInt(mIndex);
+ dest.writeInt(mFromLayout ? 1 : 0);
+ dest.writeInt(mFragmentId);
+ dest.writeInt(mContainerId);
+ dest.writeString(mTag);
+ dest.writeInt(mRetainInstance ? 1 : 0);
+ dest.writeBundle(mArguments);
+ dest.writeBundle(mSavedFragmentState);
+ }
+
+ public static final Parcelable.Creator The Fragment class can be used many ways to achieve a wide variety of
+ * results. It is core, it represents a particular operation or interface
+ * that is running within a larger {@link Activity}. A Fragment is closely
+ * tied to the Activity it is in, and can not be used apart from one. Though
+ * Fragment defines its own lifecycle, that lifecycle is dependent on its
+ * activity: if the activity is stopped, no fragments inside of it can be
+ * started; when the activity is destroyed, all fragments will be destroyed.
+ *
+ * All subclasses of Fragment must include a public empty constructor.
+ * The framework will often re-instantiate a fragment class when needed,
+ * in particular during state restore, and needs to be able to find this
+ * constructor to instantiate it. If the empty constructor is not available,
+ * a runtime exception will occur in some cases during state restore.
+ *
+ * Topics covered here:
+ * Though a Fragment's lifecycle is tied to its owning activity, it has
+ * its own wrinkle on the standard activity lifecycle. It includes basic
+ * activity lifecycle methods such as {@link #onResume}, but also important
+ * are methods related to interactions with the activity and UI generation.
+ *
+ * The core series of lifecycle methods that are called to bring a fragment
+ * up to resumed state (interacting with the user) are:
+ *
+ * As a fragment is no longer being used, it goes through a reverse
+ * series of callbacks:
+ *
+ * Fragments can be used as part of your application's layout, allowing
+ * you to better modularize your code and more easily adjust your user
+ * interface to the screen it is running on. As an example, we can look
+ * at a simple program consisting of a list of items, and display of the
+ * details of each item. An activity's layout XML can include The layout is installed in the activity in the normal way: The titles fragment, showing a list of titles, is very simple, relying
+ * on {@link ListFragment} for most of its work. Note the implementation of
+ * clicking an item, which can either update
+ * the content of the details fragment or start a new activity show the
+ * details depending on whether the current activity's layout can show the
+ * details. The details fragment showing the contents of selected item here just
+ * displays a string of text based on an index of a string array built in to
+ * the app: In this case when the user clicks on a title, there is no details
+ * fragment in the current activity, so the title title fragment's click code will
+ * launch a new activity to display the details fragment: However the screen may be large enough to show both the list of titles
+ * and details about the currently selected title. To use such a layout on
+ * a landscape screen, this alternative layout can be placed under layout-land: Note how the prior code will adjust to this alternative UI flow: the
+ * titles fragment will now show its text inside of its activity, and the
+ * details activity will finish of it finds itself running in a configuration
+ * where the details can be shown inline.
+ *
+ * When a configuration change causes the activity hosting these fragments
+ * to restart, its new instance may use a different layout that doesn't
+ * include the same fragments as the previous layout. In this case all of
+ * the previous fragments will still be instantiated and running in the new
+ * instance; however, any that are no longer associated with a <fragment>
+ * tag in the view hierarchy will not have their content view created and will
+ * return false from {@link #isInLayout}.
+ *
+ * The attributes of the <fragment> tag are used to control the
+ * LayoutParams provider when attaching the fragment's view to the parent
+ * container. They can alse be parsed by the fragment in {@link #onInflate}
+ * as parameters.
+ *
+ * The fragment being instantiated must have some kind of unique identifier
+ * so that it can be re-associated with a previous instance if the parent
+ * activity needs to be destroyed and recreated. This can be provided these
+ * ways:
+ *
+ * The transaction in which fragments are modified can be placed on an
+ * internal back-stack of the owning activity. When the user presses back
+ * in the activity, any transactions on the back stack are popped off before
+ * the activity itself is finished.
+ *
+ * For example, consider this simple fragment that is instantiated with
+ * an integer argument and displays that in a TextView in its UI: A function that creates a new instance of the fragment, replacing
+ * whatever current fragment instance is being shown and pushing that change
+ * on to the back stack could be written as:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
+ * add_stack}
+ *
+ * After each call to this function, a new entry is on the stack, and
+ * pressing back will pop it to return the user to whatever previous state
+ * the activity UI was in.
+ */
+public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {
+ private static final HashMap Applications should generally not implement a constructor. The
+ * first place application code an run where the fragment is ready to
+ * be used is in {@link #onAttach(Activity)}, the point where the fragment
+ * is actually associated with its activity. Some applications may also
+ * want to implement {@link #onInflate} to retrieve attributes from a
+ * layout resource, though should take care here because this happens for
+ * the fragment is attached to its activity.
+ */
+ public Fragment() {
+ }
+
+ /**
+ * Like {@link #instantiate(Context, String, Bundle)} but with a null
+ * argument Bundle.
+ */
+ public static Fragment instantiate(Context context, String fname) {
+ return instantiate(context, fname, null);
+ }
+
+ /**
+ * Create a new instance of a Fragment with the given class name. This is
+ * the same as calling its empty constructor.
+ *
+ * @param context The calling context being used to instantiate the fragment.
+ * This is currently just used to get its ClassLoader.
+ * @param fname The class name of the fragment to instantiate.
+ * @param args Bundle of arguments to supply to the fragment, which it
+ * can retrieve with {@link #getArguments()}. May be null.
+ * @return Returns a new fragment instance.
+ * @throws InstantiationException If there is a failure in instantiating
+ * the given fragment class. This is a runtime exception; it is not
+ * normally expected to happen.
+ */
+ public static Fragment instantiate(Context context, String fname, Bundle args) {
+ try {
+ Class> clazz = sClassMap.get(fname);
+ if (clazz == null) {
+ // Class not found in the cache, see if it's real, and try to add it
+ clazz = context.getClassLoader().loadClass(fname);
+ sClassMap.put(fname, clazz);
+ }
+ Fragment f = (Fragment)clazz.newInstance();
+ if (args != null) {
+ args.setClassLoader(f.getClass().getClassLoader());
+ f.mArguments = args;
+ }
+ return f;
+ } catch (ClassNotFoundException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
+ } catch (java.lang.InstantiationException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
+ } catch (IllegalAccessException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
+ }
+ }
+
+ void restoreViewState() {
+ if (mSavedViewState != null) {
+ mView.restoreHierarchyState(mSavedViewState);
+ mSavedViewState = null;
+ }
+ }
+
+ void setIndex(int index) {
+ mIndex = index;
+ mWho = "android:fragment:" + mIndex;
+ }
+
+ void clearIndex() {
+ mIndex = -1;
+ mWho = null;
+ }
+
+ /**
+ * Subclasses can not override equals().
+ */
+ @Override final public boolean equals(Object o) {
+ return super.equals(o);
+ }
+
+ /**
+ * Subclasses can not override hashCode().
+ */
+ @Override final public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Fragment{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ if (mIndex >= 0) {
+ sb.append(" #");
+ sb.append(mIndex);
+ }
+ if (mFragmentId != 0) {
+ sb.append(" id=0x");
+ sb.append(Integer.toHexString(mFragmentId));
+ }
+ if (mTag != null) {
+ sb.append(" ");
+ sb.append(mTag);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Return the identifier this fragment is known by. This is either
+ * the android:id value supplied in a layout or the container view ID
+ * supplied when adding the fragment.
+ */
+ final public int getId() {
+ return mFragmentId;
+ }
+
+ /**
+ * Get the tag name of the fragment, if specified.
+ */
+ final public String getTag() {
+ return mTag;
+ }
+
+ /**
+ * Supply the construction arguments for this fragment. This can only
+ * be called before the fragment has been attached to its activity; that
+ * is, you should call it immediately after constructing the fragment. The
+ * arguments supplied here will be retained across fragment destroy and
+ * creation.
+ */
+ public void setArguments(Bundle args) {
+ if (mIndex >= 0) {
+ throw new IllegalStateException("Fragment already active");
+ }
+ mArguments = args;
+ }
+
+ /**
+ * Return the arguments supplied when the fragment was instantiated,
+ * if any.
+ */
+ final public Bundle getArguments() {
+ return mArguments;
+ }
+
+ /**
+ * Optional target for this fragment. This may be used, for example,
+ * if this fragment is being started by another, and when done wants to
+ * give a result back to the first. The target set here is retained
+ * across instances via {@link FragmentManager#putFragment
+ * FragmentManager.putFragment()}.
+ *
+ * @param fragment The fragment that is the target of this one.
+ * @param requestCode Optional request code, for convenience if you
+ * are going to call back with {@link #onActivityResult(int, int, Intent)}.
+ */
+ public void setTargetFragment(Fragment fragment, int requestCode) {
+ mTarget = fragment;
+ mTargetRequestCode = requestCode;
+ }
+
+ /**
+ * Return the target fragment set by {@link #setTargetFragment}.
+ */
+ final public Fragment getTargetFragment() {
+ return mTarget;
+ }
+
+ /**
+ * Return the target request code set by {@link #setTargetFragment}.
+ */
+ final public int getTargetRequestCode() {
+ return mTargetRequestCode;
+ }
+
+ /**
+ * Return the Activity this fragment is currently associated with.
+ */
+ final public Activity getActivity() {
+ return mActivity;
+ }
+
+ /**
+ * Return the FragmentManager for interacting with fragments associated
+ * with this fragment's activity. Note that this will be non-null slightly
+ * before {@link #getActivity()}, during the time from when the fragment is
+ * placed in a {@link FragmentTransaction} until it is committed and
+ * attached to its activity.
+ */
+ final public FragmentManager getFragmentManager() {
+ return mFragmentManager;
+ }
+
+ /**
+ * Return true if the fragment is currently added to its activity.
+ */
+ final public boolean isAdded() {
+ return mActivity != null && mActivity.mFragments.mAdded.contains(this);
+ }
+
+ /**
+ * Return true if the layout is included as part of an activity view
+ * hierarchy via the <fragment> tag. This will always be true when
+ * fragments are created through the <fragment> tag, except
+ * in the case where an old fragment is restored from a previous state and
+ * it does not appear in the layout of the current state.
+ */
+ final public boolean isInLayout() {
+ return mInLayout;
+ }
+
+ /**
+ * Return true if the fragment is in the resumed state. This is true
+ * for the duration of {@link #onResume()} and {@link #onPause()} as well.
+ */
+ final public boolean isResumed() {
+ return mResumed;
+ }
+
+ /**
+ * Return true if the fragment is currently visible to the user. This means
+ * it: (1) has been added, (2) has its view attached to the window, and
+ * (3) is not hidden.
+ */
+ final public boolean isVisible() {
+ return isAdded() && !isHidden() && mView != null
+ && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Return true if the fragment has been hidden. By default fragments
+ * are shown. You can find out about changes to this state with
+ * {@link #onHiddenChanged}. Note that the hidden state is orthogonal
+ * to other states -- that is, to be visible to the user, a fragment
+ * must be both started and not hidden.
+ */
+ final public boolean isHidden() {
+ return mHidden;
+ }
+
+ /**
+ * Called when the hidden state (as returned by {@link #isHidden()} of
+ * the fragment has changed. Fragments start out not hidden; this will
+ * be called whenever the fragment changes state from that.
+ * @param hidden True if the fragment is now hidden, false if it is not
+ * visible.
+ */
+ public void onHiddenChanged(boolean hidden) {
+ }
+
+ /**
+ * Control whether a fragment instance is retained across Activity
+ * re-creation (such as from a configuration change). This can only
+ * be used with fragments not in the back stack. If set, the fragment
+ * lifecycle will be slightly different when an activity is recreated:
+ * This is called every time the fragment is inflated, even if it is
+ * being inflated into a new instance with saved state. Because a fragment's
+ * arguments are retained across instances, it may make no sense to re-parse
+ * the attributes into new arguments. You may want to first check
+ * {@link #getArguments()} and only parse the attributes if it returns null,
+ * the assumption being that if it is non-null those are the same arguments
+ * from the first time the fragment was inflated. (That said, you may want
+ * to have layouts change for different configurations such as landscape
+ * and portrait, which can have different attributes. If so, you will need
+ * to re-parse the attributes each time this is called to generate new
+ * arguments.) Note that this can be called while the fragment's activity is
+ * still in the process of being created. As such, you can not rely
+ * on things like the activity's content view hierarchy being initialized
+ * at this point. If you want to do work once the activity itself is
+ * created, see {@link #onActivityCreated(Bundle)}.
+ *
+ * @param savedInstanceState If the fragment is being re-created from
+ * a previous saved state, this is the state.
+ */
+ public void onCreate(Bundle savedInstanceState) {
+ mCalled = true;
+ }
+
+ /**
+ * Called to have the fragment instantiate its user interface view.
+ * This is optional, and non-graphical fragments can return null (which
+ * is the default implementation). This will be called between
+ * {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}.
+ *
+ * If you return a View from here, you will later be called in
+ * {@link #onDestroyView} when the view is being released.
+ *
+ * @param inflater The LayoutInflater object that can be used to inflate
+ * any views in the fragment,
+ * @param container If non-null, this is the parent view that the fragment's
+ * UI should be attached to. The fragment should not add the view itself,
+ * but this can be used to generate the LayoutParams of the view.
+ * @param savedInstanceState If non-null, this fragment is being re-constructed
+ * from a previous saved state as given here.
+ *
+ * @return Return the View for the fragment's UI, or null.
+ */
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return null;
+ }
+
+ public View getView() {
+ return mView;
+ }
+
+ /**
+ * Called when the fragment's activity has been created and this
+ * fragment's view hierarchy instantiated. It can be used to do final
+ * initialization once these pieces are in place, such as retrieving
+ * views or restoring state. It is also useful for fragments that use
+ * {@link #setRetainInstance(boolean)} to retain their instance,
+ * as this callback tells the fragment when it is fully associated with
+ * the new activity instance. This is called after {@link #onCreateView}
+ * and before {@link #onStart()}.
+ *
+ * @param savedInstanceState If the fragment is being re-created from
+ * a previous saved state, this is the state.
+ */
+ public void onActivityCreated(Bundle savedInstanceState) {
+ mCalled = true;
+ }
+
+ /**
+ * Called when the Fragment is visible to the user. This is generally
+ * tied to {@link Activity#onStart() Activity.onStart} of the containing
+ * Activity's lifecycle.
+ */
+ public void onStart() {
+ mCalled = true;
+ mStarted = true;
+ if (!mCheckedForLoaderManager) {
+ mCheckedForLoaderManager = true;
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false);
+ }
+ if (mLoaderManager != null) {
+ mLoaderManager.doStart();
+ }
+ }
+
+ /**
+ * Called when the fragment is visible to the user and actively running.
+ * This is generally
+ * tied to {@link Activity#onResume() Activity.onResume} of the containing
+ * Activity's lifecycle.
+ */
+ public void onResume() {
+ mCalled = true;
+ }
+
+ /**
+ * Called to ask the fragment to save its current dynamic state, so it
+ * can later be reconstructed in a new instance of its process is
+ * restarted. If a new instance of the fragment later needs to be
+ * created, the data you place in the Bundle here will be available
+ * in the Bundle given to {@link #onCreate(Bundle)},
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, and
+ * {@link #onActivityCreated(Bundle)}.
+ *
+ * This corresponds to {@link Activity#onSaveInstanceState(Bundle)
+ * Activity.onSaveInstanceState(Bundle)} and most of the discussion there
+ * applies here as well. Note however: this method may be called
+ * at any time before {@link #onDestroy()}. There are many situations
+ * where a fragment may be mostly torn down (such as when placed on the
+ * back stack with no UI showing), but its state will not be saved until
+ * its owning activity actually needs to save its state.
+ *
+ * @param outState Bundle in which to place your saved state.
+ */
+ public void onSaveInstanceState(Bundle outState) {
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ mCalled = true;
+ }
+
+ /**
+ * Called when the Fragment is no longer resumed. This is generally
+ * tied to {@link Activity#onPause() Activity.onPause} of the containing
+ * Activity's lifecycle.
+ */
+ public void onPause() {
+ mCalled = true;
+ }
+
+ /**
+ * Called when the Fragment is no longer started. This is generally
+ * tied to {@link Activity#onStop() Activity.onStop} of the containing
+ * Activity's lifecycle.
+ */
+ public void onStop() {
+ mCalled = true;
+ }
+
+ public void onLowMemory() {
+ mCalled = true;
+ }
+
+ /**
+ * Called when the view previously created by {@link #onCreateView} has
+ * been detached from the fragment. The next time the fragment needs
+ * to be displayed, a new view will be created. This is called
+ * after {@link #onStop()} and before {@link #onDestroy()}. It is called
+ * regardless of whether {@link #onCreateView} returned a
+ * non-null view. Internally it is called after the view's state has
+ * been saved but before it has been removed from its parent.
+ */
+ public void onDestroyView() {
+ mCalled = true;
+ }
+
+ /**
+ * Called when the fragment is no longer in use. This is called
+ * after {@link #onStop()} and before {@link #onDetach()}.
+ */
+ public void onDestroy() {
+ mCalled = true;
+ //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager
+ // + " mLoaderManager=" + mLoaderManager);
+ if (!mCheckedForLoaderManager) {
+ mCheckedForLoaderManager = true;
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false);
+ }
+ if (mLoaderManager != null) {
+ mLoaderManager.doDestroy();
+ }
+ }
+
+ /**
+ * Called when the fragment is no longer attached to its activity. This
+ * is called after {@link #onDestroy()}.
+ */
+ public void onDetach() {
+ mCalled = true;
+ }
+
+ /**
+ * Initialize the contents of the Activity's standard options menu. You
+ * should place your menu items in to menu. For this method
+ * to be called, you must have first called {@link #setHasOptionsMenu}. See
+ * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu}
+ * for more information.
+ *
+ * @param menu The options menu in which you place your items.
+ *
+ * @see #setHasOptionsMenu
+ * @see #onPrepareOptionsMenu
+ * @see #onOptionsItemSelected
+ */
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ }
+
+ /**
+ * Prepare the Screen's standard options menu to be displayed. This is
+ * called right before the menu is shown, every time it is shown. You can
+ * use this method to efficiently enable/disable items or otherwise
+ * dynamically modify the contents. See
+ * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu}
+ * for more information.
+ *
+ * @param menu The options menu as last shown or first initialized by
+ * onCreateOptionsMenu().
+ *
+ * @see #setHasOptionsMenu
+ * @see #onCreateOptionsMenu
+ */
+ public void onPrepareOptionsMenu(Menu menu) {
+ }
+
+ /**
+ * This hook is called whenever an item in your options menu is selected.
+ * The default implementation simply returns false to have the normal
+ * processing happen (calling the item's Runnable or sending a message to
+ * its Handler as appropriate). You can use this method for any items
+ * for which you would like to do processing without those other
+ * facilities.
+ *
+ * Derived classes should call through to the base class for it to
+ * perform the default menu handling.
+ *
+ * @param item The menu item that was selected.
+ *
+ * @return boolean Return false to allow normal menu processing to
+ * proceed, true to consume it here.
+ *
+ * @see #onCreateOptionsMenu
+ */
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
+ }
+
+ /**
+ * This hook is called whenever the options menu is being closed (either by the user canceling
+ * the menu with the back/menu button, or when an item is selected).
+ *
+ * @param menu The options menu as last shown or first initialized by
+ * onCreateOptionsMenu().
+ */
+ public void onOptionsMenuClosed(Menu menu) {
+ }
+
+ /**
+ * Called when a context menu for the {@code view} is about to be shown.
+ * Unlike {@link #onCreateOptionsMenu}, this will be called every
+ * time the context menu is about to be shown and should be populated for
+ * the view (or item inside the view for {@link AdapterView} subclasses,
+ * this can be found in the {@code menuInfo})).
+ *
+ * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an
+ * item has been selected.
+ *
+ * The default implementation calls up to
+ * {@link Activity#onCreateContextMenu Activity.onCreateContextMenu}, though
+ * you can not call this implementation if you don't want that behavior.
+ *
+ * It is not safe to hold onto the context menu after this method returns.
+ * {@inheritDoc}
+ */
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ getActivity().onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ /**
+ * Registers a context menu to be shown for the given view (multiple views
+ * can show the context menu). This method will set the
+ * {@link OnCreateContextMenuListener} on the view to this fragment, so
+ * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be
+ * called when it is time to show the context menu.
+ *
+ * @see #unregisterForContextMenu(View)
+ * @param view The view that should show a context menu.
+ */
+ public void registerForContextMenu(View view) {
+ view.setOnCreateContextMenuListener(this);
+ }
+
+ /**
+ * Prevents a context menu to be shown for the given view. This method will
+ * remove the {@link OnCreateContextMenuListener} on the view.
+ *
+ * @see #registerForContextMenu(View)
+ * @param view The view that should stop showing a context menu.
+ */
+ public void unregisterForContextMenu(View view) {
+ view.setOnCreateContextMenuListener(null);
+ }
+
+ /**
+ * This hook is called whenever an item in a context menu is selected. The
+ * default implementation simply returns false to have the normal processing
+ * happen (calling the item's Runnable or sending a message to its Handler
+ * as appropriate). You can use this method for any items for which you
+ * would like to do processing without those other facilities.
+ *
+ * Use {@link MenuItem#getMenuInfo()} to get extra information set by the
+ * View that added this menu item.
+ *
+ * Derived classes should call through to the base class for it to perform
+ * the default menu handling.
+ *
+ * @param item The context menu item that was selected.
+ * @return boolean Return false to allow normal context menu processing to
+ * proceed, true to consume it here.
+ */
+ public boolean onContextItemSelected(MenuItem item) {
+ return false;
+ }
+
+ /**
+ * Print the Fragments's state into the given stream.
+ *
+ * @param prefix Text to print at the front of each line.
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param writer The PrintWriter to which you should dump your state. This will be
+ * closed for you after you return.
+ * @param args additional arguments to the dump request.
+ */
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.print(prefix); writer.print("mFragmentId="); writer.print(mFragmentId);
+ writer.print(" mContainerId="); writer.print(mContainerId);
+ writer.print(" mTag="); writer.println(mTag);
+ writer.print(prefix); writer.print("mState="); writer.print(mState);
+ writer.print(" mIndex="); writer.print(mIndex);
+ writer.print(" mWho="); writer.print(mWho);
+ writer.print(" mBackStackNesting="); writer.println(mBackStackNesting);
+ writer.print(prefix); writer.print("mAdded="); writer.print(mAdded);
+ writer.print(" mResumed="); writer.print(mResumed);
+ writer.print(" mFromLayout="); writer.print(mFromLayout);
+ writer.print(" mInLayout="); writer.println(mInLayout);
+ writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
+ writer.print(" mRetainInstance="); writer.print(mRetainInstance);
+ writer.print(" mRetaining="); writer.print(mRetaining);
+ writer.print(" mHasMenu="); writer.println(mHasMenu);
+ if (mFragmentManager != null) {
+ writer.print(prefix); writer.print("mFragmentManager=");
+ writer.println(mFragmentManager);
+ }
+ if (mImmediateActivity != null) {
+ writer.print(prefix); writer.print("mImmediateActivity=");
+ writer.println(mImmediateActivity);
+ }
+ if (mActivity != null) {
+ writer.print(prefix); writer.print("mActivity=");
+ writer.println(mActivity);
+ }
+ if (mArguments != null) {
+ writer.print(prefix); writer.print("mArguments="); writer.println(mArguments);
+ }
+ if (mSavedFragmentState != null) {
+ writer.print(prefix); writer.print("mSavedFragmentState=");
+ writer.println(mSavedFragmentState);
+ }
+ if (mSavedViewState != null) {
+ writer.print(prefix); writer.print("mSavedViewState=");
+ writer.println(mSavedViewState);
+ }
+ if (mTarget != null) {
+ writer.print(prefix); writer.print("mTarget="); writer.print(mTarget);
+ writer.print(" mTargetRequestCode=");
+ writer.println(mTargetRequestCode);
+ }
+ if (mNextAnim != 0) {
+ writer.print(prefix); writer.print("mNextAnim="); writer.println(mNextAnim);
+ }
+ if (mContainer != null) {
+ writer.print(prefix); writer.print("mContainer="); writer.println(mContainer);
+ }
+ if (mView != null) {
+ writer.print(prefix); writer.print("mView="); writer.println(mView);
+ }
+ if (mLoaderManager != null) {
+ writer.print(prefix); writer.print("mLoaderManager="); writer.print(mLoaderManager);
+ writer.print(" mStarted="); writer.print(mStarted);
+ writer.print(" mCheckedForLoaderManager=");
+ writer.println(mCheckedForLoaderManager);
+ }
+ }
+
+ void performStop() {
+ onStop();
+ if (mStarted) {
+ mStarted = false;
+ if (!mCheckedForLoaderManager) {
+ mCheckedForLoaderManager = true;
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false);
+ }
+ if (mLoaderManager != null) {
+ if (mActivity == null || !mActivity.mChangingConfigurations) {
+ mLoaderManager.doStop();
+ } else {
+ mLoaderManager.doRetain();
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
new file mode 100644
index 0000000000000000000000000000000000000000..22e074756291a222632fa072c225a9b461658a79
--- /dev/null
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app;
+
+import android.app.FragmentManager.BackStackEntry;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Helper class for showing "bread crumbs" representing the fragment
+ * stack in an activity. This is intended to be used with
+ * {@link ActionBar#setCustomNavigationMode(View)
+ * ActionBar.setCustomNavigationMode(View)} to place the bread crumbs in
+ * the navigation area of the action bar.
+ *
+ * The default style for this view is
+ * {@link android.R.style#Widget_FragmentBreadCrumbs}.
+ */
+public class FragmentBreadCrumbs extends ViewGroup
+ implements FragmentManager.OnBackStackChangedListener {
+ Activity mActivity;
+ LayoutInflater mInflater;
+ LinearLayout mContainer;
+
+ // Hahah
+ BackStackRecord mTopEntry;
+
+ public FragmentBreadCrumbs(Context context) {
+ this(context, null);
+ }
+
+ public FragmentBreadCrumbs(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.style.Widget_FragmentBreadCrumbs);
+ }
+
+ public FragmentBreadCrumbs(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Attach the bread crumbs to their activity. This must be called once
+ * when creating the bread crumbs.
+ */
+ public void setActivity(Activity a) {
+ mActivity = a;
+ mInflater = (LayoutInflater)a.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mContainer = (LinearLayout)mInflater.inflate(
+ com.android.internal.R.layout.fragment_bread_crumbs,
+ this, false);
+ addView(mContainer);
+ a.getFragmentManager().addOnBackStackChangedListener(this);
+ updateCrumbs();
+ }
+
+ /**
+ * Set a custom title for the bread crumbs. This will be the first entry
+ * shown at the left, representing the root of the bread crumbs. If the
+ * title is null, it will not be shown.
+ */
+ public void setTitle(CharSequence title, CharSequence shortTitle) {
+ if (title == null) {
+ mTopEntry = null;
+ } else {
+ mTopEntry = new BackStackRecord((FragmentManagerImpl)
+ mActivity.getFragmentManager());
+ mTopEntry.setBreadCrumbTitle(title);
+ mTopEntry.setBreadCrumbShortTitle(shortTitle);
+ }
+ updateCrumbs();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Eventually we should implement our own layout of the views,
+ // rather than relying on a linear layout.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+
+ int childRight = mPaddingLeft + child.getMeasuredWidth() - mPaddingRight;
+ int childBottom = mPaddingTop + child.getMeasuredHeight() - mPaddingBottom;
+ child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int count = getChildCount();
+
+ int maxHeight = 0;
+ int maxWidth = 0;
+
+ // Find rightmost and bottom-most child
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+ maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+ }
+ }
+
+ // Account for padding too
+ maxWidth += mPaddingLeft + mPaddingRight;
+ maxHeight += mPaddingTop + mPaddingBottom;
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
+ resolveSize(maxHeight, heightMeasureSpec));
+ }
+
+ @Override
+ public void onBackStackChanged() {
+ updateCrumbs();
+ }
+
+ void updateCrumbs() {
+ FragmentManager fm = mActivity.getFragmentManager();
+ int numEntries = fm.countBackStackEntries();
+ int numViews = mContainer.getChildCount();
+ for (int i = mTopEntry != null ? -1 : 0; i < numEntries; i++) {
+ BackStackEntry bse = i == -1 ? mTopEntry : fm.getBackStackEntry(i);
+ int viewI = mTopEntry != null ? i + 1 : i;
+ if (viewI < numViews) {
+ View v = mContainer.getChildAt(viewI);
+ Object tag = v.getTag();
+ if (tag != bse) {
+ for (int j = viewI; j < numViews; j++) {
+ mContainer.removeViewAt(viewI);
+ }
+ numViews = viewI;
+ }
+ }
+ if (viewI >= numViews) {
+ View item = mInflater.inflate(
+ com.android.internal.R.layout.fragment_bread_crumb_item,
+ this, false);
+ TextView text = (TextView)item.findViewById(com.android.internal.R.id.title);
+ text.setText(bse.getBreadCrumbTitle());
+ item.setTag(bse);
+ if (viewI == 0) {
+ text.setCompoundDrawables(null, null, null, null);
+ }
+ mContainer.addView(item);
+ item.setOnClickListener(mOnClickListener);
+ }
+ }
+ int viewI = mTopEntry != null ? numEntries + 1 : numEntries;
+ numViews = mContainer.getChildCount();
+ while (numViews > viewI) {
+ mContainer.removeViewAt(numViews-1);
+ numViews--;
+ }
+ }
+
+ private OnClickListener mOnClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ if (v.getTag() instanceof BackStackEntry) {
+ BackStackEntry bse = (BackStackEntry) v.getTag();
+ mActivity.getFragmentManager().popBackStack(bse.getId(),
+ bse == mTopEntry? FragmentManager.POP_BACK_STACK_INCLUSIVE : 0);
+ }
+ }
+ };
+}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..da7ba6f547b473b47618c15fbb97a52499a8a3a8
--- /dev/null
+++ b/core/java/android/app/FragmentManager.java
@@ -0,0 +1,1404 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Interface for interacting with {@link Fragment} objects inside of an
+ * {@link Activity}
+ */
+public interface FragmentManager {
+ /**
+ * Representation of an entry on the fragment back stack, as created
+ * with {@link FragmentTransaction#addToBackStack(String)
+ * FragmentTransaction.addToBackStack()}. Entries can later be
+ * retrieved with {@link FragmentManager#getBackStackEntry(int)
+ * FragmentManager.getBackStackEntry()}.
+ *
+ * Note that you should never hold on to a BackStackEntry object;
+ * the identifier as returned by {@link #getId} is the only thing that
+ * will be persisted across activity instances.
+ */
+ public interface BackStackEntry {
+ /**
+ * Return the unique identifier for the entry. This is the only
+ * representation of the entry that will persist across activity
+ * instances.
+ */
+ public int getId();
+
+ /**
+ * Return the full bread crumb title for the entry, or null if it
+ * does not have one.
+ */
+ public CharSequence getBreadCrumbTitle();
+
+ /**
+ * Return the short bread crumb title for the entry, or null if it
+ * does not have one.
+ */
+ public CharSequence getBreadCrumbShortTitle();
+ }
+
+ /**
+ * Interface to watch for changes to the back stack.
+ */
+ public interface OnBackStackChangedListener {
+ /**
+ * Called whenever the contents of the back stack change.
+ */
+ public void onBackStackChanged();
+ }
+
+ /**
+ * Start a series of edit operations on the Fragments associated with
+ * this FragmentManager.
+ */
+ public FragmentTransaction openTransaction();
+
+ /**
+ * Finds a fragment that was identified by the given id either when inflated
+ * from XML or as the container ID when added in a transaction. This first
+ * searches through fragments that are currently added to the manager's
+ * activity; if no such fragment is found, then all fragments currently
+ * on the back stack associated with this ID are searched.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentById(int id);
+
+ /**
+ * Finds a fragment that was identified by the given tag either when inflated
+ * from XML or as supplied when added in a transaction. This first
+ * searches through fragments that are currently added to the manager's
+ * activity; if no such fragment is found, then all fragments currently
+ * on the back stack are searched.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentByTag(String tag);
+
+ /**
+ * Flag for {@link #popBackStack(String, int)}
+ * and {@link #popBackStack(int, int)}: If set, and the name or ID of
+ * a back stack entry has been supplied, then all matching entries will
+ * be consumed until one that doesn't match is found or the bottom of
+ * the stack is reached. Otherwise, all entries up to but not including that entry
+ * will be removed.
+ */
+ public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
+
+ /**
+ * Pop the top state off the back stack. Returns true if there was one
+ * to pop, else false.
+ */
+ public boolean popBackStack();
+
+ /**
+ * Pop the last fragment transition from the manager's fragment
+ * back stack. If there is nothing to pop, false is returned.
+ * @param name If non-null, this is the name of a previous back state
+ * to look for; if found, all states up to that state will be popped. The
+ * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+ * the named state itself is popped. If null, only the top state is popped.
+ * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ */
+ public boolean popBackStack(String name, int flags);
+
+ /**
+ * Pop all back stack states up to the one with the given identifier.
+ * @param id Identifier of the stated to be popped. If no identifier exists,
+ * false is returned.
+ * The identifier is the number returned by
+ * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The
+ * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+ * the named state itself is popped.
+ * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ */
+ public boolean popBackStack(int id, int flags);
+
+ /**
+ * Return the number of entries currently in the back stack.
+ */
+ public int countBackStackEntries();
+
+ /**
+ * Return the BackStackEntry at index index in the back stack;
+ * entries start index 0 being the bottom of the stack.
+ */
+ public BackStackEntry getBackStackEntry(int index);
+
+ /**
+ * Add a new listener for changes to the fragment back stack.
+ */
+ public void addOnBackStackChangedListener(OnBackStackChangedListener listener);
+
+ /**
+ * Remove a listener that was previously added with
+ * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
+ */
+ public void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
+
+ /**
+ * Put a reference to a fragment in a Bundle. This Bundle can be
+ * persisted as saved state, and when later restoring
+ * {@link #getFragment(Bundle, String)} will return the current
+ * instance of the same fragment.
+ *
+ * @param bundle The bundle in which to put the fragment reference.
+ * @param key The name of the entry in the bundle.
+ * @param fragment The Fragment whose reference is to be stored.
+ */
+ public void putFragment(Bundle bundle, String key, Fragment fragment);
+
+ /**
+ * Retrieve the current Fragment instance for a reference previously
+ * placed with {@link #putFragment(Bundle, String, Fragment)}.
+ *
+ * @param bundle The bundle from which to retrieve the fragment reference.
+ * @param key The name of the entry in the bundle.
+ * @return Returns the current Fragment instance that is associated with
+ * the given reference.
+ */
+ public Fragment getFragment(Bundle bundle, String key);
+
+ /**
+ * Print the FragmentManager's state into the given stream.
+ *
+ * @param prefix Text to print at the front of each line.
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param writer A PrintWriter to which the dump is to be set.
+ * @param args additional arguments to the dump request.
+ */
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
+}
+
+final class FragmentManagerState implements Parcelable {
+ FragmentState[] mActive;
+ int[] mAdded;
+ BackStackState[] mBackStack;
+
+ public FragmentManagerState() {
+ }
+
+ public FragmentManagerState(Parcel in) {
+ mActive = in.createTypedArray(FragmentState.CREATOR);
+ mAdded = in.createIntArray();
+ mBackStack = in.createTypedArray(BackStackState.CREATOR);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedArray(mActive, flags);
+ dest.writeIntArray(mAdded);
+ dest.writeTypedArray(mBackStack, flags);
+ }
+
+ public static final Parcelable.Creator
+ * ListFragment hosts a {@link android.widget.ListView ListView} object that can
+ * be bound to different data sources, typically either an array or a Cursor
+ * holding query results. Binding, screen layout, and row layout are discussed
+ * in the following sections.
+ *
+ * Screen Layout
+ *
+ * ListFragment has a default layout that consists of a single list view.
+ * However, if you desire, you can customize the fragment layout by returning
+ * your own view hierarchy from {@link #onCreateView}.
+ * To do this, your view hierarchy must contain a ListView object with the
+ * id "@android:id/list" (or {@link android.R.id#list} if it's in code)
+ *
+ * Optionally, your view hierarchy can contain another view object of any type to
+ * display when the list view is empty. This "empty list" notifier must have an
+ * id "android:empty". Note that when an empty view is present, the list view
+ * will be hidden when there is no data to display.
+ *
+ * The following code demonstrates an (ugly) custom list layout. It has a list
+ * with a green background, and an alternate red "no data" message.
+ *
+ * Row Layout
+ *
+ * You can specify the layout of individual rows in the list. You do this by
+ * specifying a layout resource in the ListAdapter object hosted by the fragment
+ * (the ListAdapter binds the ListView to the data; more on this later).
+ *
+ * A ListAdapter constructor takes a parameter that specifies a layout resource
+ * for each row. It also has two additional parameters that let you specify
+ * which data field to associate with which object in the row layout resource.
+ * These two parameters are typically parallel arrays.
+ *
+ * Android provides some standard row layout resources. These are in the
+ * {@link android.R.layout} class, and have names such as simple_list_item_1,
+ * simple_list_item_2, and two_line_list_item. The following layout XML is the
+ * source for the resource two_line_list_item, which displays two data
+ * fields,one above the other, for each list row.
+ *
+ * You must identify the data bound to each TextView object in this layout. The
+ * syntax for this is discussed in the next section.
+ *
+ * Binding to Data
+ *
+ * You bind the ListFragment's ListView object to data using a class that
+ * implements the {@link android.widget.ListAdapter ListAdapter} interface.
+ * Android provides two standard list adapters:
+ * {@link android.widget.SimpleAdapter SimpleAdapter} for static data (Maps),
+ * and {@link android.widget.SimpleCursorAdapter SimpleCursorAdapter} for Cursor
+ * query results.
+ *
+ * You must use
+ * {@link #setListAdapter(ListAdapter) ListFragment.setListAdapter()} to
+ * associate the list with an adapter. Do not directly call
+ * {@link ListView#setAdapter(ListAdapter) ListView.setAdapter()} or else
+ * important initialization will be skipped.
+ * If you are overriding this method with your own custom content,
+ * consider including the standard layout {@link android.R.layout#list_content}
+ * in your layout file, so that you continue to retain all of the standard
+ * behavior of ListFragment. In particular, this is currently the only
+ * way to have the built-in indeterminant progress state be shown.
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(com.android.internal.R.layout.list_content,
+ container, false);
+ }
+
+ /**
+ * Attach to list view once Fragment is ready to run.
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ ensureList();
+ }
+
+ /**
+ * Detach from list view.
+ */
+ @Override
+ public void onDestroyView() {
+ mHandler.removeCallbacks(mRequestFocus);
+ mList = null;
+ super.onDestroyView();
+ }
+
+ /**
+ * This method will be called when an item in the list is selected.
+ * Subclasses should override. Subclasses can call
+ * getListView().getItemAtPosition(position) if they need to access the
+ * data associated with the selected item.
+ *
+ * @param l The ListView where the click happened
+ * @param v The view that was clicked within the ListView
+ * @param position The position of the view in the list
+ * @param id The row id of the item that was clicked
+ */
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ }
+
+ /**
+ * Provide the cursor for the list view.
+ */
+ public void setListAdapter(ListAdapter adapter) {
+ boolean hadAdapter = mAdapter != null;
+ mAdapter = adapter;
+ if (mList != null) {
+ mList.setAdapter(adapter);
+ if (!mListShown && !hadAdapter) {
+ // The list was hidden, and previously didn't have an
+ // adapter. It is now time to show it.
+ setListShown(true, getView().getWindowToken() != null);
+ }
+ }
+ }
+
+ /**
+ * Set the currently selected list item to the specified
+ * position with the adapter's data
+ *
+ * @param position
+ */
+ public void setSelection(int position) {
+ ensureList();
+ mList.setSelection(position);
+ }
+
+ /**
+ * Get the position of the currently selected list item.
+ */
+ public int getSelectedItemPosition() {
+ ensureList();
+ return mList.getSelectedItemPosition();
+ }
+
+ /**
+ * Get the cursor row ID of the currently selected list item.
+ */
+ public long getSelectedItemId() {
+ ensureList();
+ return mList.getSelectedItemId();
+ }
+
+ /**
+ * Get the activity's list view widget.
+ */
+ public ListView getListView() {
+ ensureList();
+ 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");
+ }
+ mStandardEmptyView.setText(text);
+ if (!mSetEmptyText) {
+ mList.setEmptyView(mStandardEmptyView);
+ mSetEmptyText = true;
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * Applications do not normally need to use this themselves. The default
+ * behavior of ListFragment is to start with the list not being shown, only
+ * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}.
+ * If the list at that point had not been shown, when it does get shown
+ * it will be do without the user ever seeing the hidden state.
+ *
+ * @param shown If true, the list view is shown; if false, the progress
+ * indicator. The initial value is true.
+ */
+ public void setListShown(boolean shown) {
+ setListShown(shown, true);
+ }
+
+ /**
+ * Like {@link #setListShown(boolean)}, but no animation is used when
+ * transitioning from the previous state.
+ */
+ public void setListShownNoAnimation(boolean shown) {
+ setListShown(shown, false);
+ }
+
+ /**
+ * 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.
+ */
+ private 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() {
+ return mAdapter;
+ }
+
+ private void ensureList() {
+ if (mList != null) {
+ return;
+ }
+ View root = getView();
+ if (root == null) {
+ throw new IllegalStateException("Content view not yet created");
+ }
+ 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);
+ } else {
+ // We are starting without an adapter, so assume we won't
+ // have our data right away and start with the progress indicator.
+ if (mProgressContainer != null) {
+ setListShown(false, false);
+ }
+ }
+ mHandler.post(mRequestFocus);
+ }
+}
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..28abcaa7735f14242c1fde6afd06e1057c7be21a
--- /dev/null
+++ b/core/java/android/app/LoaderManager.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app;
+
+import android.content.Loader;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * Interface associated with an {@link Activity} or {@link Fragment} for managing
+ * one or more {@link android.content.Loader} instances associated with it.
+ */
+public interface LoaderManager {
+ /**
+ * Callback interface for a client to interact with the manager.
+ */
+ public interface LoaderCallbacks In either case, the given callback is associated with the loader, and
+ * will be called as the loader state changes. If at the point of call
+ * the caller is in its started state, and the requested loader
+ * already exists and has generated its data, then
+ * callback. {@link LoaderCallbacks#onLoadFinished} will
+ * be called immediately (inside of this function), so you must be prepared
+ * for this to happen.
+ */
+ public AnimatorSet:
+ * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
+ * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
+ * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
+ * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
+ * class to add animations
+ * one by one.AnimatorSet with circular dependencies between
+ * its animations. For example, an animation a1 could be set up to start before animation a2, a2
+ * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
+ * result in none of the affected animations being played. Because of this (and because
+ * circular dependencies do not make logical sense anyway), circular dependencies
+ * should be avoided, and the dependency flow of animations should only be in one direction.
+ */
+public final class AnimatorSet extends Animator {
+
+ /**
+ * Internal variables
+ * NOTE: This object implements the clone() method, making a deep copy of any referenced
+ * objects. As other non-trivial fields are added to this class, make sure to add logic
+ * to clone() to make deep copies of them.
+ */
+
+ /**
+ * Tracks animations currently being played, so that we know what to
+ * cancel or end when cancel() or end() is called on this AnimatorSet
+ */
+ private ArrayListBuilder object, which is used to
+ * set up playing constraints. This initial play() method
+ * tells the Builder the animation that is the dependency for
+ * the succeeding commands to the Builder. For example,
+ * calling play(a1).with(a2) sets up the AnimatorSet to play
+ * a1 and a2 at the same time,
+ * play(a1).before(a2) sets up the AnimatorSet to play
+ * a1 first, followed by a2, and
+ * play(a1).after(a2) sets up the AnimatorSet to play
+ * a2 first, followed by a1.
+ *
+ * play() is the only way to tell the
+ * Builder the animation upon which the dependency is created,
+ * so successive calls to the various functions in Builder
+ * will all refer to the initial parameter supplied in play()
+ * as the dependency of the other animations. For example, calling
+ * play(a1).before(a2).before(a3) will play both a2
+ * and a3 when a1 ends; it does not set up a dependency between
+ * a2 and a3.Builder object. A null parameter will result
+ * in a null Builder return value.
+ * @return Builder The object that constructs the AnimatorSet based on the dependencies
+ * outlined in the calls to play and the other methods in the
+ * BuilderNote that canceling a AnimatorSet also cancels all of the animations that it is
+ * responsible for.AnimatorSet also ends all of the animations that it is
+ * responsible for.AnimatorSet will, in turn, start the animations for which
+ * it is responsible. The details of when exactly those animations are started depends on
+ * the dependency relationships that have been set up between the animations.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void start() {
+ mCanceled = false;
+
+ // First, sort the nodes (if necessary). This will ensure that sortedNodes
+ // contains the animation nodes in the correct order.
+ sortNodes();
+
+ // nodesToStart holds the list of nodes to be started immediately. We don't want to
+ // start the animations in the loop directly because we first need to set up
+ // dependencies on all of the nodes. For example, we don't want to start an animation
+ // when some other animation also wants to start when the first animation begins.
+ final ArrayListBuilder object is a utility class to facilitate adding animations to a
+ * AnimatorSet along with the relationships between the various animations. The
+ * intention of the Builder methods, along with the {@link
+ * AnimatorSet#play(Animator) play()} method of AnimatorSet is to make it possible to
+ * express the dependency relationships of animations in a natural way. Developers can also use
+ * the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
+ * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
+ * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
+ *
+ * Builder object cannot be constructed directly, but is rather constructed
+ * internally via a call to {@link AnimatorSet#play(Animator)}.
+ * AnimatorSet s = new AnimatorSet();
+ * s.play(anim1).with(anim2);
+ * s.play(anim2).before(anim3);
+ * s.play(anim4).after(anim3);
+ *
+ *
+ * Builder object to express
+ * multiple relationships. However, note that it is only the animation passed into the initial
+ * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
+ * calls to the Builder object. For example, the following code starts both anim2
+ * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
+ * anim3:
+ *
+ * AnimatorSet s = new AnimatorSet();
+ * s.play(anim1).before(anim2).before(anim3);
+ *
+ * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
+ * relationship correctly:
+ * AnimatorSet s = new AnimatorSet();
+ * s.play(anim1).before(anim2);
+ * s.play(anim2).before(anim3);
+ *
+ *
+ * play(anim1).after(anim1) makes no
+ * sense. In general, circular dependencies like this one (or more indirect ones where a depends
+ * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
+ * that can boil down to a simple, one-way relationship of animations starting with, before, and
+ * after other, different, animations.Builder object.
+ *
+ * @param anim The animation that will play when the animation supplied to the
+ * {@link AnimatorSet#play(Animator)} method starts.
+ */
+ public void with(Animator anim) {
+ Node node = mNodeMap.get(anim);
+ if (node == null) {
+ node = new Node(anim);
+ mNodeMap.put(anim, node);
+ mNodes.add(node);
+ }
+ Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
+ node.addDependency(dependency);
+ }
+
+ /**
+ * Sets up the given animation to play when the animation supplied in the
+ * {@link AnimatorSet#play(Animator)} call that created this Builder object
+ * ends.
+ *
+ * @param anim The animation that will play when the animation supplied to the
+ * {@link AnimatorSet#play(Animator)} method ends.
+ */
+ public void before(Animator anim) {
+ Node node = mNodeMap.get(anim);
+ if (node == null) {
+ node = new Node(anim);
+ mNodeMap.put(anim, node);
+ mNodes.add(node);
+ }
+ Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
+ node.addDependency(dependency);
+ }
+
+ /**
+ * Sets up the given animation to play when the animation supplied in the
+ * {@link AnimatorSet#play(Animator)} call that created this Builder object
+ * to start when the animation supplied in this method call ends.
+ *
+ * @param anim The animation whose end will cause the animation supplied to the
+ * {@link AnimatorSet#play(Animator)} method to play.
+ */
+ public void after(Animator anim) {
+ Node node = mNodeMap.get(anim);
+ if (node == null) {
+ node = new Node(anim);
+ mNodeMap.put(anim, node);
+ mNodes.add(node);
+ }
+ Dependency dependency = new Dependency(node, Dependency.AFTER);
+ mCurrentNode.addDependency(dependency);
+ }
+
+ /**
+ * Sets up the animation supplied in the
+ * {@link AnimatorSet#play(Animator)} call that created this Builder object
+ * to play when the given amount of time elapses.
+ *
+ * @param delay The number of milliseconds that should elapse before the
+ * animation starts.
+ */
+ public void after(long delay) {
+ // setup dummy ValueAnimator just to run the clock
+ after(new ValueAnimator(delay, 0f, 1f));
+ }
+
+ }
+
+}
diff --git a/core/java/android/animation/DoubleEvaluator.java b/core/java/android/animation/DoubleEvaluator.java
new file mode 100644
index 0000000000000000000000000000000000000000..e46eb372e5541294be87527fe6325743ad670d0a
--- /dev/null
+++ b/core/java/android/animation/DoubleEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between double values.
+ */
+public class DoubleEvaluator implements TypeEvaluator {
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
+ * where x0 is startValue, x1 is endValue,
+ * and t is fraction.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value; should be of type double or
+ * Double
+ * @param endValue The end value; should be of type double or
+ * Double
+ * @return A linear interpolation between the start and end values, given the
+ * fraction parameter.
+ */
+ public Object evaluate(float fraction, Object startValue, Object endValue) {
+ double startDouble = ((Number) startValue).doubleValue();
+ return startDouble + fraction * (((Number) endValue).doubleValue() - startDouble);
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e2054d0dc02d5086dd806be060469dffc72f0bf
--- /dev/null
+++ b/core/java/android/animation/FloatEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between float values.
+ */
+public class FloatEvaluator implements TypeEvaluator {
+
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
+ * where x0 is startValue, x1 is endValue,
+ * and t is fraction.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value; should be of type float or
+ * Float
+ * @param endValue The end value; should be of type float or Float
+ * @return A linear interpolation between the start and end values, given the
+ * fraction parameter.
+ */
+ public Object evaluate(float fraction, Object startValue, Object endValue) {
+ float startFloat = ((Number) startValue).floatValue();
+ return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java
new file mode 100644
index 0000000000000000000000000000000000000000..7288927f783b2ea55eb05cb6971bfa5238f768db
--- /dev/null
+++ b/core/java/android/animation/IntEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between int values.
+ */
+public class IntEvaluator implements TypeEvaluator {
+
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
+ * where x0 is startValue, x1 is endValue,
+ * and t is fraction.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value; should be of type int or
+ * Integer
+ * @param endValue The end value; should be of type int or Integer
+ * @return A linear interpolation between the start and end values, given the
+ * fraction parameter.
+ */
+ public Object evaluate(float fraction, Object startValue, Object endValue) {
+ int startInt = ((Number) startValue).intValue();
+ return (int) (startInt + fraction * (((Number) endValue).intValue() - startInt));
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/Keyframe.java b/core/java/android/animation/Keyframe.java
new file mode 100644
index 0000000000000000000000000000000000000000..192ba5c78d02fbcc5a5bd7f2d9cb4c5d90d2765a
--- /dev/null
+++ b/core/java/android/animation/Keyframe.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+import android.view.animation.Interpolator;
+
+/**
+ * This class holds a time/value pair for an animation. The Keyframe class is used
+ * by {@link ValueAnimator} to define the values that the animation target will have over the course
+ * of the animation. As the time proceeds from one keyframe to the other, the value of the
+ * target object will animate between the value at the previous keyframe and the value at the
+ * next keyframe. Each keyframe also holds an option {@link android.view.animation.Interpolator}
+ * object, which defines the time interpolation over the intervalue preceding the keyframe.
+ */
+public class Keyframe implements Cloneable {
+ /**
+ * The time at which mValue will hold true.
+ */
+ private float mFraction;
+
+ /**
+ * The value of the animation at the time mFraction.
+ */
+ private Object mValue;
+
+ /**
+ * The type of the value in this Keyframe. This type is determined at construction time,
+ * based on the type of the value object passed into the constructor.
+ */
+ private Class mValueType;
+
+ /**
+ * The optional time interpolator for the interval preceding this keyframe. A null interpolator
+ * (the default) results in linear interpolation over the interval.
+ */
+ private Interpolator mInterpolator = null;
+
+ /**
+ * Private constructor, called from the public constructors with the additional
+ * valueType parameter.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ * @param valueType The type of the value object. This is used by the
+ * {@link #getValue()} functionm, which is queried by {@link ValueAnimator} to determine
+ * the type of {@link TypeEvaluator} to use to interpolate between values.
+ */
+ private Keyframe(float fraction, Object value, Class valueType) {
+ mFraction = fraction;
+ mValue = value;
+ mValueType = valueType;
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public Keyframe(float fraction, Object value) {
+ this(fraction, value, (value != null) ? value.getClass() : Object.class);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and float value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public Keyframe(float fraction, Float value) {
+ this(fraction, value, Float.class);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and integer value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public Keyframe(float fraction, Integer value) {
+ this(fraction, value, Integer.class);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and double value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public Keyframe(float fraction, Double value) {
+ this(fraction, value, Double.class);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and integer value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public Keyframe(float fraction, int value) {
+ this(fraction, value, int.class);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and float value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public Keyframe(float fraction, float value) {
+ this(fraction, value, float.class);
+ }
+
+ /**
+ * Constructs a Keyframe object with the given time and double value. The time defines the
+ * time, as a proportion of an overall animation's duration, at which the value will hold true
+ * for the animation. The value for the animation between keyframes will be calculated as
+ * an interpolation between the values at those keyframes.
+ *
+ * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+ * of time elapsed of the overall animation duration.
+ * @param value The value that the object will animate to as the animation time approaches
+ * the time in this keyframe, and the the value animated from as the time passes the time in
+ * this keyframe.
+ */
+ public Keyframe(float fraction, double value) {
+ this(fraction, value, double.class);
+ }
+
+ /**
+ * Gets the value for this Keyframe.
+ *
+ * @return The value for this Keyframe.
+ */
+ public Object getValue() {
+ return mValue;
+ }
+
+ /**
+ * Sets the value for this Keyframe.
+ *
+ * @param value value for this Keyframe.
+ */
+ public void setValue(Object value) {
+ mValue = value;
+ }
+
+ /**
+ * Gets the time for this keyframe, as a fraction of the overall animation duration.
+ *
+ * @return The time associated with this keyframe, as a fraction of the overall animation
+ * duration. This should be a value between 0 and 1.
+ */
+ public float getFraction() {
+ return mFraction;
+ }
+
+ /**
+ * Sets the time for this keyframe, as a fraction of the overall animation duration.
+ *
+ * @param fraction time associated with this keyframe, as a fraction of the overall animation
+ * duration. This should be a value between 0 and 1.
+ */
+ public void setFraction(float fraction) {
+ mFraction = fraction;
+ }
+
+ /**
+ * Gets the optional interpolator for this Keyframe. A value of null indicates
+ * that there is no interpolation, which is the same as linear interpolation.
+ *
+ * @return The optional interpolator for this Keyframe.
+ */
+ public Interpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ /**
+ * Sets the optional interpolator for this Keyframe. A value of null indicates
+ * that there is no interpolation, which is the same as linear interpolation.
+ *
+ * @return The optional interpolator for this Keyframe.
+ */
+ public void setInterpolator(Interpolator interpolator) {
+ mInterpolator = interpolator;
+ }
+
+ /**
+ * Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
+ * {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
+ * on the type of Keyframe created.
+ *
+ * @return The type of the value stored in the Keyframe.
+ */
+ public Class getType() {
+ return mValueType;
+ }
+
+ @Override
+ public Keyframe clone() {
+ Keyframe kfClone = new Keyframe(mFraction, mValue, mValueType);
+ kfClone.setInterpolator(mInterpolator);
+ return kfClone;
+ }
+}
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..af47a158efde5778f30db1b0f53f5f7539ac5d92
--- /dev/null
+++ b/core/java/android/animation/KeyframeSet.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+import java.util.ArrayList;
+
+import android.view.animation.Interpolator;
+
+/**
+ * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ */
+class KeyframeSet {
+
+ private int mNumKeyframes;
+
+ ArrayListleft, top, right,
+ * and bottom. Values for these properties are updated with the pre- and post-layout
+ * values when the transition begins. Custom animations will be similarly populated with
+ * the target and values being animated, assuming they use ObjectAnimator objects with
+ * property names that are known on the target object.transitionType parameter determines the animation whose start delay
+ * is being set.
+ *
+ * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+ * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
+ * delay is being set.
+ * @param delay The length of time, in milliseconds, to delay before starting the animation.
+ * @see Animator#setStartDelay(long)
+ */
+ public void setStartDelay(int transitionType, long delay) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ mChangingAppearingDelay = delay;
+ break;
+ case CHANGE_DISAPPEARING:
+ mChangingDisappearingDelay = delay;
+ break;
+ case APPEARING:
+ mAppearingDelay = delay;
+ break;
+ case DISAPPEARING:
+ mDisappearingDelay = delay;
+ break;
+ }
+ }
+
+ /**
+ * Gets the start delay on one of the animation objects used by this transition. The
+ * transitionType parameter determines the animation whose start delay
+ * is returned.
+ *
+ * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+ * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
+ * delay is returned.
+ * @return long The start delay of the specified animation.
+ * @see Animator#getStartDelay()
+ */
+ public long getStartDelay(int transitionType) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ return mChangingAppearingDuration;
+ case CHANGE_DISAPPEARING:
+ return mChangingDisappearingDuration;
+ case APPEARING:
+ return mAppearingDuration;
+ case DISAPPEARING:
+ return mDisappearingDuration;
+ }
+ // shouldn't reach here
+ return 0;
+ }
+
+ /**
+ * Sets the duration on one of the animation objects used by this transition. The
+ * transitionType parameter determines the animation whose duration
+ * is being set.
+ *
+ * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+ * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+ * duration is being set.
+ * @param duration The length of time, in milliseconds, that the specified animation should run.
+ * @see Animator#setDuration(long)
+ */
+ public void setDuration(int transitionType, long duration) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ mChangingAppearingDuration = duration;
+ break;
+ case CHANGE_DISAPPEARING:
+ mChangingDisappearingDuration = duration;
+ break;
+ case APPEARING:
+ mAppearingDuration = duration;
+ break;
+ case DISAPPEARING:
+ mDisappearingDuration = duration;
+ break;
+ }
+ }
+
+ /**
+ * Gets the duration on one of the animation objects used by this transition. The
+ * transitionType parameter determines the animation whose duration
+ * is returned.
+ *
+ * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+ * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+ * duration is returned.
+ * @return long The duration of the specified animation.
+ * @see Animator#getDuration()
+ */
+ public long getDuration(int transitionType) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ return mChangingAppearingDuration;
+ case CHANGE_DISAPPEARING:
+ return mChangingDisappearingDuration;
+ case APPEARING:
+ return mAppearingDuration;
+ case DISAPPEARING:
+ return mDisappearingDuration;
+ }
+ // shouldn't reach here
+ return 0;
+ }
+
+ /**
+ * Sets the length of time to delay between starting each animation during one of the
+ * CHANGE animations.
+ *
+ * @param transitionType A value of {@link #CHANGE_APPEARING} or @link #CHANGE_DISAPPEARING}.
+ * @param duration The length of time, in milliseconds, to delay before launching the next
+ * animation in the sequence.
+ */
+ public void setStagger(int transitionType, long duration) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ mChangingAppearingStagger = duration;
+ break;
+ case CHANGE_DISAPPEARING:
+ mChangingDisappearingStagger = duration;
+ break;
+ // noop other cases
+ }
+ }
+
+ /**
+ * Tets the length of time to delay between starting each animation during one of the
+ * CHANGE animations.
+ *
+ * @param transitionType A value of {@link #CHANGE_APPEARING} or @link #CHANGE_DISAPPEARING}.
+ * @return long The length of time, in milliseconds, to delay before launching the next
+ * animation in the sequence.
+ */
+ public long getStagger(int transitionType) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ return mChangingAppearingStagger;
+ case CHANGE_DISAPPEARING:
+ return mChangingDisappearingStagger;
+ }
+ // shouldn't reach here
+ return 0;
+ }
+
+ /**
+ * Sets the interpolator on one of the animation objects used by this transition. The
+ * transitionType parameter determines the animation whose interpolator
+ * is being set.
+ *
+ * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+ * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+ * duration is being set.
+ * @param interpolator The interpolator that the specified animation should use.
+ * @see Animator#setInterpolator(android.view.animation.Interpolator)
+ */
+ public void setInterpolator(int transitionType, Interpolator interpolator) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ mChangingAppearingInterpolator = interpolator;
+ break;
+ case CHANGE_DISAPPEARING:
+ mChangingDisappearingInterpolator = interpolator;
+ break;
+ case APPEARING:
+ mAppearingInterpolator = interpolator;
+ break;
+ case DISAPPEARING:
+ mDisappearingInterpolator = interpolator;
+ break;
+ }
+ }
+
+ /**
+ * Gets the interpolator on one of the animation objects used by this transition. The
+ * transitionType parameter determines the animation whose interpolator
+ * is returned.
+ *
+ * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+ * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+ * duration is being set.
+ * @return Interpolator The interpolator that the specified animation uses.
+ * @see Animator#setInterpolator(android.view.animation.Interpolator)
+ */
+ public Interpolator getInterpolator(int transitionType) {
+ switch (transitionType) {
+ case CHANGE_APPEARING:
+ return mChangingAppearingInterpolator;
+ case CHANGE_DISAPPEARING:
+ return mChangingDisappearingInterpolator;
+ case APPEARING:
+ return mAppearingInterpolator;
+ case DISAPPEARING:
+ return mDisappearingInterpolator;
+ }
+ // shouldn't reach here
+ return null;
+ }
+
+ /**
+ * Sets the animation used during one of the transition types that may run. Any
+ * Animator object can be used, but to be most useful in the context of layout
+ * transitions, the animation should either be a ObjectAnimator or a AnimatorSet
+ * of animations including PropertyAnimators. Also, these ObjectAnimator objects
+ * should be able to get and set values on their target objects automatically. For
+ * example, a ObjectAnimator that animates the property "left" is able to set and get the
+ * left property from the View objects being animated by the layout
+ * transition. The transition works by setting target objects and properties
+ * dynamically, according to the pre- and post-layoout values of those objects, so
+ * having animations that can handle those properties appropriately will work best
+ * for custom animation. The dynamic setting of values is only the case for the
+ * CHANGE animations; the APPEARING and DISAPPEARING animations are simply run with
+ * the values they have.
+ *
+ * foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ *
+ * valueFrom and valueTo properties, otherwise the call to
+ * the setter function will fail.foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ */
+ public String getPropertyName() {
+ return mPropertyName;
+ }
+
+ /**
+ * Determine the setter or getter function using the JavaBeans convention of setFoo or
+ * getFoo for a property named 'foo'. This function figures out what the name of the
+ * function should be and uses reflection to find the Method with that name on the
+ * target object.
+ *
+ * @param prefix "set" or "get", depending on whether we need a setter or getter.
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method getPropertyFunction(String prefix, Class valueType) {
+ // TODO: faster implementation...
+ Method returnVal = null;
+ String firstLetter = mPropertyName.substring(0, 1);
+ String theRest = mPropertyName.substring(1);
+ firstLetter = firstLetter.toUpperCase();
+ String setterName = prefix + firstLetter + theRest;
+ Class args[] = null;
+ if (valueType != null) {
+ args = new Class[1];
+ args[0] = valueType;
+ }
+ try {
+ returnVal = mTarget.getClass().getMethod(setterName, args);
+ } catch (NoSuchMethodException e) {
+ Log.e("ObjectAnimator",
+ "Couldn't find setter/getter for property " + mPropertyName + ": " + e);
+ }
+ return returnVal;
+ }
+
+ /**
+ * Creates a new ObjectAnimator object. This default constructor is primarily for
+ * use internally; the other constructors which take parameters are more generally
+ * useful.
+ */
+ public ObjectAnimator() {
+ }
+
+ /**
+ * A constructor that takes a single property name and set of values. This constructor is
+ * used in the simple case of animating a single property.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param target The object whose property is to be animated. This object should
+ * have a public method on it called setName(), where name is
+ * the value of the propertyName parameter.
+ * @param propertyName The name of the property being animated.
+ * @param values The set of values to animate between. If there is only one value, it
+ * is assumed to be the final value being animated to, and the initial value will be
+ * derived on the fly.
+ */
+ public ObjectAnimator(long duration, Object target, String propertyName, T...values) {
+ super(duration, (T[]) values);
+ mTarget = target;
+ setPropertyName(propertyName);
+ }
+
+ /**
+ * A constructor that takes PropertyValueHolder values. This constructor should
+ * be used when animating several properties at once with the same ObjectAnimator, since
+ * PropertyValuesHolder allows you to associate a set of animation values with a property
+ * name.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param target The object whose property is to be animated. This object should
+ * have public methods on it called setName(), where name is
+ * the name of the property passed in as the propertyName parameter for
+ * each of the PropertyValuesHolder objects.
+ * @param values The PropertyValuesHolder objects which hold each the property name and values
+ * to animate that property between.
+ */
+ public ObjectAnimator(long duration, Object target, PropertyValuesHolder...values) {
+ super(duration);
+ setValues(values);
+ mTarget = target;
+ }
+
+ /**
+ * This function is called immediately before processing the first animation
+ * frame of an animation. If there is a nonzero startDelay, the
+ * function is called after that delay ends.
+ * It takes care of the final initialization steps for the
+ * animation. This includes setting mEvaluator, if the user has not yet
+ * set it up, and the setter/getter methods, if the user did not supply
+ * them.
+ *
+ * end()
+ * function is called, to set the final value on the property.
+ *
+ * propertyName or set explicitly via
+ * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param propertyName The name of the property associated with this set of values. This
+ * can be the actual property name to be used when using a ObjectAnimator object, or
+ * just a name used to get animated values, such as if this object is used with an
+ * ValueAnimator object.
+ * @param values The set of values to animate between.
+ */
+ public PropertyValuesHolder(String propertyName, T... values) {
+ mPropertyName = propertyName;
+ setValues(values);
+ }
+
+ /**
+ * Sets the values being animated between.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function either
+ * derived automatically from propertyName or set explicitly via
+ * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param values The set of values to animate between.
+ */
+ public void setValues(T... values) {
+ int numKeyframes = values.length;
+ for (int i = 0; i < numKeyframes; ++i) {
+ if (values[i] != null) {
+ Class thisValueType = values[i].getClass();
+ if (mValueType == null) {
+ mValueType = thisValueType;
+ } else {
+ if (thisValueType != mValueType) {
+ if (mValueType == Integer.class &&
+ (thisValueType == Float.class || thisValueType == Double.class)) {
+ mValueType = thisValueType;
+ } else if (mValueType == Float.class && thisValueType == Double.class) {
+ mValueType = thisValueType;
+ }
+ }
+ }
+ }
+ }
+ Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ if (mValueType.equals(Keyframe.class)) {
+ mValueType = ((Keyframe)values[0]).getType();
+ for (int i = 0; i < numKeyframes; ++i) {
+ keyframes[i] = (Keyframe)values[i];
+ }
+ } else {
+ if (numKeyframes == 1) {
+ keyframes[0] = new Keyframe(0f, (Object) null);
+ keyframes[1] = new Keyframe(1f, values[0]);
+ } else {
+ keyframes[0] = new Keyframe(0f, values[0]);
+ for (int i = 1; i < numKeyframes; ++i) {
+ if (values[i] != null && (values[i].getClass() != mValueType)) {
+
+ }
+ keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]);
+ }
+ }
+ }
+ mKeyframeSet = new KeyframeSet(keyframes);
+ }
+
+
+
+ /**
+ * Determine the setter or getter function using the JavaBeans convention of setFoo or
+ * getFoo for a property named 'foo'. This function figures out what the name of the
+ * function should be and uses reflection to find the Method with that name on the
+ * target object.
+ *
+ * @param targetClass The class to search for the method
+ * @param prefix "set" or "get", depending on whether we need a setter or getter.
+ * @param valueType The type of the parameter (in the case of a setter). This type
+ * is derived from the values set on this PropertyValuesHolder. This type is used as
+ * a first guess at the parameter type, but we check for methods with several different
+ * types to avoid problems with slight mis-matches between supplied values and actual
+ * value types used on the setter.
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
+ // TODO: faster implementation...
+ Method returnVal = null;
+ String firstLetter = mPropertyName.substring(0, 1);
+ String theRest = mPropertyName.substring(1);
+ firstLetter = firstLetter.toUpperCase();
+ String methodName = prefix + firstLetter + theRest;
+ Class args[] = null;
+ if (valueType == null) {
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ } catch (NoSuchMethodException e) {
+ Log.e("PropertyValuesHolder",
+ "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
+ }
+ } else {
+ args = new Class[1];
+ Class typeVariants[];
+ if (mValueType.equals(Float.class)) {
+ typeVariants = FLOAT_VARIANTS;
+ } else if (mValueType.equals(Integer.class)) {
+ typeVariants = INTEGER_VARIANTS;
+ } else if (mValueType.equals(Double.class)) {
+ typeVariants = DOUBLE_VARIANTS;
+ } else {
+ typeVariants = new Class[1];
+ typeVariants[0] = mValueType;
+ }
+ for (Class typeVariant : typeVariants) {
+ args[0] = typeVariant;
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ // change the value type to suit
+ mValueType = typeVariant;
+ return returnVal;
+ } catch (NoSuchMethodException e) {
+ // Swallow the error and keep trying other variants
+ }
+ }
+ // If we got here, then no appropriate function was found
+ Log.e("PropertyValuesHolder",
+ "Couldn't find setter/getter for property " + mPropertyName +
+ "with value type "+ mValueType);
+ }
+
+ return returnVal;
+ }
+
+
+ /**
+ * Returns the setter or getter requested. This utility function checks whether the
+ * requested method exists in the propertyMapMap cache. If not, it calls another
+ * utility function to request the Method from the targetClass directly.
+ * @param targetClass The Class on which the requested method should exist.
+ * @param propertyMapMap The cache of setters/getters derived so far.
+ * @param prefix "set" or "get", for the setter or getter.
+ * @param valueType The type of parameter passed into the method (null for getter).
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method setupSetterOrGetter(Class targetClass,
+ HashMapMethod that is called with the animated values calculated
+ * during the animation. Setting the setter method is an alternative to supplying a
+ * {@link #setPropertyName(String) propertyName} from which the method is derived. This
+ * approach is more direct, and is especially useful when a function must be called that does
+ * not correspond to the convention of setName(). For example, if a function
+ * called offset() is to be called with the animated values, there is no way
+ * to tell ObjectAnimator how to call that function simply through a property
+ * name, so a setter method should be supplied instead.
+ *
+ * valueFrom and valueTo properties, otherwise the call to
+ * the setter function will fail.Method that is called with the animated values calculated
+ * during the animation.
+ */
+ public Method getSetter() {
+ return mSetter;
+ }
+
+ /**
+ * Sets the Method that is called to get unsupplied valueFrom or
+ * valueTo properties. Setting the getter method is an alternative to supplying a
+ * {@link #setPropertyName(String) propertyName} from which the method is derived. This
+ * approach is more direct, and is especially useful when a function must be called that does
+ * not correspond to the convention of setName(). For example, if a function
+ * called offset() is to be called to get an initial value, there is no way
+ * to tell ObjectAnimator how to call that function simply through a property
+ * name, so a getter method should be supplied instead.
+ *
+ * valueFrom or valueTo are
+ * null. If both of those values are non-null, then there is no need to get one of the
+ * values and the getter is not called.
+ *
+ * valueFrom and valueTo properties (whichever of them are
+ * non-null), otherwise the call to the getter function will fail.Method that is called to get unsupplied valueFrom or
+ * valueTo properties.
+ */
+ public Method getGetter() {
+ return mGetter;
+ }
+
+ /**
+ * Sets the name of the property that will be animated. This name is used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ *
+ * valueFrom and valueTo properties, otherwise the call to
+ * the setter function will fail.foo will result
+ * in a call to the function setFoo() on the target object. If either
+ * valueFrom or valueTo is null, then a getter function will
+ * also be derived and called.
+ */
+ public String getPropertyName() {
+ return mPropertyName;
+ }
+
+ /**
+ * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
+ * most recently calculated in calculateValue().
+ * @return
+ */
+ Object getAnimatedValue() {
+ return mAnimatedValue;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/RGBEvaluator.java b/core/java/android/animation/RGBEvaluator.java
new file mode 100644
index 0000000000000000000000000000000000000000..bae0af0c8b36b3e5b381ef36f8f29c8329b4372a
--- /dev/null
+++ b/core/java/android/animation/RGBEvaluator.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between integer
+ * values that represent ARGB colors.
+ */
+public class RGBEvaluator implements TypeEvaluator {
+
+ /**
+ * This function returns the calculated in-between value for a color
+ * given integers that represent the start and end values in the four
+ * bytes of the 32-bit int. Each channel is separately linearly interpolated
+ * and the resulting calculated values are recombined into the return value.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue A 32-bit int value representing colors in the
+ * separate bytes of the parameter
+ * @param endValue A 32-bit int value representing colors in the
+ * separate bytes of the parameter
+ * @return A value that is calculated to be the linearly interpolated
+ * result, derived by separating the start and end values into separate
+ * color channels and interpolating each one separately, recombining the
+ * resulting values in the same way.
+ */
+ public Object evaluate(float fraction, Object startValue, Object endValue) {
+ int startInt = (Integer) startValue;
+ int startA = (startInt >> 24);
+ int startR = (startInt >> 16) & 0xff;
+ int startG = (startInt >> 8) & 0xff;
+ int startB = startInt & 0xff;
+
+ int endInt = (Integer) endValue;
+ int endA = (endInt >> 24);
+ int endR = (endInt >> 16) & 0xff;
+ int endG = (endInt >> 8) & 0xff;
+ int endB = endInt & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+ (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+ (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+ (int)((startB + (int)(fraction * (endB - startB))));
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/TypeEvaluator.java b/core/java/android/animation/TypeEvaluator.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa49175ce846f8a7dc278b9025c7a76aaa5dcb7d
--- /dev/null
+++ b/core/java/android/animation/TypeEvaluator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+/**
+ * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
+ * allow developers to create animations on arbitrary property types, by allowing them to supply
+ * custom evaulators for types that are not automatically understood and used by the animation
+ * system.
+ *
+ * @see ValueAnimator#setEvaluator(TypeEvaluator)
+ */
+public interface TypeEvaluator {
+
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),
+ * where x0 is startValue, x1 is endValue,
+ * and t is fraction.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value.
+ * @param endValue The end value.
+ * @return A linear interpolation between the start and end values, given the
+ * fraction parameter.
+ */
+ public Object evaluate(float fraction, Object startValue, Object endValue);
+
+}
\ No newline at end of file
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
new file mode 100755
index 0000000000000000000000000000000000000000..1e2bbccb74477330fa1480a4af04d6b4f33d68f7
--- /dev/null
+++ b/core/java/android/animation/ValueAnimator.java
@@ -0,0 +1,981 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.animation;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class provides a simple timing engine for running animations
+ * which calculate animated values and set them on target objects.
+ *
+ * repeatCount is INFINITE
+ * or a positive value, the animation restarts from the beginning.
+ */
+ public static final int RESTART = 1;
+ /**
+ * When the animation reaches the end and repeatCount is INFINITE
+ * or a positive value, the animation reverses direction on every iteration.
+ */
+ public static final int REVERSE = 2;
+ /**
+ * This value used used with the {@link #setRepeatCount(int)} property to repeat
+ * the animation indefinitely.
+ */
+ public static final int INFINITE = -1;
+
+ /**
+ * Creates a new ValueAnimator object. This default constructor is primarily for
+ * use internally; the other constructors which take parameters are more generally
+ * useful.
+ */
+ public ValueAnimator() {
+ }
+
+ /**
+ * Constructs an ValueAnimator object with the specified duration and set of
+ * values. If the values are a set of PropertyValuesHolder objects, then these objects
+ * define the potentially multiple properties being animated and the values the properties are
+ * animated between. Otherwise, the values define a single set of values animated between.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param values The set of values to animate between. If these values are not
+ * PropertyValuesHolder objects, then there should be more than one value, since the values
+ * determine the interval to animate between.
+ */
+ public ValueAnimator(long duration, T...values) {
+ mDuration = duration;
+ if (values.length > 0) {
+ setValues(values);
+ }
+ }
+
+ /**
+ * Sets the values, per property, being animated between. This function is called internally
+ * by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can
+ * be constructed without values and this method can be called to set the values manually
+ * instead.
+ *
+ * @param values The set of values, per property, being animated between.
+ */
+ public void setValues(PropertyValuesHolder... values) {
+ int numValues = values.length;
+ mValues = values;
+ mValuesMap = new HashMapvalues is
+ * a set of PropertyValuesHolder objects, these objects will become the set of properties
+ * animated and the values that those properties are animated between. Otherwise, this method
+ * will set only one set of values for the ValueAnimator. Also, if the values are not
+ * PropertyValuesHolder objects and if there are already multiple sets of
+ * values defined for this ValueAnimator via
+ * more than one PropertyValuesHolder objects, this method will set the values for
+ * the first of those objects.
+ *
+ * @param values The set of values to animate between.
+ */
+ public void setValues(T... values) {
+ if (mValues == null || mValues.length == 0) {
+ setValues(new PropertyValuesHolder[]{
+ new PropertyValuesHolder("", (Object[])values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ valuesHolder.setValues(values);
+ }
+ }
+
+ /**
+ * This function is called immediately before processing the first animation
+ * frame of an animation. If there is a nonzero startDelay, the
+ * function is called after that delay ends.
+ * It takes care of the final initialization steps for the
+ * animation.
+ *
+ * ValueAnimator when there is just one
+ * property being animated. This value is only sensible while the animation is running. The main
+ * purpose for this read-only property is to retrieve the value from the ValueAnimator
+ * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+ * is called during each animation frame, immediately after the value is calculated.
+ *
+ * @return animatedValue The value most recently calculated by this ValueAnimator for
+ * the single property being animated. If there are several properties being animated
+ * (specified by several PropertyValuesHolder objects in the constructor), this function
+ * returns the animated value for the first of those objects.
+ */
+ public Object getAnimatedValue() {
+ if (mValues != null && mValues.length > 0) {
+ return mValues[0].getAnimatedValue();
+ }
+ // Shouldn't get here; should always have values unless ValueAnimator was set up wrong
+ return null;
+ }
+
+ /**
+ * The most recent value calculated by this ValueAnimator for propertyName.
+ * The main purpose for this read-only property is to retrieve the value from the
+ * ValueAnimator during a call to
+ * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+ * is called during each animation frame, immediately after the value is calculated.
+ *
+ * @return animatedValue The value most recently calculated for the named property
+ * by this ValueAnimator.
+ */
+ public Object getAnimatedValue(String propertyName) {
+ PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
+ if (valuesHolder != null) {
+ return valuesHolder.getAnimatedValue();
+ } else {
+ // At least avoid crashing if called with bogus propertyName
+ return null;
+ }
+ }
+
+ /**
+ * Sets how many times the animation should be repeated. If the repeat
+ * count is 0, the animation is never repeated. If the repeat count is
+ * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
+ * into account. The repeat count is 0 by default.
+ *
+ * @param value the number of times the animation should be repeated
+ */
+ public void setRepeatCount(int value) {
+ mRepeatCount = value;
+ }
+ /**
+ * Defines how many times the animation should repeat. The default value
+ * is 0.
+ *
+ * @return the number of times the animation should repeat, or {@link #INFINITE}
+ */
+ public int getRepeatCount() {
+ return mRepeatCount;
+ }
+
+ /**
+ * Defines what this animation should do when it reaches the end. This
+ * setting is applied only when the repeat count is either greater than
+ * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
+ *
+ * @param value {@link #RESTART} or {@link #REVERSE}
+ */
+ public void setRepeatMode(int value) {
+ mRepeatMode = value;
+ }
+
+ /**
+ * Defines what this animation should do when it reaches the end.
+ *
+ * @return either one of {@link #REVERSE} or {@link #RESTART}
+ */
+ public int getRepeatMode() {
+ return mRepeatMode;
+ }
+
+ /**
+ * Adds a listener to the set of listeners that are sent update events through the life of
+ * an animation. This method is called on all listeners for every frame of the animation,
+ * after the values for the animation have been calculated.
+ *
+ * @param listener the listener to be added to the current set of listeners for this animation.
+ */
+ public void addUpdateListener(AnimatorUpdateListener listener) {
+ if (mUpdateListeners == null) {
+ mUpdateListeners = new ArrayListstartValue and endValue in the constructor. But if these values
+ * are not one of these primitive types, or if different evaluation is desired (such as is
+ * necessary with int values that represent colors), a custom evaluator needs to be assigned.
+ * For example, when running an animation on color values, the {@link RGBEvaluator}
+ * should be used to get correct RGB color interpolation.
+ *
+ * startDelay phase. The return value indicates whether it
+ * should be woken up and put on the active animations queue.
+ *
+ * @param currentTime The current animation time, used to calculate whether the animation
+ * has exceeded its startDelay and should be started.
+ * @return True if the animation's startDelay has been exceeded and the animation
+ * should be added to the set of active animations.
+ */
+ private boolean delayedAnimationFrame(long currentTime) {
+ if (mPlayingState == CANCELED || mPlayingState == ENDED) {
+ // end the delay, process an animation frame to actually cancel it
+ return true;
+ }
+ if (!mStartedDelay) {
+ mStartedDelay = true;
+ mDelayStartTime = currentTime;
+ } else {
+ long deltaTime = currentTime - mDelayStartTime;
+ if (deltaTime > mStartDelay) {
+ // startDelay ended - start the anim and record the
+ // mStartTime appropriately
+ mStartTime = currentTime - (deltaTime - mStartDelay);
+ mPlayingState = RUNNING;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This internal function processes a single animation frame for a given animation. The
+ * currentTime parameter is the timing pulse sent by the handler, used to calculate the
+ * elapsed duration, and therefore
+ * the elapsed fraction, of the animation. The return value indicates whether the animation
+ * should be ended (which happens when the elapsed time of the animation exceeds the
+ * animation's duration, including the repeatCount).
+ *
+ * @param currentTime The current time, as tracked by the static timing handler
+ * @return true if the animation's duration, including any repetitions due to
+ * repeatCount has been exceeded and the animation should be ended.
+ */
+ private boolean animationFrame(long currentTime) {
+ boolean done = false;
+
+ if (mPlayingState == STOPPED) {
+ mPlayingState = RUNNING;
+ if (mSeekTime < 0) {
+ mStartTime = currentTime;
+ } else {
+ mStartTime = currentTime - mSeekTime;
+ // Now that we're playing, reset the seek time
+ mSeekTime = -1;
+ }
+ }
+ switch (mPlayingState) {
+ case RUNNING:
+ case SEEKED:
+ float fraction = (float)(currentTime - mStartTime) / mDuration;
+ if (fraction >= 1f) {
+ if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
+ // Time to repeat
+ if (mListeners != null) {
+ for (AnimatorListener listener : mListeners) {
+ listener.onAnimationRepeat(this);
+ }
+ }
+ ++mCurrentIteration;
+ if (mRepeatMode == REVERSE) {
+ mPlayingBackwards = mPlayingBackwards ? false : true;
+ }
+ // TODO: doesn't account for fraction going Wayyyyy over 1, like 2+
+ fraction = fraction - 1f;
+ mStartTime += mDuration;
+ } else {
+ done = true;
+ fraction = Math.min(fraction, 1.0f);
+ }
+ }
+ if (mPlayingBackwards) {
+ fraction = 1f - fraction;
+ }
+ animateValue(fraction);
+ break;
+ case ENDED:
+ // The final value set on the target varies, depending on whether the animation
+ // was supposed to repeat an odd number of times
+ if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
+ animateValue(0f);
+ } else {
+ animateValue(1f);
+ }
+ // Fall through to set done flag
+ case CANCELED:
+ done = true;
+ mPlayingState = STOPPED;
+ break;
+ }
+
+ return done;
+ }
+
+ /**
+ * This method is called with the elapsed fraction of the animation during every
+ * animation frame. This function turns the elapsed fraction into an interpolated fraction
+ * and then into an animated value (from the evaluator. The function is called mostly during
+ * animation updates, but it is also called when the end()
+ * function is called, to set the final value on the property.
+ *
+ * ValueAnimator instance to receive callbacks on every animation
+ * frame, after the current frame's values have been calculated for that
+ * ValueAnimator.
+ */
+ public static interface AnimatorUpdateListener {
+ /**
+ *
+ *
+ *
+ * @return The current navigation mode.
+ *
+ * @see #setStandardNavigationMode()
+ * @see #setStandardNavigationMode(CharSequence)
+ * @see #setStandardNavigationMode(CharSequence, CharSequence)
+ * @see #setDropdownNavigationMode(SpinnerAdapter)
+ * @see #setTabNavigationMode()
+ * @see #setCustomNavigationMode(View)
+ */
+ public abstract int getNavigationMode();
+
+ /**
+ * @return The current set of display options.
+ */
+ public abstract int getDisplayOptions();
+
+ /**
+ * Set the action bar into tabbed navigation mode.
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ * @see #removeTab(Tab)
+ * @see #removeTabAt(int)
+ */
+ public abstract void setTabNavigationMode();
+
+ /**
+ * Create and return a new {@link Tab}.
+ * This tab will not be included in the action bar until it is added.
+ *
+ * @return A new Tab
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ */
+ public abstract Tab newTab();
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
+ *
+ * @param tab Tab to add
+ */
+ public abstract void addTab(Tab tab);
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be inserted at
+ * position.
+ *
+ * @param tab The tab to add
+ * @param position The new position of the tab
+ */
+ public abstract void addTab(Tab tab, int position);
+
+ /**
+ * Remove a tab from the action bar.
+ *
+ * @param tab The tab to remove
+ */
+ public abstract void removeTab(Tab tab);
+
+ /**
+ * Remove a tab from the action bar.
+ *
+ * @param position Position of the tab to remove
+ */
+ public abstract void removeTabAt(int position);
+
+ /**
+ * Select the specified tab. If it is not a child of this action bar it will be added.
+ *
+ * @param tab Tab to select
+ */
+ public abstract void selectTab(Tab tab);
+
+ /**
+ * Returns the currently selected tab if in tabbed navigation mode and there is at least
+ * one tab present.
+ *
+ * @return The currently selected tab or null
+ */
+ public abstract Tab getSelectedTab();
+
+ /**
+ * Retrieve the current height of the ActionBar.
+ *
+ * @return The ActionBar's height
+ */
+ public abstract int getHeight();
+
+ /**
+ * Show the ActionBar if it is not currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void show();
+
+ /**
+ * Hide the ActionBar if it is not currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void hide();
+
+ /**
+ * @return true if the ActionBar is showing, false otherwise.
+ */
+ public abstract boolean isShowing();
+
+ /**
+ * Callback interface for ActionBar navigation events.
+ */
+ public interface NavigationCallback {
+ /**
+ * This method is called whenever a navigation item in your action bar
+ * is selected.
+ *
+ * @param itemPosition Position of the item clicked.
+ * @param itemId ID of the item clicked.
+ * @return True if the event was handled, false otherwise.
+ */
+ public boolean onNavigationItemSelected(int itemPosition, long itemId);
+ }
+
+ /**
+ * A tab in the action bar.
+ *
+ *
+ *
*
+ *
+ * Fragments
+ *
+ * Activity Lifecycle
*
@@ -592,7 +607,7 @@ import java.util.HashMap;
* or finished.
*/
public class Activity extends ContextThemeWrapper
- implements LayoutInflater.Factory,
+ implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
private static final String TAG = "Activity";
@@ -604,9 +619,8 @@ public class Activity extends ContextThemeWrapper
/** Start of user-defined activity results. */
public static final int RESULT_FIRST_USER = 1;
- private static long sInstanceCount = 0;
-
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
+ private static final String FRAGMENTS_TAG = "android:fragments";
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
@@ -628,18 +642,28 @@ public class Activity extends ContextThemeWrapper
private ComponentName mComponent;
/*package*/ ActivityInfo mActivityInfo;
/*package*/ ActivityThread mMainThread;
- /*package*/ Object mLastNonConfigurationInstance;
- /*package*/ HashMapandroid:immersive but may be changed at runtime by
+ * {@link #setImmersive}.
+ *
+ * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+ * @hide
+ */
+ public boolean isImmersive() {
+ try {
+ return ActivityManagerNative.getDefault().isImmersive(mToken);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Adjust the current immersive mode setting.
+ *
+ * Note that changing this value will have no effect on the activity's
+ * {@link android.content.pm.ActivityInfo} structure; that is, if
+ * android:immersive is set to true
+ * in the application's manifest entry for this activity, the {@link
+ * android.content.pm.ActivityInfo#flags ActivityInfo.flags} member will
+ * always have its {@link android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+ * FLAG_IMMERSIVE} bit set.
+ *
+ * @see #isImmersive
+ * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+ * @hide
+ */
+ public void setImmersive(boolean i) {
+ try {
+ ActivityManagerNative.getDefault().setImmersive(mToken, i);
+ } catch (RemoteException e) {
+ // pass
+ }
+ }
+
+ /**
+ * Start a context mode.
+ *
+ * @param callback Callback that will manage lifecycle events for this context mode
+ * @return The ContextMode that was started, or null if it was canceled
+ *
+ * @see ActionMode
+ */
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return mWindow.getDecorView().startActionMode(callback);
+ }
+
+ public ActionMode onStartActionMode(ActionMode.Callback callback) {
+ initActionBar();
+ if (mActionBar != null) {
+ return mActionBar.startActionMode(callback);
+ }
+ return null;
+ }
+
// ------------------ Internal API ------------------
final void setParent(Activity parent) {
@@ -3763,28 +4217,30 @@ public class Activity extends ContextThemeWrapper
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
Application application, Intent intent, ActivityInfo info, CharSequence title,
- Activity parent, String id, Object lastNonConfigurationInstance,
+ Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id,
- lastNonConfigurationInstance, null, config);
+ lastNonConfigurationInstances, config);
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
- Object lastNonConfigurationInstance,
- HashMap
+ *
+ *
+ *
+ * Lifecycle
+ *
+ * Basic Dialog
+ *
+ * Alert Dialog
+ *
+ * Selecting Between Dialog or Embedding
+ *
+ *
+ *
+ *
+ *
+ * Lifecycle
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Layout
+ *
+ * <fragment> tags
+ * to embed fragment instances inside of the layout. For example, here is
+ * a simple layout that embeds one fragment:
+ *
+ *
+ *
+ * android:tag can be used in <fragment> to provide
+ * a specific tag name for the fragment.
+ * android:id can be used in <fragment> to provide
+ * a specific identifier for the fragment.
+ * Back Stack
+ *
+ *
+ *
+ */
+ public void setRetainInstance(boolean retain) {
+ mRetainInstance = retain;
+ }
+
+ final public boolean getRetainInstance() {
+ return mRetainInstance;
+ }
+
+ /**
+ * Report that this fragment would like to participate in populating
+ * the options menu by receiving a call to {@link #onCreateOptionsMenu}
+ * and related methods.
+ *
+ * @param hasMenu If true, the fragment has menu items to contribute.
+ */
+ public void setHasOptionsMenu(boolean hasMenu) {
+ if (mHasMenu != hasMenu) {
+ mHasMenu = hasMenu;
+ if (isAdded() && !isHidden()) {
+ mActivity.invalidateOptionsMenu();
+ }
+ }
+ }
+
+ /**
+ * Return the LoaderManager for this fragment, creating it if needed.
+ */
+ public LoaderManager getLoaderManager() {
+ if (mLoaderManager != null) {
+ return mLoaderManager;
+ }
+ mCheckedForLoaderManager = true;
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, true);
+ return mLoaderManager;
+ }
+
+ /**
+ * Call {@link Activity#startActivity(Intent)} on the fragment's
+ * containing Activity.
+ */
+ public void startActivity(Intent intent) {
+ mActivity.startActivityFromFragment(this, intent, -1);
+ }
+
+ /**
+ * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's
+ * containing Activity.
+ */
+ public void startActivityForResult(Intent intent, int requestCode) {
+ mActivity.startActivityFromFragment(this, intent, requestCode);
+ }
+
+ /**
+ * Receive the result from a previous call to
+ * {@link #startActivityForResult(Intent, int)}. This follows the
+ * related Activity API as described there in
+ * {@link Activity#onActivityResult(int, int, Intent)}.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ */
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ }
+
+ /**
+ * Called when a fragment is being created as part of a view layout
+ * inflation, typically from setting the content view of an activity. This
+ * will be called immediately after the fragment is created from a false otherwise.
+ */
+ public boolean isEmpty();
+
+ /**
+ * Bit mask that is set for all enter transitions.
+ */
+ public final int TRANSIT_ENTER_MASK = 0x1000;
+
+ /**
+ * Bit mask that is set for all exit transitions.
+ */
+ public final int TRANSIT_EXIT_MASK = 0x2000;
+
+ /** Not set up for a transition. */
+ public final int TRANSIT_UNSET = -1;
+ /** No animation for transition. */
+ public final int TRANSIT_NONE = 0;
+ /** Fragment is being added */
+ public final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK;
+ /** Fragment is being removed */
+ public final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK;
+
+ /**
+ * Set specific animation resources to run for the fragments that are
+ * entering and exiting in this transaction.
+ */
+ public FragmentTransaction setCustomAnimations(int enter, int exit);
+
+ /**
+ * Select a standard transition animation for this transaction. May be
+ * one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
+ * or {@link #TRANSIT_FRAGMENT_CLOSE}
+ */
+ public FragmentTransaction setTransition(int transit);
+
+ /**
+ * Set a custom style resource that will be used for resolving transit
+ * animations.
+ */
+ public FragmentTransaction setTransitionStyle(int styleRes);
+
+ /**
+ * Add this transaction to the back stack. This means that the transaction
+ * will be remembered after it is committed, and will reverse its operation
+ * when later popped off the stack.
+ *
+ * @param name An optional name for this back stack state, or null.
+ */
+ public FragmentTransaction addToBackStack(String name);
+
+ /**
+ * Set the full title to show as a bread crumb when this transaction
+ * is on the back stack, as used by {@link FragmentBreadCrumbs}.
+ *
+ * @param res A string resource containing the title.
+ */
+ public FragmentTransaction setBreadCrumbTitle(int res);
+
+ /**
+ * Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this
+ * method is not recommended, as the string can not be changed
+ * later if the locale changes.
+ */
+ public FragmentTransaction setBreadCrumbTitle(CharSequence text);
+
+ /**
+ * Set the short title to show as a bread crumb when this transaction
+ * is on the back stack, as used by {@link FragmentBreadCrumbs}.
+ *
+ * @param res A string resource containing the title.
+ */
+ public FragmentTransaction setBreadCrumbShortTitle(int res);
+
+ /**
+ * Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this
+ * method is not recommended, as the string can not be changed
+ * later if the locale changes.
+ */
+ public FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
+
+ /**
+ * Schedules a commit of this transaction. Note that the commit does
+ * not happen immediately; it will be scheduled as work on the main thread
+ * to be done the next time that thread is ready.
+ *
+ * @return Returns the identifier of this transaction's back stack entry,
+ * if {@link #addToBackStack(String)} had been called. Otherwise, returns
+ * a negative number.
+ */
+ public int commit();
+}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f9bd46134ccb1634b3fa7ccef5703ed2418022dd..901f117c2a7fe785d61a6f204234e98a2edaf708 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -311,6 +311,10 @@ public interface IActivityManager extends IInterface {
public void finishHeavyWeightApp() throws RemoteException;
+ public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
+ public boolean isImmersive(IBinder token) throws RemoteException;
+ public boolean isTopActivityImmersive() throws RemoteException;
+
public void crashApplication(int uid, int initialPid, String packageName,
String message) throws RemoteException;
@@ -320,6 +324,10 @@ public interface IActivityManager extends IInterface {
public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
int mode) throws RemoteException;
+ // Cause the specified process to dump the specified heap.
+ public boolean dumpHeap(String process, boolean managed, String path,
+ ParcelFileDescriptor fd) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -529,4 +537,5 @@ public interface IActivityManager extends IInterface {
int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
+ int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index c8ef17f1b7a94d977b99e8863648bbf96c9beb1f..1f8a7c58325fe3de061c06a4a535577a1a30688a 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -97,13 +97,17 @@ public interface IApplicationThread extends IInterface {
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
throws RemoteException;
+ void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
+ throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException;
static final int PACKAGE_REMOVED = 0;
static final int EXTERNAL_STORAGE_UNAVAILABLE = 1;
void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException;
void scheduleCrash(String msg) throws RemoteException;
-
+ void dumpActivity(FileDescriptor fd, IBinder servicetoken, String[] args)
+ throws RemoteException;
+
String descriptor = "android.app.IApplicationThread";
int SCHEDULE_PAUSE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
@@ -140,4 +144,6 @@ public interface IApplicationThread extends IInterface {
int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
+ int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
+ int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b8c3aa3622641996b5340bde6c52418cfb3f396e..4d5f36ac7131bb825f34e42516848ddffdf6c102 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -997,8 +997,10 @@ public class Instrumentation {
IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
- activity.attach(context, aThread, this, token, application, intent, info, title,
- parent, id, lastNonConfigurationInstance, new Configuration());
+ activity.attach(context, aThread, this, token, application, intent,
+ info, title, parent, id,
+ (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
+ new Configuration());
return activity;
}
@@ -1058,21 +1060,23 @@ public class Instrumentation {
}
public void callActivityOnDestroy(Activity activity) {
- if (mWaitingActivities != null) {
- synchronized (mSync) {
- final int N = mWaitingActivities.size();
- for (int i=0; i
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:orientation="vertical"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent"
+ * android:paddingLeft="8dp"
+ * android:paddingRight="8dp">
+ *
+ * <ListView android:id="@id/android:list"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent"
+ * android:background="#00FF00"
+ * android:layout_weight="1"
+ * android:drawSelectorOnTop="false"/>
+ *
+ * <TextView android:id="@id/android:empty"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent"
+ * android:background="#FF0000"
+ * android:text="No data"/>
+ * </LinearLayout>
+ *
+ *
+ *
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:layout_width="match_parent"
+ * android:layout_height="wrap_content"
+ * android:orientation="vertical">
+ *
+ * <TextView android:id="@+id/text1"
+ * android:textSize="16sp"
+ * android:textStyle="bold"
+ * android:layout_width="match_parent"
+ * android:layout_height="wrap_content"/>
+ *
+ * <TextView android:id="@+id/text2"
+ * android:textSize="16sp"
+ * android:layout_width="match_parent"
+ * android:layout_height="wrap_content"/>
+ * </LinearLayout>
+ *
+ *
+ *