diff options
20 files changed, 549 insertions, 121 deletions
diff --git a/api/current.xml b/api/current.xml index 9fc6e3ede857..4f9ba23a8430 100644 --- a/api/current.xml +++ b/api/current.xml @@ -23228,6 +23228,17 @@ <parameter name="exitAnim" type="int"> </parameter> </method> +<method name="recreate" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="registerForContextMenu" return="void" abstract="false" @@ -89752,6 +89763,17 @@ visibility="public" > </method> +<method name="getPreferredPreviewSizeForVideo" + return="android.hardware.Camera.Size" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getPreviewFormat" return="int" abstract="false" @@ -89941,6 +89963,17 @@ visibility="public" > </method> +<method name="getSupportedVideoSizes" + return="java.util.List<android.hardware.Camera.Size>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSupportedWhiteBalance" return="java.util.List<java.lang.String>" abstract="false" @@ -194856,6 +194889,17 @@ visibility="public" > </method> +<method name="getLocalState" + return="java.lang.Object" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getResult" return="boolean" abstract="false" @@ -208857,6 +208901,8 @@ </parameter> <parameter name="myWindowOnly" type="boolean"> </parameter> +<parameter name="myLocalState" type="java.lang.Object"> +</parameter> </method> <method name="unscheduleDrawable" return="void" diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 6b619fb5567d..0a2e031dd74b 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -42,6 +42,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Parcelable; import android.os.RemoteException; import android.text.Selection; @@ -78,6 +79,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; /** * An activity is a single, focused thing that the user can do. Almost all @@ -842,8 +844,6 @@ public class Activity extends ContextThemeWrapper * @see #onPostCreate */ protected void onCreate(Bundle savedInstanceState) { - mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( - com.android.internal.R.styleable.Window_windowNoDisplay, false); if (mLastNonConfigurationInstances != null) { mAllLoaderManagers = mLastNonConfigurationInstances.loaders; } @@ -3509,6 +3509,22 @@ public class Activity extends ContextThemeWrapper } /** + * Cause this Activity to be recreated with a new instance. This results + * in essentially the same flow as when the Activity is created due to + * a configuration change -- the current instance will go through its + * lifecycle to {@link #onDestroy} and a new instance then created after it. + */ + public void recreate() { + if (mParent != null) { + throw new IllegalStateException("Can only be called on top-level activity"); + } + if (Looper.myLooper() != mMainThread.getLooper()) { + throw new IllegalStateException("Must be called from main thread"); + } + mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false); + } + + /** * Call this when your activity is done and should be closed. The * ActivityResult is propagated back to whoever launched you via * onActivityResult(). @@ -4262,6 +4278,8 @@ public class Activity extends ContextThemeWrapper final void performCreate(Bundle icicle) { onCreate(icicle); + mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c0714e3bba25..a8f08c2df10e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -224,6 +224,11 @@ public final class ActivityThread { boolean startsNotResumed; boolean isForward; + int pendingConfigChanges; + boolean onlyLocalRequest; + + View mPendingRemoveWindow; + WindowManager mPendingRemoveWindowManager; ActivityClientRecord() { parent = null; @@ -444,19 +449,8 @@ public final class ActivityThread { public final void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) { - ActivityClientRecord r = new ActivityClientRecord(); - - r.token = token; - r.pendingResults = pendingResults; - r.pendingIntents = pendingNewIntents; - r.startsNotResumed = notResumed; - r.createdConfig = config; - - synchronized (mPackages) { - mRelaunchingActivities.add(r); - } - - queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges); + requestRelaunchActivity(token, pendingResults, pendingNewIntents, + configChanges, notResumed, config, true); } public final void scheduleNewIntent(List<Intent> intents, IBinder token) { @@ -981,7 +975,7 @@ public final class ActivityThread { } break; case RELAUNCH_ACTIVITY: { ActivityClientRecord r = (ActivityClientRecord)msg.obj; - handleRelaunchActivity(r, msg.arg1); + handleRelaunchActivity(r); } break; case PAUSE_ACTIVITY: handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2); @@ -2183,6 +2177,19 @@ public final class ActivityThread { return r; } + final void cleanUpPendingRemoveWindows(ActivityClientRecord r) { + if (r.mPendingRemoveWindow != null) { + r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow); + IBinder wtoken = r.mPendingRemoveWindow.getWindowToken(); + if (wtoken != null) { + WindowManagerImpl.getDefault().closeAll(wtoken, + r.activity.getClass().getName(), "Activity"); + } + } + r.mPendingRemoveWindow = null; + r.mPendingRemoveWindowManager = null; + } + final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. @@ -2235,6 +2242,9 @@ public final class ActivityThread { r.hideForNow = true; } + // Get rid of anything left hanging around. + cleanUpPendingRemoveWindows(r); + // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible @@ -2267,11 +2277,14 @@ public final class ActivityThread { } } - r.nextIdle = mNewActivities; - mNewActivities = r; - if (localLOGV) Slog.v( - TAG, "Scheduling idle handler for " + r); - Looper.myQueue().addIdleHandler(new Idler()); + if (!r.onlyLocalRequest) { + r.nextIdle = mNewActivities; + mNewActivities = r; + if (localLOGV) Slog.v( + TAG, "Scheduling idle handler for " + r); + Looper.myQueue().addIdleHandler(new Idler()); + } + r.onlyLocalRequest = false; } else { // If an exception was thrown when trying to resume, then @@ -2728,6 +2741,7 @@ public final class ActivityThread { ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance); if (r != null) { + cleanUpPendingRemoveWindows(r); WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; if (v != null) { @@ -2736,16 +2750,31 @@ public final class ActivityThread { } IBinder wtoken = v.getWindowToken(); if (r.activity.mWindowAdded) { - wm.removeViewImmediate(v); + if (r.onlyLocalRequest) { + // Hold off on removing this until the new activity's + // window is being added. + r.mPendingRemoveWindow = v; + r.mPendingRemoveWindowManager = wm; + } else { + wm.removeViewImmediate(v); + } } - if (wtoken != null) { + if (wtoken != null && r.mPendingRemoveWindow == null) { WindowManagerImpl.getDefault().closeAll(wtoken, r.activity.getClass().getName(), "Activity"); } r.activity.mDecor = null; } - WindowManagerImpl.getDefault().closeAll(token, - r.activity.getClass().getName(), "Activity"); + if (r.mPendingRemoveWindow == null) { + // If we are delaying the removal of the activity window, then + // we can't clean up all windows here. Note that we can't do + // so later either, which means any windows that aren't closed + // by the app will leak. Well we try to warning them a lot + // about leaking windows, because that is a bug, so if they are + // using this recreate facility then they get to live with leaks. + WindowManagerImpl.getDefault().closeAll(token, + r.activity.getClass().getName(), "Activity"); + } // Mocked out contexts won't be participating in the normal // process lifecycle, but if we're running with a proper @@ -2766,17 +2795,70 @@ public final class ActivityThread { } } - private final void handleRelaunchActivity(ActivityClientRecord tmp, int configChanges) { + public final void requestRelaunchActivity(IBinder token, + List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + int configChanges, boolean notResumed, Configuration config, + boolean fromServer) { + ActivityClientRecord target = null; + + synchronized (mPackages) { + for (int i=0; i<mRelaunchingActivities.size(); i++) { + ActivityClientRecord r = mRelaunchingActivities.get(i); + if (r.token == token) { + target = r; + if (pendingResults != null) { + if (r.pendingResults != null) { + r.pendingResults.addAll(pendingResults); + } else { + r.pendingResults = pendingResults; + } + } + if (pendingNewIntents != null) { + if (r.pendingIntents != null) { + r.pendingIntents.addAll(pendingNewIntents); + } else { + r.pendingIntents = pendingNewIntents; + } + } + break; + } + } + + if (target == null) { + target = new ActivityClientRecord(); + target.token = token; + target.pendingResults = pendingResults; + target.pendingIntents = pendingNewIntents; + if (!fromServer) { + ActivityClientRecord existing = mActivities.get(token); + if (existing != null) { + target.startsNotResumed = existing.paused; + } + target.onlyLocalRequest = true; + } + mRelaunchingActivities.add(target); + queueOrSendMessage(H.RELAUNCH_ACTIVITY, target); + } + + if (fromServer) { + target.startsNotResumed = notResumed; + target.onlyLocalRequest = false; + } + if (config != null) { + target.createdConfig = config; + } + target.pendingConfigChanges |= configChanges; + } + } + + private final void handleRelaunchActivity(ActivityClientRecord tmp) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); Configuration changedConfig = null; + int configChanges = 0; - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity " - + tmp.token + " with configChanges=0x" - + Integer.toHexString(configChanges)); - // First: make sure we have the most recent configuration and most // recent version of the activity, or skip it if some previous call // had taken a more recent version. @@ -2788,6 +2870,7 @@ public final class ActivityThread { ActivityClientRecord r = mRelaunchingActivities.get(i); if (r.token == token) { tmp = r; + configChanges |= tmp.pendingConfigChanges; mRelaunchingActivities.remove(i); i--; N--; @@ -2799,6 +2882,10 @@ public final class ActivityThread { return; } + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity " + + tmp.token + " with configChanges=0x" + + Integer.toHexString(configChanges)); + if (mPendingConfiguration != null) { changedConfig = mPendingConfiguration; mPendingConfiguration = null; @@ -2834,6 +2921,7 @@ public final class ActivityThread { } r.activity.mConfigChangeFlags |= configChanges; + r.onlyLocalRequest = tmp.onlyLocalRequest; Intent currentIntent = r.activity.mIntent; Bundle savedState = null; diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index f3b2c81a72f4..fe4b900510fe 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1021,6 +1021,9 @@ public class Camera { private static final String KEY_ZOOM_SUPPORTED = "zoom-supported"; private static final String KEY_SMOOTH_ZOOM_SUPPORTED = "smooth-zoom-supported"; private static final String KEY_FOCUS_DISTANCES = "focus-distances"; + private static final String KEY_VIDEO_SIZE = "video-size"; + private static final String KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO = + "preferred-preview-size-for-video"; // Parameter key suffix for supported values. private static final String SUPPORTED_VALUES_SUFFIX = "-values"; @@ -1398,7 +1401,7 @@ public class Camera { /** * Returns the dimensions setting for preview pictures. * - * @return a Size object with the height and width setting + * @return a Size object with the width and height setting * for the preview picture */ public Size getPreviewSize() { @@ -1418,6 +1421,46 @@ public class Camera { } /** + * Gets the supported video frame sizes that can be used by + * MediaRecorder. + * + * If the returned list is not null, the returned list will contain at + * least one Size and one of the sizes in the returned list must be + * passed to MediaRecorder.setVideoSize() for camcorder application if + * camera is used as the video source. In this case, the size of the + * preview can be different from the resolution of the recorded video + * during video recording. + * + * @return a list of Size object if camera has separate preview and + * video output; otherwise, null is returned. + * @see #getPreferredPreviewSizeForVideo() + */ + public List<Size> getSupportedVideoSizes() { + String str = get(KEY_VIDEO_SIZE + SUPPORTED_VALUES_SUFFIX); + return splitSize(str); + } + + /** + * Returns the preferred or recommended preview size (width and height) + * in pixels for video recording. Camcorder applications should + * set the preview size to a value that is not larger than the + * preferred preview size. In other words, the product of the width + * and height of the preview size should not be larger than that of + * the preferred preview size. In addition, we recommend to choose a + * preview size that has the same aspect ratio as the resolution of + * video to be recorded. + * + * @return the preferred preview size (width and height) in pixels for + * video recording if getSupportedVideoSizes() does not return + * null; otherwise, null is returned. + * @see #getSupportedVideoSizes() + */ + public Size getPreferredPreviewSizeForVideo() { + String pair = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO); + return strToSize(pair); + } + + /** * Sets the dimensions for EXIF thumbnail in Jpeg picture. If * applications set both width and height to 0, EXIF will not contain * thumbnail. diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 683e60390031..b2b8c5af9df2 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -51,7 +51,14 @@ public final class Downloads { "android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED"; /** - * The permission to directly access the download manager's cache directory + * The permission to access the all the downloads in the manager. + */ + public static final String PERMISSION_ACCESS_ALL = + "android.permission.ACCESS_ALL_DOWNLOADS"; + + /** + * The permission to directly access the download manager's cache + * directory */ public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM"; diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java index 07e87d673875..6634f008ecc0 100644 --- a/core/java/android/view/DragEvent.java +++ b/core/java/android/view/DragEvent.java @@ -29,6 +29,7 @@ public class DragEvent implements Parcelable { float mX, mY; ClipDescription mClipDescription; ClipData mClipData; + Object mLocalState; boolean mDragResult; private DragEvent mNext; @@ -139,11 +140,11 @@ public static final int ACTION_DRAG_EXITED = 6; } static DragEvent obtain() { - return DragEvent.obtain(0, 0f, 0f, null, null, false); + return DragEvent.obtain(0, 0f, 0f, null, null, null, false); } /** @hide */ - public static DragEvent obtain(int action, float x, float y, + public static DragEvent obtain(int action, float x, float y, Object localState, ClipDescription description, ClipData data, boolean result) { final DragEvent ev; synchronized (gRecyclerLock) { @@ -167,7 +168,7 @@ public static final int ACTION_DRAG_EXITED = 6; /** @hide */ public static DragEvent obtain(DragEvent source) { - return obtain(source.mAction, source.mX, source.mY, + return obtain(source.mAction, source.mX, source.mY, source.mLocalState, source.mClipDescription, source.mClipData, source.mDragResult); } @@ -218,6 +219,15 @@ public static final int ACTION_DRAG_EXITED = 6; } /** + * Provides the local state object passed as the {@code myLocalState} parameter to + * View.startDrag(). The object will always be null here if the application receiving + * the DragEvent is not the one that started the drag. + */ + public Object getLocalState() { + return mLocalState; + } + + /** * Provides an indication of whether the drag operation concluded successfully. * This method is only available on ACTION_DRAG_ENDED events. * @return {@code true} if the drag operation ended with an accepted drop; {@code false} @@ -249,6 +259,7 @@ public static final int ACTION_DRAG_EXITED = 6; mClipData = null; mClipDescription = null; + mLocalState = null; synchronized (gRecyclerLock) { if (gRecyclerUsed < MAX_RECYCLED) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f135fccc5d60..ea237e3dbc52 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10081,9 +10081,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * onProvideThumbnailMetrics() and onDrawThumbnail() methods happen, then the drag * operation is handed over to the OS. * !!! TODO: real docs + * + * @param data !!! TODO + * @param thumbBuilder !!! TODO + * @param myWindowOnly When {@code true}, indicates that the drag operation should be + * restricted to the calling application. In this case only the calling application + * will see any DragEvents related to this drag operation. + * @param myLocalState An arbitrary object that will be passed as part of every DragEvent + * delivered to the calling application during the course of the current drag operation. + * This object is private to the application that called startDrag(), and is not + * visible to other applications. It provides a lightweight way for the application to + * propagate information from the initiator to the recipient of a drag within its own + * application; for example, to help disambiguate between 'copy' and 'move' semantics. + * @return {@code true} if the drag operation was initiated successfully; {@code false} if + * an error prevented the drag from taking place. */ public final boolean startDrag(ClipData data, DragThumbnailBuilder thumbBuilder, - boolean myWindowOnly) { + boolean myWindowOnly, Object myLocalState) { if (ViewDebug.DEBUG_DRAG) { Log.d(VIEW_LOG_TAG, "startDrag: data=" + data + " local=" + myWindowOnly); } @@ -10117,8 +10131,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility surface.unlockCanvasAndPost(canvas); } + final ViewRoot root = getViewRoot(); + + // Cache the local state object for delivery with DragEvents + root.setLocalDragState(myLocalState); + // repurpose 'thumbSize' for the last touch point - getViewRoot().getLastTouchPoint(thumbSize); + root.getLastTouchPoint(thumbSize); okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, token, (float) thumbSize.x, (float) thumbSize.y, diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 77083a902112..17d413a5eb44 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -228,6 +228,7 @@ public final class ViewRoot extends Handler implements ViewParent, /* Drag/drop */ ClipDescription mDragDescription; View mCurrentDragView; + Object mLocalDragState; final PointF mDragPoint = new PointF(); final PointF mLastTouchPoint = new PointF(); @@ -2680,6 +2681,10 @@ public final class ViewRoot extends Handler implements ViewParent, } /* drag/drop */ + void setLocalDragState(Object obj) { + mLocalDragState = obj; + } + private void handleDragEvent(DragEvent event) { // From the root, only drag start/end/location are dispatched. entered/exited // are determined and dispatched by the viewgroup hierarchy, who then report @@ -2738,7 +2743,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } - // Report the drop result if necessary + // Report the drop result when we're done if (what == DragEvent.ACTION_DROP) { try { Log.i(TAG, "Reporting drop result: " + result); @@ -2747,6 +2752,12 @@ public final class ViewRoot extends Handler implements ViewParent, Log.e(TAG, "Unable to report drop result"); } } + + // When the drag operation ends, release any local state object + // that may have been in use + if (what == DragEvent.ACTION_DRAG_ENDED) { + setLocalDragState(null); + } } } event.recycle(); @@ -3063,6 +3074,7 @@ public final class ViewRoot extends Handler implements ViewParent, } else { what = DISPATCH_DRAG_EVENT; } + event.mLocalState = mLocalDragState; // only present when this app called startDrag() Message msg = obtainMessage(what, event); sendMessage(msg); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 27a4b6da9563..be475ca31238 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -314,6 +314,17 @@ public class WebView extends AbsoluteLayout implements ViewTreeObserver.OnGlobalFocusChangeListener, ViewGroup.OnHierarchyChangeListener { + private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener { + public void onGlobalLayout() { + if (isShown()) { + setGLRectViewport(); + } + } + } + + // The listener to capture global layout change event. + private InnerGlobalLayoutListener mListener = null; + // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing // the screen all-the-time. Good for profiling our drawing code static private final boolean AUTO_REDRAW_HACK = false; @@ -4689,6 +4700,11 @@ public class WebView extends AbsoluteLayout protected void onAttachedToWindow() { super.onAttachedToWindow(); if (hasWindowFocus()) setActive(true); + final ViewTreeObserver treeObserver = getViewTreeObserver(); + if (treeObserver != null && mListener == null) { + mListener = new InnerGlobalLayoutListener(); + treeObserver.addOnGlobalLayoutListener(mListener); + } } @Override @@ -4696,6 +4712,13 @@ public class WebView extends AbsoluteLayout clearHelpers(); mZoomManager.dismissZoomPicker(); if (hasWindowFocus()) setActive(false); + + final ViewTreeObserver treeObserver = getViewTreeObserver(); + if (treeObserver != null && mListener != null) { + treeObserver.removeGlobalOnLayoutListener(mListener); + mListener = null; + } + super.onDetachedFromWindow(); } @@ -4836,13 +4859,19 @@ public class WebView extends AbsoluteLayout } void setGLRectViewport() { - View window = getRootView(); - int[] location = new int[2]; - getLocationInWindow(location); - mGLRectViewport = new Rect(location[0], window.getHeight() - - (location[1] + getHeight()), - location[0] + getWidth(), - window.getHeight() - location[1]); + // Use the getGlobalVisibleRect() to get the intersection among the parents + Rect webViewRect = new Rect(); + boolean visible = getGlobalVisibleRect(webViewRect); + + // Then need to invert the Y axis, just for GL + View rootView = getRootView(); + int rootViewHeight = rootView.getHeight(); + int savedWebViewBottom = webViewRect.bottom; + webViewRect.bottom = rootViewHeight - webViewRect.top; + webViewRect.top = rootViewHeight - savedWebViewBottom; + + // Store the viewport + mGLRectViewport = webViewRect; } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 1d3e4f4d9518..43434d369470 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -7818,7 +7818,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int end = getSelectionEnd(); CharSequence selectedText = mTransformed.subSequence(start, end); ClipData data = ClipData.newPlainText(null, null, selectedText); - startDrag(data, getTextThumbnailBuilder(selectedText), false); + startDrag(data, getTextThumbnailBuilder(selectedText), false, null); mDragSourcePositions = packRangeInLong(start, end); stopSelectionActionMode(); } else { @@ -8744,7 +8744,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Double tap detection long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; - if (duration <= ViewConfiguration.getDoubleTapTimeout()) { + if (duration <= ViewConfiguration.getDoubleTapTimeout() && + isPositionOnText(x, y)) { final int deltaX = x - mPreviousTapPositionX; final int deltaY = y - mPreviousTapPositionY; final int distanceSquared = deltaX * deltaX + deltaY * deltaY; diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java index c930c57c359f..bfef27571df8 100644 --- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java +++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java @@ -27,6 +27,7 @@ import java.io.PrintStream; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.ThreadFactory; import android.content.pm.PackageInfo; import android.util.Log; @@ -43,27 +44,40 @@ public class SamplingProfilerIntegration { private static final boolean enabled; private static final Executor snapshotWriter; - private static final int samplingProfilerHz; - - /** Whether or not we've created the snapshots dir. */ - private static boolean dirMade = false; + private static final int samplingProfilerMilliseconds; + private static final int samplingProfilerDepth; /** Whether or not a snapshot is being persisted. */ private static final AtomicBoolean pending = new AtomicBoolean(false); static { - samplingProfilerHz = SystemProperties.getInt("persist.sys.profiler_hz", 0); - // Disabling this for now, as it crashes when enabled server-side. So adding - // a new property ("REALLY") for those wanting to test and fix it. - boolean really = SystemProperties.getInt("persist.sys.profiler_hz_REALLY", 0) > 0; - if (samplingProfilerHz > 0 && really) { - snapshotWriter = Executors.newSingleThreadExecutor(); - enabled = true; - Log.i(TAG, "Profiler is enabled. Sampling Profiler Hz: " + samplingProfilerHz); + samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0); + samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4); + if (samplingProfilerMilliseconds > 0) { + File dir = new File(SNAPSHOT_DIR); + dir.mkdirs(); + // the directory needs to be writable to anybody to allow file writing + dir.setWritable(true, false); + // the directory needs to be executable to anybody to allow file creation + dir.setExecutable(true, false); + if (dir.isDirectory()) { + snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() { + public Thread newThread(Runnable r) { + return new Thread(r, TAG); + } + }); + enabled = true; + Log.i(TAG, "Profiling enabled. Sampling interval ms: " + + samplingProfilerMilliseconds); + } else { + snapshotWriter = null; + enabled = true; + Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR); + } } else { snapshotWriter = null; enabled = false; - Log.i(TAG, "Profiler is disabled."); + Log.i(TAG, "Profiling disabled."); } } @@ -85,8 +99,8 @@ public class SamplingProfilerIntegration { } ThreadGroup group = Thread.currentThread().getThreadGroup(); SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group); - INSTANCE = new SamplingProfiler(4, threadSet); // TODO parameter for depth - INSTANCE.start(1000/samplingProfilerHz); + INSTANCE = new SamplingProfiler(samplingProfilerDepth, threadSet); + INSTANCE.start(samplingProfilerMilliseconds); } /** @@ -106,25 +120,8 @@ public class SamplingProfilerIntegration { if (pending.compareAndSet(false, true)) { snapshotWriter.execute(new Runnable() { public void run() { - if (!dirMade) { - File dir = new File(SNAPSHOT_DIR); - dir.mkdirs(); - // the directory needs to be writable to anybody - dir.setWritable(true, false); - // the directory needs to be executable to anybody - // don't know why yet, but mode 723 would work, while - // mode 722 throws FileNotFoundExecption at line 151 - dir.setExecutable(true, false); - if (new File(SNAPSHOT_DIR).isDirectory()) { - dirMade = true; - } else { - Log.w(TAG, "Creation of " + SNAPSHOT_DIR + " failed."); - pending.set(false); - return; - } - } try { - writeSnapshot(SNAPSHOT_DIR, processName, packageInfo); + writeSnapshotFile(processName, packageInfo); } finally { pending.set(false); } @@ -140,7 +137,7 @@ public class SamplingProfilerIntegration { if (!enabled) { return; } - writeSnapshot("zygote", null); + writeSnapshotFile("zygote", null); INSTANCE.shutdown(); INSTANCE = null; } @@ -148,7 +145,7 @@ public class SamplingProfilerIntegration { /** * pass in PackageInfo to retrieve various values for snapshot header */ - private static void writeSnapshot(String dir, String processName, PackageInfo packageInfo) { + private static void writeSnapshotFile(String processName, PackageInfo packageInfo) { if (!enabled) { return; } @@ -161,7 +158,7 @@ public class SamplingProfilerIntegration { */ long start = System.currentTimeMillis(); String name = processName.replaceAll(":", "."); - String path = dir + "/" + name + "-" +System.currentTimeMillis() + ".snapshot"; + String path = SNAPSHOT_DIR + "/" + name + "-" +System.currentTimeMillis() + ".snapshot"; PrintStream out = null; try { out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path))); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ddc63ddb8681..a2666e24f778 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1013,7 +1013,7 @@ <permission android:name="android.permission.SHUTDOWN" android:label="@string/permlab_shutdown" android:description="@string/permdesc_shutdown" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Allows an application to tell the activity manager to temporarily stop application switches, putting it into a special mode that diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd index cef057e30f7d..74167640187e 100644 --- a/docs/html/resources/dashboard/platform-versions.jd +++ b/docs/html/resources/dashboard/platform-versions.jd @@ -52,9 +52,8 @@ Android Market within a 14-day period ending on the data collection date noted b <div class="dashboard-panel"> <img alt="" height="250" width="460" -src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:7.9,15.0,0.1,40.8,36.2&chl= -Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b, -6fad0c" /> +src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:6.3,10.6,0.1,39.6,43.4&chl +=Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" /> <table> <tr> @@ -62,13 +61,13 @@ Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b, <th>API Level</th> <th>Distribution</th> </tr> -<tr><td>Android 1.5</td><td>3</td><td>7.9%</td></tr> -<tr><td>Android 1.6</td><td>4</td><td>15.0%</td></tr> -<tr><td>Android 2.1</td><td>7</td><td>40.8%</td></tr> -<tr><td>Android 2.2</td><td>8</td><td>36.2%</td></tr> +<tr><td>Android 1.5</td><td>3</td><td>6.3%</td></tr> +<tr><td>Android 1.6</td><td>4</td><td>10.6%</td></tr> +<tr><td>Android 2.1</td><td>7</td><td>39.6%</td></tr> +<tr><td>Android 2.2</td><td>8</td><td>43.4%</td></tr> </table> -<p><em>Data collected during two weeks ending on November 1, 2010</em></p> +<p><em>Data collected during two weeks ending on December 1, 2010</em></p> <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p> </div><!-- end dashboard-panel --> @@ -97,17 +96,16 @@ Android Market within a 14-day period ending on the date indicated on the x-axis <img alt="" height="250" width="660" style="padding:5px;background:#fff" src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100& -chxl=0%3A%7C2010/05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/ -01%7C10/15%7C2010/11/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25 -%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.9,99.3,100.3,100.8,99.7,99. -8,99.8,99.7,99.8,99.9,99.9,99.9,99.9|61.6,63.1,72.7,76.1,78.4,80.9,84.3,86.5,87.9,89.2,90.2,91.1,92. -0|32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77.0|0.0,0.0,0.8,1.2,1.8,3.3,4.3,11.3, -27.8,32.1,33.4,34.5,36.2&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6, -5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202. -2,131d02,3,7,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.1| -Android%202.2&chco=add274,94d134,73ad18,507d08" /> - -<p><em>Last historical dataset collected during two weeks ending on November 1, 2010</em></p> +chxl=0:|2010/06/01|06/15|07/01|07/15|08/01|08/15|09/01|09/15|10/01|10/15|11/01|11/15|2010/12/01|1:|0 +%25|25%25|50%25|75%25|100%25|2:|0%25|25%25|50%25|75%25|100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12& +chxtc=0,5&chd=t:100.3,100.8,99.7,99.8,99.8,99.7,99.8,99.9,99.9,99.9,99.9,99.9,99.9|72.7,76.1,78.4,80 +.9,84.3,86.5,87.9,89.2,90.2,91.1,92.0,92.7,93.6|45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77 +.0,79.0,83.0|0.8,1.2,1.8,3.3,4.3,11.3,27.8,32.1,33.4,34.5,36.2,38.3,43.4&chm=tAndroid%201.5,7caa36,0 +,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b +,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202.2,131d02,3,5,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl= +Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=add274,94d134,73ad18,507d08" /> + +<p><em>Last historical dataset collected during two weeks ending on December 1, 2010</em></p> </div><!-- end dashboard-panel --> diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml index 078daa76d501..b0597c4e6ee4 100755 --- a/packages/DefaultContainerService/AndroidManifest.xml +++ b/packages/DefaultContainerService/AndroidManifest.xml @@ -1,6 +1,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.defcontainer"> <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/> + <uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS"/> <uses-permission android:name="android.permission.ASEC_ACCESS"/> <uses-permission android:name="android.permission.ASEC_CREATE"/> <uses-permission android:name="android.permission.ASEC_DESTROY"/> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java index d1e61a9052cf..4d0835fae12c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java @@ -169,7 +169,7 @@ public class ShirtPocket extends FrameLayout { thumb = new DragThumbnailBuilder(mWindow.findViewById(R.id.preview)); } - v.startDrag(clip, thumb, false); + v.startDrag(clip, thumb, false, null); // TODO: only discard the clipping if it was accepted stash(null); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 27ec1af5eda0..d7a6a0e4f400 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -619,7 +619,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mDragInProgress && newWin.isPotentialDragTarget()) { DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, touchX - newWin.mFrame.left, touchY - newWin.mFrame.top, - desc, null, false); + null, desc, null, false); try { newWin.mClient.dispatchDragEvent(event); // track each window that we've notified that the drag is starting @@ -659,7 +659,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.d(TAG, "broadcasting DRAG_ENDED"); } DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, - 0, 0, null, null, mDragResult); + 0, 0, null, null, null, mDragResult); for (WindowState ws: mNotifiedWindows) { try { ws.mClient.dispatchDragEvent(evt); @@ -711,7 +711,7 @@ public class WindowManagerService extends IWindowManager.Stub // force DRAG_EXITED_EVENT if appropriate DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED, x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top, - null, null, false); + null, null, null, false); mTargetWindow.mClient.dispatchDragEvent(evt); if (myPid != mTargetWindow.mSession.mPid) { evt.recycle(); @@ -723,7 +723,7 @@ public class WindowManagerService extends IWindowManager.Stub } DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION, x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, null, false); + null, null, null, false); touchedWin.mClient.dispatchDragEvent(evt); if (myPid != touchedWin.mSession.mPid) { evt.recycle(); @@ -754,7 +754,7 @@ public class WindowManagerService extends IWindowManager.Stub final IBinder token = touchedWin.mClient.asBinder(); DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, mData, false); + null, null, mData, false); try { touchedWin.mClient.dispatchDragEvent(evt); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 2de1cbb0267b..ca87b4e868b8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -23,6 +23,7 @@ import com.android.layoutlib.api.IXmlPullParser; import com.android.layoutlib.api.LayoutBridge; import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; +import com.android.layoutlib.api.SceneResult.SceneStatus; import com.android.layoutlib.bridge.android.BridgeAssetManager; import com.android.layoutlib.bridge.impl.FontLoader; import com.android.layoutlib.bridge.impl.LayoutSceneImpl; @@ -308,8 +309,13 @@ public final class Bridge extends LayoutBridge { return new BridgeLayoutScene(scene, lastResult); } catch (Throwable t) { - t.printStackTrace(); - return new BridgeLayoutScene(null, new SceneResult("error!", t)); + // get the real cause of the exception. + Throwable t2 = t; + while (t2.getCause() != null) { + t2 = t.getCause(); + } + return new BridgeLayoutScene(null, + new SceneResult(SceneStatus.ERROR_UNKNOWN, t2.getMessage(), t2)); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java index 97bf857447fa..f80721436aac 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java @@ -16,12 +16,16 @@ package com.android.layoutlib.bridge; +import com.android.layoutlib.api.IXmlPullParser; import com.android.layoutlib.api.LayoutScene; import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; import com.android.layoutlib.api.ViewInfo; import com.android.layoutlib.bridge.impl.LayoutSceneImpl; +import android.view.View; +import android.view.ViewGroup; + import java.awt.image.BufferedImage; import java.util.Map; @@ -92,13 +96,86 @@ public class BridgeLayoutScene extends LayoutScene { } @Override - public void dispose() { - // TODO Auto-generated method stub + public SceneResult insertChild(Object parentView, IXmlPullParser childXml, Object beforeSibling, + IAnimationListener listener) { + if (parentView instanceof ViewGroup == false) { + throw new IllegalArgumentException("parentView is not a ViewGroup"); + } + if (beforeSibling != null && beforeSibling instanceof View == false) { + throw new IllegalArgumentException("beforeSibling is not a View"); + } + + try { + mScene.prepareThread(); + mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); + if (mLastResult == SceneResult.SUCCESS) { + mLastResult = mScene.insertChild((ViewGroup) parentView, childXml, + (View) beforeSibling, listener); + } + } finally { + mScene.release(); + mScene.cleanupThread(); + } + + return mLastResult; + } + + + @Override + public SceneResult moveChild(Object parentView, Object childView, Object beforeSibling, + IAnimationListener listener) { + if (parentView instanceof ViewGroup == false) { + throw new IllegalArgumentException("parentView is not a ViewGroup"); + } + if (childView instanceof View == false) { + throw new IllegalArgumentException("childView is not a View"); + } + if (beforeSibling != null && beforeSibling instanceof View == false) { + throw new IllegalArgumentException("beforeSibling is not a View"); + } + + try { + mScene.prepareThread(); + mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); + if (mLastResult == SceneResult.SUCCESS) { + mLastResult = mScene.moveChild((ViewGroup) parentView, (View) childView, + (View) beforeSibling, listener); + } + } finally { + mScene.release(); + mScene.cleanupThread(); + } + + return mLastResult; + } + @Override + public SceneResult removeChild(Object childView, IAnimationListener listener) { + if (childView instanceof View == false) { + throw new IllegalArgumentException("childView is not a View"); + } + + try { + mScene.prepareThread(); + mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); + if (mLastResult == SceneResult.SUCCESS) { + mLastResult = mScene.removeChild((View) childView, listener); + } + } finally { + mScene.release(); + mScene.cleanupThread(); + } + + return mLastResult; + } + + @Override + public void dispose() { } /*package*/ BridgeLayoutScene(LayoutSceneImpl scene, SceneResult lastResult) { mScene = scene; + mScene.setScene(this); mLastResult = lastResult; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java index c20bdfd6c906..d5766ab2fa74 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java @@ -105,7 +105,7 @@ public class AnimationThread extends Thread { try { bundle.mTarget.handleMessage(bundle.mMessage); if (mScene.render() == SceneResult.SUCCESS) { - mListener.onNewFrame(mScene.getImage()); + mListener.onNewFrame(mScene.getScene()); } } finally { mScene.release(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java index 8a0776724f1b..05d207cc0077 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java @@ -24,13 +24,16 @@ import com.android.internal.util.XmlUtils; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; +import com.android.layoutlib.api.IXmlPullParser; import com.android.layoutlib.api.LayoutBridge; +import com.android.layoutlib.api.LayoutScene; import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; import com.android.layoutlib.api.ViewInfo; import com.android.layoutlib.api.IDensityBasedResourceValue.Density; import com.android.layoutlib.api.LayoutScene.IAnimationListener; import com.android.layoutlib.api.SceneParams.RenderingMode; +import com.android.layoutlib.api.SceneResult.SceneStatus; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; @@ -53,6 +56,7 @@ import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.View.AttachInfo; import android.view.View.MeasureSpec; import android.widget.FrameLayout; @@ -92,6 +96,7 @@ public class LayoutSceneImpl { private final SceneParams mParams; // scene state + private LayoutScene mScene; private BridgeContext mContext; private BridgeXmlBlockParser mBlockParser; private BridgeInflater mInflater; @@ -362,7 +367,7 @@ public class LayoutSceneImpl { return SceneResult.SUCCESS; } catch (PostInflateException e) { - return new SceneResult("Error during post inflation process:\n" + e.getMessage()); + return new SceneResult(SceneStatus.ERROR_INFLATION, e.getMessage(), e); } catch (Throwable e) { // get the real cause of the exception. Throwable t = e; @@ -373,7 +378,7 @@ public class LayoutSceneImpl { // log it mParams.getLogger().error(t); - return new SceneResult("Unknown error during inflation.", t); + return new SceneResult(SceneStatus.ERROR_INFLATION, t.getMessage(), t); } } @@ -391,7 +396,7 @@ public class LayoutSceneImpl { try { long current = System.currentTimeMillis(); if (mViewRoot == null) { - return new SceneResult("Layout has not been inflated!"); + return new SceneResult(SceneStatus.ERROR_NOT_INFLATED); } // measure the views int w_spec, h_spec; @@ -487,7 +492,7 @@ public class LayoutSceneImpl { // log it mParams.getLogger().error(t); - return new SceneResult("Unknown error during rendering.", t); + return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t); } } @@ -530,12 +535,74 @@ public class LayoutSceneImpl { return SceneResult.SUCCESS; } } catch (Exception e) { - e.printStackTrace(); - return new SceneResult("", e); + // get the real cause of the exception. + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + + return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t); } } - return new SceneResult("Failed to find animation"); + return new SceneResult(SceneStatus.ERROR_ANIM_NOT_FOUND); + } + + public SceneResult insertChild(ViewGroup parentView, IXmlPullParser childXml, + View beforeSibling, IAnimationListener listener) { + checkLock(); + + int index = parentView.indexOfChild(beforeSibling); + if (beforeSibling != null && index == -1) { + throw new IllegalArgumentException("beforeSibling not in parentView"); + } + + // create a block parser for the XML + BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(childXml, mContext, + false /* platformResourceFlag */); + + // inflate the child without adding it to the root since we want to control where it'll + // get added. We do pass the parentView however to ensure that the layoutParams will + // be created correctly. + View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/); + + // add it to the parentView in the correct location + parentView.addView(child, index); + + return render(); + } + + public SceneResult moveChild(ViewGroup parentView, View childView, View beforeSibling, + IAnimationListener listener) { + checkLock(); + + int index = parentView.indexOfChild(beforeSibling); + if (beforeSibling != null && index == -1) { + throw new IllegalArgumentException("beforeSibling not in parentView"); + } + + ViewParent parent = childView.getParent(); + if (parent instanceof ViewGroup) { + ViewGroup parentGroup = (ViewGroup) parent; + parentGroup.removeView(childView); + } + + // add it to the parentView in the correct location + parentView.addView(childView, index); + + return render(); + } + + public SceneResult removeChild(View childView, IAnimationListener listener) { + checkLock(); + + ViewParent parent = childView.getParent(); + if (parent instanceof ViewGroup) { + ViewGroup parentGroup = (ViewGroup) parent; + parentGroup.removeView(childView); + } + + return render(); } /** @@ -915,4 +982,12 @@ public class LayoutSceneImpl { public Map<String, String> getDefaultViewPropertyValues(Object viewObject) { return mContext.getDefaultPropMap(viewObject); } + + public void setScene(LayoutScene scene) { + mScene = scene; + } + + public LayoutScene getScene() { + return mScene; + } } |