diff options
69 files changed, 2086 insertions, 715 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 39685ae87cf4..56edc72cb79c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -30341,6 +30341,7 @@ package android.telecom { method public android.telecom.PhoneAccountHandle getConnectionManager(); method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String); method public android.content.ComponentName getDefaultPhoneApp(); + method public java.lang.String getLine1Number(android.telecom.PhoneAccountHandle); method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle); method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage(); method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 38cd1265437e..9568897dca61 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2377,8 +2377,10 @@ public class Activity extends ContextThemeWrapper if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) { return false; } else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) { - if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, - keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) { + Window w = getWindow(); + if (w.hasFeature(Window.FEATURE_OPTIONS_PANEL) && + w.performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, keyCode, event, + Menu.FLAG_ALWAYS_PERFORM_CLOSE)) { return true; } return false; @@ -2943,7 +2945,8 @@ public class Activity extends ContextThemeWrapper * time it needs to be displayed. */ public void invalidateOptionsMenu() { - if (mActionBar == null || !mActionBar.invalidateOptionsMenu()) { + if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && + (mActionBar == null || !mActionBar.invalidateOptionsMenu())) { mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); } } @@ -3155,7 +3158,8 @@ public class Activity extends ContextThemeWrapper * open, this method does nothing. */ public void openOptionsMenu() { - if (mActionBar == null || !mActionBar.openOptionsMenu()) { + if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && + (mActionBar == null || !mActionBar.openOptionsMenu())) { mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); } } @@ -3165,7 +3169,9 @@ public class Activity extends ContextThemeWrapper * closed, this method does nothing. */ public void closeOptionsMenu() { - mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); + if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) { + mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); + } } /** @@ -3224,7 +3230,9 @@ public class Activity extends ContextThemeWrapper * Programmatically closes the most recently opened context menu, if showing. */ public void closeContextMenu() { - mWindow.closePanel(Window.FEATURE_CONTEXT_MENU); + if (mWindow.hasFeature(Window.FEATURE_CONTEXT_MENU)) { + mWindow.closePanel(Window.FEATURE_CONTEXT_MENU); + } } /** diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index d0d9d7153eaf..e3b27b51f69a 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -799,6 +799,15 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { mIsStartingTransition = false; } + /** + * Cancels any pending transitions and returns true if there is a transition is in + * the middle of starting. + */ + protected boolean cancelPendingTransitions() { + mPendingTransition = null; + return mIsStartingTransition; + } + protected void moveSharedElementsToOverlay() { if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) { return; diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index 555d20bba99a..a2bfa4e4fba9 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -22,6 +22,7 @@ import android.util.ArrayMap; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.Window; import java.lang.ref.WeakReference; @@ -252,7 +253,7 @@ class ActivityTransitionState { } } - public boolean startExitBackTransition(Activity activity) { + public boolean startExitBackTransition(final Activity activity) { if (mEnteringNames == null) { return false; } else { @@ -260,10 +261,11 @@ class ActivityTransitionState { mHasExited = true; Transition enterViewsTransition = null; ViewGroup decor = null; + boolean delayExitBack = false; if (mEnterTransitionCoordinator != null) { enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition(); decor = mEnterTransitionCoordinator.getDecor(); - mEnterTransitionCoordinator.cancelEnter(); + delayExitBack = mEnterTransitionCoordinator.cancelEnter(); mEnterTransitionCoordinator = null; if (enterViewsTransition != null && decor != null) { enterViewsTransition.pause(decor); @@ -275,7 +277,23 @@ class ActivityTransitionState { if (enterViewsTransition != null && decor != null) { enterViewsTransition.resume(decor); } - mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData); + if (delayExitBack && decor != null) { + final ViewGroup finalDecor = decor; + decor.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + finalDecor.getViewTreeObserver().removeOnPreDrawListener(this); + if (mReturnExitCoordinator != null) { + mReturnExitCoordinator.startExit(activity.mResultCode, + activity.mResultData); + } + return true; + } + }); + } else { + mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData); + } } return true; } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 12d451380c3d..067073a1f71c 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -910,21 +910,27 @@ public class Dialog implements DialogInterface, Window.Callback, * @see Activity#openOptionsMenu() */ public void openOptionsMenu() { - mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); + if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) { + mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); + } } - + /** * @see Activity#closeOptionsMenu() */ public void closeOptionsMenu() { - mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); + if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) { + mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); + } } /** * @see Activity#invalidateOptionsMenu() */ public void invalidateOptionsMenu() { - mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); + if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) { + mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); + } } /** diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index ecf19c783e83..c053c830adfc 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -18,7 +18,6 @@ package android.app; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; -import android.graphics.Matrix; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.ResultReceiver; @@ -565,7 +564,12 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { clearState(); } - public void cancelEnter() { + /** + * Cancels the enter transition. + * @return True if the enter transition is still pending capturing the target state. If so, + * any transition started on the decor will do nothing. + */ + public boolean cancelEnter() { setGhostVisibility(View.INVISIBLE); mHasStopped = true; mIsCanceled = true; @@ -576,6 +580,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } mActivity = null; clearState(); + return super.cancelPendingTransitions(); } private void makeOpaque() { diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 602a68c81a69..8ebcacdcd988 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -19,6 +19,8 @@ package android.util; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.net.SntpClient; import android.os.SystemClock; import android.provider.Settings; @@ -34,10 +36,13 @@ public class NtpTrustedTime implements TrustedTime { private static final boolean LOGD = false; private static NtpTrustedTime sSingleton; + private static Context sContext; private final String mServer; private final long mTimeout; + private ConnectivityManager mCM; + private boolean mHasCache; private long mCachedNtpTime; private long mCachedNtpElapsedRealtime; @@ -66,6 +71,7 @@ public class NtpTrustedTime implements TrustedTime { final String server = secureServer != null ? secureServer : defaultServer; sSingleton = new NtpTrustedTime(server, timeout); + sContext = context; } return sSingleton; @@ -78,6 +84,20 @@ public class NtpTrustedTime implements TrustedTime { return false; } + // We can't do this at initialization time: ConnectivityService might not be running yet. + synchronized (this) { + if (mCM == null) { + mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE); + } + } + + final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo(); + if (ni == null || !ni.isConnected()) { + if (LOGD) Log.d(TAG, "forceRefresh: no connectivity"); + return false; + } + + if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); final SntpClient client = new SntpClient(); if (client.requestTime(mServer, (int) mTimeout)) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6a36c269df9a..ed75de39234f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5932,23 +5932,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return true; } - /** - * Adds the clickable rectangles withing the bounds of this view. They - * may overlap. This method is intended for use only by the accessibility - * layer. - * - * @param outRects List to which to add clickable areas. - * - * @hide - */ - public void addClickableRectsForAccessibility(List<RectF> outRects) { - if (isClickable() || isLongClickable()) { - RectF bounds = new RectF(); - bounds.set(0, 0, getWidth(), getHeight()); - outRects.add(bounds); - } - } - static void offsetRects(List<RectF> rects, float offsetX, float offsetY) { final int rectCount = rects.size(); for (int i = 0; i < rectCount; i++) { @@ -16650,6 +16633,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (changed) { requestLayout(); + invalidateOutline(); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6678ff223b14..0b1a2d4da926 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -829,8 +829,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Clip the bounds by our bounds. bounds.left = Math.max(bounds.left, 0); bounds.top = Math.max(bounds.top, 0); - bounds.right = Math.min(bounds.right, mRight); - bounds.bottom = Math.min(bounds.bottom, mBottom); + bounds.right = Math.min(bounds.right, getWidth()); + bounds.bottom = Math.min(bounds.bottom, getHeight()); Iterator<View> iterator = obtainOrderedChildIterator(); while (iterator.hasNext()) { @@ -855,27 +855,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Compute the intersection between the child and the sibling. if (siblingBounds.intersect(bounds)) { - List<RectF> clickableRects = new ArrayList<>(); - sibling.addClickableRectsForAccessibility(clickableRects); - - final int clickableRectCount = clickableRects.size(); - for (int j = 0; j < clickableRectCount; j++) { - RectF clickableRect = clickableRects.get(j); - - // Translate the clickable rect to our coordinates. - offsetChildRectToMyCoords(clickableRect, sibling); - - // Compute the intersection between the child and the clickable rects. - if (clickableRect.intersect(bounds)) { - // If a clickable rect completely covers the child, done. - if (clickableRect.equals(bounds)) { - releaseOrderedChildIterator(); - return false; - } - // Keep track of the intersection rectangle. - intersections.add(clickableRect); - } - } + // Conservatively we consider an overlapping sibling to be + // interactive and ignore it. This is not ideal as if the + // sibling completely covers the view despite handling no + // touch events we will not be able to click on the view. + intersections.add(siblingBounds); } } @@ -890,54 +874,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return true; } - /** - * @hide - */ - @Override - public void addClickableRectsForAccessibility(List<RectF> outRects) { - int sizeBefore = outRects.size(); - - super.addClickableRectsForAccessibility(outRects); - - // If we added ourselves, then no need to visit children. - if (outRects.size() > sizeBefore) { - return; - } - - Iterator<View> iterator = obtainOrderedChildIterator(); - while (iterator.hasNext()) { - View child = iterator.next(); - - // Cannot click on an invisible view. - if (!isVisible(child)) { - continue; - } - - sizeBefore = outRects.size(); - - // Add clickable rects in the child bounds. - child.addClickableRectsForAccessibility(outRects); - - // Offset the clickable rects for out children to our coordinates. - final int sizeAfter = outRects.size(); - for (int j = sizeBefore; j < sizeAfter; j++) { - RectF rect = outRects.get(j); - - // Translate the clickable rect to our coordinates. - offsetChildRectToMyCoords(rect, child); - - // If a clickable rect fills the parent, done. - if ((int) rect.left == 0 && (int) rect.top == 0 - && (int) rect.right == mRight && (int) rect.bottom == mBottom) { - releaseOrderedChildIterator(); - return; - } - } - } - - releaseOrderedChildIterator(); - } - private void offsetChildRectToMyCoords(RectF rect, View child) { if (!child.hasIdentityMatrix()) { child.getMatrix().mapRect(rect); diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 371b4804a1de..1b93b9705135 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -762,18 +762,6 @@ public class HorizontalScrollView extends FrameLayout { awakenScrollBars(); } - /** - * @hide - */ - @Override - public void addClickableRectsForAccessibility(List<RectF> outRects) { - // This class always consumes touch events, therefore if it - // covers a view we do not want to send a click over it. - RectF bounds = new RectF(); - bounds.set(0, 0, getWidth(), getHeight()); - outRects.add(bounds); - } - @Override public boolean performAccessibilityAction(int action, Bundle arguments) { if (super.performAccessibilityAction(action, arguments)) { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index fe8b08bea78b..d85bbb9e1571 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -1648,19 +1648,32 @@ public class ListPopupWindow { private void setPressedItem(View child, int position, float x, float y) { mDrawsInPressedState = true; - // Ordering is essential. First update the pressed state and layout - // the children. This will ensure the selector actually gets drawn. - setPressed(true); - layoutChildren(); + // Ordering is essential. First, update the container's pressed state. + drawableHotspotChanged(x, y); + if (!isPressed()) { + setPressed(true); + } + + // Next, run layout if we need to stabilize child positions. + if (mDataChanged) { + layoutChildren(); + } // Manage the pressed view based on motion position. This allows us to // play nicely with actual touch and scroll events. final View motionView = getChildAt(mMotionPosition - mFirstPosition); - if (motionView != null) { + if (motionView != null && motionView != child && motionView.isPressed()) { motionView.setPressed(false); } mMotionPosition = position; - child.setPressed(true); + + // Offset for child coordinates. + final float childX = x - child.getLeft(); + final float childY = y - child.getTop(); + child.drawableHotspotChanged(childX, childY); + if (!child.isPressed()) { + child.setPressed(true); + } // Ensure that keyboard focus starts from the last touched position. setSelectedPositionInt(position); diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java index 7b64cf51835f..11fda2cec2c5 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -1381,11 +1381,19 @@ public class RadialTimePickerView extends View implements View.OnTouchListener { @Override protected int getVirtualViewAt(float x, float y) { final int id; + + // Calling getDegreesXY() has side-effects, so we need to cache the + // current inner circle value and restore after the call. + final boolean wasOnInnerCircle = mIsOnInnerCircle; final int degrees = getDegreesFromXY(x, y); + final boolean isOnInnerCircle = mIsOnInnerCircle; + mIsOnInnerCircle = wasOnInnerCircle; + if (degrees != -1) { final int snapDegrees = snapOnly30s(degrees, 0) % 360; if (mShowHours) { - final int hour = getHourForDegrees(snapDegrees, mIsOnInnerCircle); + final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle); + final int hour = mIs24HourMode ? hour24 : hour24To12(hour24); id = makeId(TYPE_HOUR, hour); } else { final int current = getCurrentMinute(); @@ -1514,6 +1522,16 @@ public class RadialTimePickerView extends View implements View.OnTouchListener { return hour24; } + private int hour24To12(int hour24) { + if (hour24 == 0) { + return 12; + } else if (hour24 > 12) { + return hour24 - 12; + } else { + return hour24; + } + } + private void getBoundsForVirtualView(int virtualViewId, Rect bounds) { final float radius; final int type = getTypeFromId(virtualViewId); diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index f90d64a24804..c5325c4d3b53 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.app.ActionBar; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index 847a47d72040..7937a95b88c8 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -23,6 +23,7 @@ import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Outline; import android.graphics.PixelFormat; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.ActionMode; @@ -31,6 +32,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import java.util.List; + /** * This class acts as a container for the action bar view and action mode context views. * It applies special styles as needed to help handle animated transitions between them. diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp index 1dafa1b5397d..d99ddeb9bfc1 100644 --- a/core/jni/android/graphics/NinePatchPeeker.cpp +++ b/core/jni/android/graphics/NinePatchPeeker.cpp @@ -24,7 +24,9 @@ bool NinePatchPeeker::peek(const char tag[], const void* data, size_t length) { if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) { Res_png_9patch* patch = (Res_png_9patch*) data; size_t patchSize = patch->serializedSize(); - assert(length == patchSize); + if (length != patchSize) { + return false; + } // You have to copy the data because it is owned by the png reader Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize); memcpy(patchNew, patch, patchSize); diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml index 3a665070f2ce..89174312aaaf 100644 --- a/core/res/res/drawable/ic_corp_badge.xml +++ b/core/res/res/drawable/ic_corp_badge.xml @@ -14,23 +14,20 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="19.0dp" - android:height="19.0dp" - android:viewportWidth="19.0" - android:viewportHeight="19.0"> + android:width="20.0dp" + android:height="20.0dp" + android:viewportWidth="20.0" + android:viewportHeight="20.0"> <path - android:pathData="M9.5,9.5m-9.5,0a9.5,9.5 0,1 1,19 0a9.5,9.5 0,1 1,-19 0" + android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0" android:fillColor="#FF5722"/> <path - android:pathData="M13.741,6.286l-1.53,0L12.211,5.247l-1.039,-1.039L8.025,4.208L6.986,5.247l0,1.039L5.429,6.286c-0.574,0 -1.034,0.465 -1.034,1.039L4.39,13.039c0.0,0.574 0.465,1.039 1.039,1.039l8.312,0c0.574,0 1.039,-0.465 1.039,-1.039L14.780001,7.325C14.78,6.751 14.316,6.286 13.741,6.286zM11.173,6.286L8.025,6.286L8.025,5.247l3.147,0L11.172,6.286z" + android:pathData="M15.2,6.2L4.8,6.2c-0.5,0.0 -0.9,0.4 -0.9,1.0L3.9,10.0c0.0,0.5 0.4,1.0 0.9,1.0l3.8,0.0l0.0,-1.0l2.9,0.0l0.0,1.0l3.8,0.0c0.5,0.0 1.0,-0.4 1.0,-1.0L16.3,7.1C16.2,6.6 15.8,6.2 15.2,6.2z" android:fillColor="#FFFFFF"/> <path - android:pathData="M15.172,7.039c0.0,-0.58 -0.501,-1.05 -1.12,-1.05L5.113,5.989c-0.619,0 -1.115,0.47 -1.115,1.05l0.002,2.193c0,0.618 0.5,1.118 1.118,1.118l8.931,0c0.618,0 1.118,-0.5 1.118,-1.118L15.172,7.039z" + android:pathData="M8.6,12.9l0.0,-1.0L4.3,11.9l0.0,2.4c0.0,0.5 0.4,0.9 0.9,0.9l9.5,0.0c0.5,0.0 0.9,-0.4 0.9,-0.9l0.0,-2.4l-4.3,0.0l0.0,1.0L8.6,12.9z" android:fillColor="#FFFFFF"/> <path - android:pathData="M3.5,9.812l12,0l0,1l-12,0z" - android:fillColor="#FF5722"/> - <path - android:pathData="M8.567,9.467l2.037,0l0,2.037l-2.037,0z" - android:fillColor="#FF5722"/> + android:pathData="M7.1,5.2l0.0,1.0 1.0,0.0 0.0,-1.0 3.799999,0.0 0.0,1.0 1.0,0.0 0.0,-1.0 -1.0,-0.9 -3.799999,0.0z" + android:fillColor="#FFFFFF"/> </vector> diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml index 538dade1dd8b..02735459e04c 100644 --- a/core/res/res/drawable/ic_corp_icon_badge.xml +++ b/core/res/res/drawable/ic_corp_icon_badge.xml @@ -20,25 +20,22 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="64.0"> <path android:fillColor="#FF000000" - android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:pathData="M49.1,50.1m-13.9,0.0a13.9,13.9 0.0,1.0 1.0,27.8 0.0a13.9,13.9 0.0,1.0 1.0,-27.8 0.0" android:fillAlpha="0.2"/> <path android:fillColor="#FF000000" - android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:pathData="M49.1,49.4m-13.9,0.0a13.9,13.9 0.0,1.0 1.0,27.8 0.0a13.9,13.9 0.0,1.0 1.0,-27.8 0.0" android:fillAlpha="0.2"/> <path - android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:pathData="M49.1,48.8m-13.9,0.0a13.9,13.9 0.0,1.0 1.0,27.8 0.0a13.9,13.9 0.0,1.0 1.0,-27.8 0.0" android:fillColor="#FF5722"/> <path - android:pathData="M55.801,43.688l-2.837,-0.001l0.0,-1.137l-1.587,-1.588l-4.72,-0.001l-1.588,1.587l0.0,1.137l-2.867,-0.001c-0.94,0.0 -1.691,0.76 -1.691,1.699L40.5,48.654c0.0,0.94 0.76,1.7 1.699,1.7l5.255,0.001l0.0,-1.271l0.225,0.0l2.589,0.0l0.225,0.0l0.0,1.271l5.303,0.001c0.939,0.0 1.7,-0.76 1.7,-1.699l0.002,-3.269C57.5,44.449 56.74,43.689 55.801,43.688zM51.377,43.687l-4.72,-0.001l0.0,-1.137l4.72,0.001L51.377,43.687z" + android:pathData="M56.4,43.5L41.8,43.5c-0.7,0.0 -1.3,0.6 -1.3,1.3l0.0,4.0c0.0,0.7 0.6,1.3 1.3,1.3L47.0,50.1l0.0,-1.3l4.0,0.0l0.0,1.4l5.4,0.0c0.7,0.0 1.3,-0.6 1.3,-1.3l0.0,-4.0C57.6,44.1 57.0,43.5 56.4,43.5z" android:fillColor="#FFFFFF"/> <path - android:pathData="M50.494,52.012l-3.04,0.0l0.0,-0.901l-6.417,0.0l0.0,3.172c0.0,0.94 0.741,1.7 1.68,1.7l12.464,0.003c0.939,0.0 1.702,-0.76 1.703,-1.699l0.0,-3.176l-6.39,0.0L50.494,52.012z" + android:pathData="M47.1,52.8l0.0,-1.3l-6.0,0.0l0.0,3.3c0.0,0.7 0.6,1.3 1.3,1.3l13.2,0.0c0.7,0.0 1.3,-0.6 1.3,-1.3l0.0,-3.3l-6.0,0.0l0.0,1.3L47.1,52.8z" android:fillColor="#FFFFFF"/> <path - android:pathData="M40.726,40.726 h16.13 v16.13 h-16.13z" - android:fillColor="#00000000"/> - <path - android:pathData="M46.657,42.55 h4.72 v1.137 h-4.72z" - android:fillColor="#00000000"/> + android:pathData="M45.1,42.2l0.0,1.299999 1.300003,0.0 0.0,-1.299999 5.299999,0.0 0.0,1.299999 1.399998,0.0 0.0,-1.299999 -1.399998,-1.299999 -5.299999,0.0z" + android:fillColor="#FFFFFF"/> </vector> diff --git a/core/res/res/values-mcc214-mnc03/config.xml b/core/res/res/values-mcc214-mnc03/config.xml deleted file mode 100644 index aa164685a9a9..000000000000 --- a/core/res/res/values-mcc214-mnc03/config.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. Do not translate. --> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - - <!-- String containing the apn value for tethering. May be overriden by secure settings - TETHER_DUN_APN. Value is a comma separated series of strings: - "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type", - Or string format of ApnSettingV3. - note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" --> - <string-array translatable="false" name="config_tether_apndata"> - <item>Orange Internet PC,internet,,,orange,orange,,,,,214,03,1,DUN</item> - </string-array> - -</resources> diff --git a/core/res/res/values-mcc234-mnc33/config.xml b/core/res/res/values-mcc234-mnc33/config.xml index 4637519d7311..776b5702a0d0 100644 --- a/core/res/res/values-mcc234-mnc33/config.xml +++ b/core/res/res/values-mcc234-mnc33/config.xml @@ -29,13 +29,4 @@ <item>23434</item> <item>23486</item> </string-array> - - <!-- String containing the apn value for tethering. May be overriden by secure settings - TETHER_DUN_APN. Value is a comma separated series of strings: - "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type", - Or string format of ApnSettingV3. - note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" --> - <string-array translatable="false" name="config_tether_apndata"> - <item>Consumer Broadband,consumerbroadband,,,,,,,,,234,33,,DUN</item> - </string-array> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 7a42f3e6bf72..ebe0f87b83e1 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1198,7 +1198,7 @@ <item quantity="other" msgid="2973062968038355991">"za <xliff:g id="COUNT">%d</xliff:g> dni"</item> </plurals> <string name="preposition_for_date" msgid="9093949757757445117">"w dniu <xliff:g id="DATE">%s</xliff:g>"</string> - <string name="preposition_for_time" msgid="5506831244263083793">"o godzinie <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="preposition_for_time" msgid="5506831244263083793">"o godzinie <xliff:g id="TIME">%s</xliff:g>"</string> <string name="preposition_for_year" msgid="5040395640711867177">"w <xliff:g id="YEAR">%s</xliff:g> r."</string> <string name="day" msgid="8144195776058119424">"dzień"</string> <string name="days" msgid="4774547661021344602">"dni"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 2f9cd1031c10..30f27db7d262 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1841,7 +1841,7 @@ <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Запрашивать PIN-код для отключения блокировки"</string> <string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Запрашивать графический ключ для отключения блокировки"</string> <string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Запрашивать пароль для отключения блокировки"</string> - <string name="battery_saver_description" msgid="1960431123816253034">"Чтобы заряд батареи расходовался медленнее, режим энергосбережения уменьшает быстродействие устройства и ограничивает количество ресурсов, затрачиваемых на вибрацию, Геолокацию и большинство процессов обработки данных в фоновом режиме. Приложения, которые используют синхронизацию (например, для электронной почты и обмена SMS), могут не обновляться, пока вы их не откроете.\n\nКогда устройство заряжается, режим энергосбережения отключается автоматически."</string> + <string name="battery_saver_description" msgid="1960431123816253034">"Чтобы продлить время работы устройства от батареи, в режиме энергосбережения снижается производительность, а также ограничивается использование вибрации, геолокации и фоновой передачи данных. Данные, требующие синхронизации, могут обновляться только когда вы откроете приложение.\n\nРежим энергосбережения автоматически отключается во время зарядки устройства."</string> <string name="downtime_condition_summary" msgid="8761776337475705749">"До отключения режима (в <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>)"</string> <string name="downtime_condition_line_one" msgid="8762708714645352010">"До отключения режима"</string> <plurals name="zen_mode_duration_minutes_summary"> diff --git a/core/res/res/values-mcc222-mnc01/config.xml b/core/res/res/values-television/config.xml index 4b1981f988f6..34354746b97c 100644 --- a/core/res/res/values-mcc222-mnc01/config.xml +++ b/core/res/res/values-television/config.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!-- /* -** Copyright 2009, The Android Open Source Project +** Copyright 2015, 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. @@ -18,15 +18,9 @@ --> <!-- These resources are around just to allow their values to be customized - for different hardware and product builds. Do not translate. --> + for TV products. Do not translate. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- String containing the apn value for tethering. May be overriden by secure settings - TETHER_DUN_APN. Value is a comma separated series of strings: - "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type", - Or string format of ApnSettingV3. - note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" --> - <string-array translatable="false" name="config_tether_apndata"> - <item>TIM WEB,ibox.tim.it,,,,,,,,,222,01,,DUN</item> - </string-array> + <!-- Flags enabling default window features. See Window.java --> + <bool name="config_defaultWindowFeatureOptionsPanel">false</bool> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 24ec7ce94916..fd30ce9937ac 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -968,7 +968,7 @@ <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"重试"</string> <string name="lockscreen_password_wrong" msgid="5737815393253165301">"重试"</string> <string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超过“人脸解锁”尝试次数上限"</string> - <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"没有SIM卡"</string> + <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"没有 SIM 卡"</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板电脑中没有SIM卡。"</string> <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"电视中没有 SIM 卡。"</string> <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"手机中无SIM卡"</string> @@ -1460,9 +1460,9 @@ <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"允许应用调用默认的容器服务,以便复制内容。普通应用不应使用此权限。"</string> <string name="permlab_route_media_output" msgid="1642024455750414694">"更改媒体输出线路"</string> <string name="permdesc_route_media_output" msgid="4932818749547244346">"允许该应用将媒体输出线路更改到其他外部设备。"</string> - <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"访问密钥保护安全存储空间"</string> + <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"访问锁屏安全存储空间"</string> <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"允许应用访问密钥保护安全存储空间。"</string> - <string name="permlab_control_keyguard" msgid="172195184207828387">"控制是显示还是隐藏锁屏"</string> + <string name="permlab_control_keyguard" msgid="172195184207828387">"控制锁屏界面的显示和隐藏状态"</string> <string name="permdesc_control_keyguard" msgid="3043732290518629061">"允许应用控制锁屏。"</string> <string name="permlab_trust_listener" msgid="1765718054003704476">"检测信任状态的变化。"</string> <string name="permdesc_trust_listener" msgid="8233895334214716864">"允许应用检测信任状态的变化。"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d036140451c0..d053ceb160ab 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2021,4 +2021,7 @@ <!-- Use ERI text for network name on CDMA LTE --> <bool name="config_LTE_eri_for_network_name">true</bool> + + <!-- Whether to start in touch mode --> + <bool name="config_defaultInTouchMode">true</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4a1ed5546ed7..e47d5f7f4d06 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2155,4 +2155,5 @@ <java-symbol type="bool" name="config_use_sim_language_file" /> <java-symbol type="bool" name="config_LTE_eri_for_network_name" /> + <java-symbol type="bool" name="config_defaultInTouchMode" /> </resources> diff --git a/docs/html/images/transitions/transition_sample_video.mp4 b/docs/html/images/transitions/transition_sample_video.mp4 Binary files differnew file mode 100644 index 000000000000..37ae685864a1 --- /dev/null +++ b/docs/html/images/transitions/transition_sample_video.mp4 diff --git a/docs/html/images/transitions/transition_sample_video.ogv b/docs/html/images/transitions/transition_sample_video.ogv Binary files differnew file mode 100644 index 000000000000..55988147ae18 --- /dev/null +++ b/docs/html/images/transitions/transition_sample_video.ogv diff --git a/docs/html/images/transitions/transition_sample_video.webm b/docs/html/images/transitions/transition_sample_video.webm Binary files differnew file mode 100644 index 000000000000..346ba8c0c87d --- /dev/null +++ b/docs/html/images/transitions/transition_sample_video.webm diff --git a/docs/html/images/transitions/transitions_diagram.png b/docs/html/images/transitions/transitions_diagram.png Binary files differnew file mode 100644 index 000000000000..936394049d7f --- /dev/null +++ b/docs/html/images/transitions/transitions_diagram.png diff --git a/docs/html/tools/building/plugin-for-gradle.jd b/docs/html/tools/building/plugin-for-gradle.jd index 77cbfda86b0f..54a03fd1761a 100644 --- a/docs/html/tools/building/plugin-for-gradle.jd +++ b/docs/html/tools/building/plugin-for-gradle.jd @@ -321,7 +321,7 @@ logic inside Gradle build files instead.</p> machine and on other machines where Android Studio is not installed.</p> <p class="caution"><strong>Caution:</strong> When you create a project, only use the Gradle wrapper -scripts and JAR from a trusted source, such as those generated by Android Studio. /p> +scripts and JAR from a trusted source, such as those generated by Android Studio. </p> <h2 id="buildVariants"> Build variants</h2> diff --git a/docs/html/training/location/display-address.jd b/docs/html/training/location/display-address.jd index 621b08224329..516f14f06781 100644 --- a/docs/html/training/location/display-address.jd +++ b/docs/html/training/location/display-address.jd @@ -1,280 +1,468 @@ page.title=Displaying a Location Address - trainingnavtop=true - @jd:body - - <div id="tb-wrapper"> -<div id="tb"> + <div id="tb"> -<h2>This lesson teaches you to</h2> -<ol> - <li><a href="#DefineTask">Define the Address Lookup Task</a></li> - <li><a href="#DisplayResults">Define a Method to Display the Results</a></li> - <li><a href="#RunTask">Run the Lookup Task</a></li> -</ol> + <h2>This lesson teaches you how to</h2> + <ol> + <li><a href="#connect">Get a Geographic Location</a></li> + <li><a href="#fetch-address">Define an Intent Service to Fetch the + Address</a></li> + <li><a href="#start-intent">Start the Intent Service</a></li> + <li><a href="#result-receiver">Receive the Geocoding Results</a></li> + </ol> -<h2>You should also read</h2> -<ul> - <li> - <a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a> - </li> - <li> - <a href="retrieve-current.html">Retrieving the Current Location</a> - </li> - <li> - <a href="receive-location-updates.html">Receiving Location Updates</a> - </li> -</ul> -<h2>Try it out</h2> + <h2>You should also read</h2> + <ul> + <li> + <a href="{@docRoot}google/play-services/setup.html">Setting up Google + Play Services</a> + </li> + <li> + <a href="retrieve-current.html">Getting the Last Known Location</a> + </li> + <li> + <a href="receive-location-updates.html">Receiving Location Updates</a> + </li> + </ul> + <h2>Try it out</h2> -<div class="download-box"> -<a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download - the sample app</a> -<p class="filename">LocationUpdates.zip</p> + <ul> + <li> + <a href="https://github.com/googlesamples/android-play-location/tree/master/LocationAddress" class="external-link">LocationAddress</a> + </li> + </ul> + </div> </div> -</div> -</div> +<p>The lessons <a href="retrieve-current.html">Getting the Last Known + Location</a> and <a href="receive-location-updates.html">Receiving Location + Updates</a> describe how to get the user's location in the form of a + {@link android.location.Location} object that contains latitude and longitude + coordinates. Although latitude and longitude are useful for calculating + distance or displaying a map position, in many cases the address of the + location is more useful. For example, if you want to let your users know where + they are or what is close by, a street address is more meaningful than the + geographic coordinates (latitude/longitude) of the location.</p> + +<p>Using the {@link android.location.Geocoder} class in the Android framework + location APIs, you can convert an address to the corresponding geographic + coordinates. This process is called <em>geocoding</em>. Alternatively, you can + convert a geographic location to an address. The address lookup feature is + also known as <em>reverse geocoding</em>.</p> + +<p>This lesson shows you how to use the + {@link android.location.Geocoder#getFromLocation getFromLocation()} method to + convert a geographic location to an address. The method returns an estimated + street address corresponding to a given latitude and longitude.</p> + +<h2 id="connect">Get a Geographic Location</h2> + +<p>The last known location of the device is a useful starting point for the + address lookup feature. The lesson on + <a href="retrieve-current.html">Getting the Last Known Location</a> shows you + how to use the + <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a> + method provided by the + <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused + location provider</a> to find the latest location of the device.</p> + +<p>To access the fused location provider, you need to create an instance of the + Google Play services API client. To learn how to connect your client, see + <a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect + to Google Play Services</a>.</p> + +<p>In order for the fused location provider to retrieve a precise street + address, set the location permission in your app manifest to + {@code ACCESS_FINE_LOCATION}, as shown in the following example:</p> -<p> - The lessons <a href="retrieve-current.html">Retrieving the Current Location</a> and - <a href="receive-location-updates.html">Receiving Location Updates</a> describe how to get the - user's current location in the form of a {@link android.location.Location} object that - contains latitude and longitude coordinates. Although latitude and longitude are useful for - calculating distance or displaying a map position, in many cases the address of the location is - more useful. -</p> -<p> - The Android platform API provides a feature that returns an estimated street addresses for - latitude and longitude values. This lesson shows you how to use this address lookup feature. -</p> -<p class="note"> - <strong>Note:</strong> Address lookup requires a backend service that is not included in the - core Android framework. If this backend service is not available, - {@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()} returns an empty - list. The helper method {@link android.location.Geocoder#isPresent isPresent()}, available - in API level 9 and later, checks to see if the backend service is available. -</p> -<p> - The snippets in the following sections assume that your app has already retrieved the - current location and stored it as a {@link android.location.Location} object in the global - variable {@code mLocation}. -</p> -<!-- - Define the address lookup task ---> -<h2 id="DefineTask">Define the Address Lookup Task</h2> -<p> -To get an address for a given latitude and longitude, call -{@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()}, which returns a -list of addresses. The method is synchronous, and may take a long time to do its work, so you -should call the method from the {@link android.os.AsyncTask#doInBackground -doInBackground()} method of an {@link android.os.AsyncTask}. -</p> -<p> -While your app is getting the address, display an indeterminate activity -indicator to show that your app is working in the background. Set the indicator's initial state -to {@code android:visibility="gone"}, to make it invisible and remove it from the layout -hierarchy. When you start the address lookup, you set its visibility to "visible". -</p> -<p> -The following snippet shows how to add an indeterminate {@link android.widget.ProgressBar} to -your layout file: -</p> <pre> -<ProgressBar -android:id="@+id/address_progress" -android:layout_width="wrap_content" -android:layout_height="wrap_content" -android:layout_centerHorizontal="true" -android:indeterminate="true" -android:visibility="gone" /> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.gms.location.sample.locationupdates" > + + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> +</manifest> </pre> -<p> -To create the background task, define a subclass of {@link android.os.AsyncTask} that calls -{@link android.location.Geocoder#getFromLocation getFromLocation()} and returns an address. -Define a {@link android.widget.TextView} object {@code mAddress} to contain the returned -address, and a {@link android.widget.ProgressBar} object that allows you to control the -indeterminate activity indicator. For example: -</p> + +<h2 id="fetch-address">Define an Intent Service to Fetch the Address</h2> + +<p>The {@link android.location.Geocoder#getFromLocation getFromLocation()} + method provided by the {@link android.location.Geocoder} class accepts a + latitude and longitude, and returns a list of addresses. The method is + synchronous, and may take a long time to do its work, so you should not call + it from the main, user interface (UI) thread of your app.</p> + +<p>The {@link android.app.IntentService IntentService} class provides a + structure for running a task on a background thread. Using this class, you can + handle a long-running operation without affecting your UI's responsiveness. + Note that the {@link android.os.AsyncTask AsyncTask} class also allows you to + perform background operations, but it's designed for short operations. An + {@link android.os.AsyncTask AsyncTask} shouldn't keep a reference to the UI if + the activity is recreated, for example when the device is rotated. In + contrast, an {@link android.app.IntentService IntentService} doesn't need to + be cancelled when the activity is rebuilt.</p> + +<p>Define a {@code FetchAddressIntentService} class that extends + {@link android.app.IntentService}. This class is your address lookup service. + The intent service handles an intent asynchronously on a worker thread, and + stops itself when it runs out of work. The intent extras provide the data + needed by the service, including a {@link android.location.Location} object + for conversion to an address, and a {@link android.os.ResultReceiver} object + to handle the results of the address lookup. The service uses a {@link + android.location.Geocoder} to fetch the address for the location, and sends + the results to the {@link android.os.ResultReceiver}.</p> + +<h3>Define the Intent Service in your App Manifest</h3> + +<p>Add an entry to your app manifest defining the intent service:</p> + <pre> -public class MainActivity extends FragmentActivity { +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.gms.location.sample.locationaddress" > + <application + ... + <service + android:name=".FetchAddressIntentService" + android:exported="false"/> + </application> ... - private TextView mAddress; - private ProgressBar mActivityIndicator; +</manifest> +</pre> + +<p class="note"><strong>Note:</strong> The {@code <service>} element in + the manifest doesn't need to include an intent filter, because your main + activity creates an explicit intent by specifying the name of the class to use + for the intent.</p> + +<h3>Create a Geocoder</h3> + +<p>The process of converting a geographic location to an address is called + <em>reverse geocoding</em>. To perform the main work of the intent service, + that is, your reverse geocoding request, implement + {@link android.app.IntentService#onHandleIntent onHandleIntent()} within the + {@code FetchAddressIntentService} class. Create a + {@link android.location.Geocoder} object to handle the reverse geocoding.</p> + +<p>A locale represents a specific geographical or linguistic region. Locale + objects are used to adjust the presentation of information, such as numbers or + dates, to suit the conventions in the region represented by the locale. Pass a + <a href="{@docRoot}reference/java/util/Locale.html">{@code Locale}</a> object + to the {@link android.location.Geocoder} object, to ensure that the resulting + address is localized to the user's geographic region.</p> + +<pre> +@Override +protected void onHandleIntent(Intent intent) { + Geocoder geocoder = new Geocoder(this, Locale.getDefault()); ... - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); +} +</pre> + +<h3 id="retrieve-street-address">Retrieve the street address data</h3> + +<p>The next step is to retrieve the street address from the geocoder, handle + any errors that may occur, and send the results back to the activity that + requested the address. To report the results of the geocoding + process, you need two numeric constants that indicate success or failure. + Define a {@code Constants} class to contain the values, as shown in this code + snippet:</p> + +<pre> +public final class Constants { + public static final int SUCCESS_RESULT = 0; + public static final int FAILURE_RESULT = 1; + public static final String PACKAGE_NAME = + "com.google.android.gms.location.sample.locationaddress"; + public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER"; + public static final String RESULT_DATA_KEY = PACKAGE_NAME + + ".RESULT_DATA_KEY"; + public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME + + ".LOCATION_DATA_EXTRA"; +} +</pre> + +<p>To get a street address corresponding to a geographical location, call + {@link android.location.Geocoder#getFromLocation getFromLocation()}, + passing it the latitude and longitude from the location object, and the + maximum number of addresses you want returned. In this case, you want just one + address. The geocoder returns an array of addresses. If no addresses were + found to match the given location, it returns an empty list. If there is no + backend geocoding service available, the geocoder returns null.</p> + +<p>Check for the following errors as shown in the code sample below. If an error + occurs, place the corresponding error message in the {@code errorMessage} + variable, so you can send it back to the requesting activity:</p> + +<ul> + <li><strong>No location data provided</strong> - The intent extras do not + include the {@link android.location.Location} object required for reverse + geocoding.</li> + <li><strong>Invalid latitude or longitude used</strong> - The latitude + and/or longitude values provided in the {@link android.location.Location} + object are invalid.</li> + <li><strong>No geocoder available</strong> - The background geocoding service + is not available, due to a network error or IO exception.</li> + <li><strong>Sorry, no address found</strong> - The geocoder could not find an + address for the given latitude/longitude.</li> +</ul> + +<p>To get the individual lines of an address object, use the + {@link android.location.Address#getAddressLine getAddressLine()} + method provided by the {@link android.location.Address} class. Then join the + lines into a list of address fragments ready to return to the activity that + requested the address.</p> + +<p>To send the results back to the requesting activity, call the + {@code deliverResultToReceiver()} method (defined in + <a href="#return-address">Return the address to the requestor</a>). The + results consist of the previously-mentioned numeric success/failure code and + a string. In the case of a successful reverse geocoding, the string contains + the address. In the case of a failure, the string contains the error message, + as shown in the code sample below:</p> + +<pre> +@Override +protected void onHandleIntent(Intent intent) { + String errorMessage = ""; + + // Get the location passed to this service through an extra. + Location location = intent.getParcelableExtra( + Constants.LOCATION_DATA_EXTRA); + ... - mAddress = (TextView) findViewById(R.id.address); - mActivityIndicator = - (ProgressBar) findViewById(R.id.address_progress); + + List<Address> addresses = null; + + try { + addresses = geocoder.getFromLocation( + location.getLatitude(), + location.getLongitude(), + // In this sample, get just a single address. + 1); + } catch (IOException ioException) { + // Catch network or other I/O problems. + errorMessage = getString(R.string.service_not_available); + Log.e(TAG, errorMessage, ioException); + } catch (IllegalArgumentException illegalArgumentException) { + // Catch invalid latitude or longitude values. + errorMessage = getString(R.string.invalid_lat_long_used); + Log.e(TAG, errorMessage + ". " + + "Latitude = " + location.getLatitude() + + ", Longitude = " + + location.getLongitude(), illegalArgumentException); } - ... - /** - * A subclass of AsyncTask that calls getFromLocation() in the - * background. The class definition has these generic types: - * Location - A {@link android.location.Location} object containing - * the current location. - * Void - indicates that progress units are not used - * String - An address passed to onPostExecute() - */ - private class GetAddressTask extends - AsyncTask<Location, Void, String> { - Context mContext; - public GetAddressTask(Context context) { - super(); - mContext = context; + + // Handle case where no address was found. + if (addresses == null || addresses.size() == 0) { + if (errorMessage.isEmpty()) { + errorMessage = getString(R.string.no_address_found); + Log.e(TAG, errorMessage); } - ... - /** - * Get a Geocoder instance, get the latitude and longitude - * look up the address, and return it - * - * @params params One or more Location objects - * @return A string containing the address of the current - * location, or an empty string if no address can be found, - * or an error message - */ - @Override - protected String doInBackground(Location... params) { - Geocoder geocoder = - new Geocoder(mContext, Locale.getDefault()); - // Get the current location from the input parameter list - Location loc = params[0]; - // Create a list to contain the result address - List<Address> addresses = null; - try { - /* - * Return 1 address. - */ - addresses = geocoder.getFromLocation(loc.getLatitude(), - loc.getLongitude(), 1); - } catch (IOException e1) { - Log.e("LocationSampleActivity", - "IO Exception in getFromLocation()"); - e1.printStackTrace(); - return ("IO Exception trying to get address"); - } catch (IllegalArgumentException e2) { - // Error message to post in the log - String errorString = "Illegal arguments " + - Double.toString(loc.getLatitude()) + - " , " + - Double.toString(loc.getLongitude()) + - " passed to address service"; - Log.e("LocationSampleActivity", errorString); - e2.printStackTrace(); - return errorString; - } - // If the reverse geocode returned an address - if (addresses != null && addresses.size() > 0) { - // Get the first address - Address address = addresses.get(0); - /* - * Format the first line of address (if available), - * city, and country name. - */ - String addressText = String.format( - "%s, %s, %s", - // If there's a street address, add it - address.getMaxAddressLineIndex() > 0 ? - address.getAddressLine(0) : "", - // Locality is usually a city - address.getLocality(), - // The country of the address - address.getCountryName()); - // Return the text - return addressText; - } else { - return "No address found"; - } + deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); + } else { + Address address = addresses.get(0); + ArrayList<String> addressFragments = new ArrayList<String>(); + + // Fetch the address lines using {@code getAddressLine}, + // join them, and send them to the thread. + for(int i = 0; i < address.getMaxAddressLineIndex(); i++) { + addressFragments.add(address.getAddressLine(i)); } - ... + Log.i(TAG, getString(R.string.address_found)); + deliverResultToReceiver(Constants.SUCCESS_RESULT, + TextUtils.join(System.getProperty("line.separator"), + addressFragments)); } - ... } </pre> -<p> -The next section shows you how to display the address in the user interface. -</p> -<!-- Define a method to display the address --> -<h2 id="DisplayResults">Define a Method to Display the Results</h2> -<p> - {@link android.os.AsyncTask#doInBackground doInBackground()} returns the result of the address - lookup as a {@link java.lang.String}. This value is passed to - {@link android.os.AsyncTask#onPostExecute onPostExecute()}, where you do further processing - on the results. Since {@link android.os.AsyncTask#onPostExecute onPostExecute()} - runs on the UI thread, it can update the user interface; for example, it can turn off the - activity indicator and display the results to the user: + +<h3 id="return-address">Return the address to the requestor</h3> + +<p>The final thing the intent service must do is send the address back to a + {@link android.os.ResultReceiver} in the activity that started the service. + The {@link android.os.ResultReceiver} class allows you to send a + numeric result code as well as a message containing the result data. The + numeric code is useful for reporting the success or failure of the geocoding + request. In the case of a successful reverse geocoding, the message contains + the address. In the case of a failure, the message contains some text + describing the reason for failure.</p> + +<p>You have already retrieved the address from the geocoder, trapped any errors + that may occur, and called the {@code deliverResultToReceiver()} method. Now + you need to define the {@code deliverResultToReceiver()} method that sends + a result code and message bundle to the result receiver.</p> + +<p>For the result code, use the value that you've passed to the + {@code deliverResultToReceiver()} method in the {@code resultCode} parameter. + To construct the message bundle, concatenate the {@code RESULT_DATA_KEY} + constant from your {@code Constants} class (defined in + <a href="#retrieve-street-address">Retrieve the street address data</a>) and + the value in the {@code message} parameter passed to the + {@code deliverResultToReceiver()} method, as shown in the following sample: </p> + <pre> - private class GetAddressTask extends - AsyncTask<Location, Void, String> { - ... - /** - * A method that's called once doInBackground() completes. Turn - * off the indeterminate activity indicator and set - * the text of the UI element that shows the address. If the - * lookup failed, display the error message. - */ - @Override - protected void onPostExecute(String address) { - // Set activity indicator visibility to "gone" - mActivityIndicator.setVisibility(View.GONE); - // Display the results of the lookup. - mAddress.setText(address); - } - ... +public class FetchAddressIntentService extends IntentService { + protected ResultReceiver mReceiver; + ... + private void deliverResultToReceiver(int resultCode, String message) { + Bundle bundle = new Bundle(); + bundle.putString(Constants.RESULT_DATA_KEY, message); + mReceiver.send(resultCode, bundle); } +} </pre> -<p> - The final step is to run the address lookup. -</p> -<!-- Get and display the address --> -<h2 id="RunTask">Run the Lookup Task</h2> -<p> - To get the address, call {@link android.os.AsyncTask#execute execute()}. For example, the - following snippet starts the address lookup when the user clicks the "Get Address" button: -</p> + +<h2 id="start-intent">Start the Intent Service</h2> + +<p>The intent service, as defined in the previous section, runs in the + background and is responsible for fetching the address corresponding to a + given geographic location. When you start the service, the Android framework + instantiates and starts the service if it isn't already running, and creates a + process if needed. If the service is already running then it remains running. + Because the service extends {@link android.app.IntentService IntentService}, + it shuts down automatically when all intents have been processed.</p> + +<p>Start the service from your app's main activity, + and create an {@link android.content.Intent} to pass data to the service. You + need an <em>explicit</em> intent, because you want only your service + to respond to the intent. For more information, see + <a href="{@docRoot}guide/components/intents-filters.html#Types">Intent + Types</a>.</p> + +<p>To create an explicit intent, specify the name of the + class to use for the service: {@code FetchAddressIntentService.class}. + Pass two pieces of information in the intent extras:</p> + +<ul> + <li>A {@link android.os.ResultReceiver} to handle the results of the address + lookup.</li> + <li>A {@link android.location.Location} object containing the latitude and + longitude that you want to convert to an address.</li> +</ul> + +<p>The following code sample shows you how to start the intent service:</p> + <pre> -public class MainActivity extends FragmentActivity { +public class MainActivity extends ActionBarActivity implements + ConnectionCallbacks, OnConnectionFailedListener { + + protected Location mLastLocation; + private AddressResultReceiver mResultReceiver; ... - /** - * The "Get Address" button in the UI is defined with - * android:onClick="getAddress". The method is invoked whenever the - * user clicks the button. - * - * @param v The view object associated with this method, - * in this case a Button. - */ - public void getAddress(View v) { - // Ensure that a Geocoder services is available - if (Build.VERSION.SDK_INT >= - Build.VERSION_CODES.GINGERBREAD - && - Geocoder.isPresent()) { - // Show the activity indicator - mActivityIndicator.setVisibility(View.VISIBLE); - /* - * Reverse geocoding is long-running and synchronous. - * Run it on a background thread. - * Pass the current location to the background task. - * When the task finishes, - * onPostExecute() displays the address. - */ - (new GetAddressTask(this)).execute(mLocation); + + protected void startIntentService() { + Intent intent = new Intent(this, FetchAddressIntentService.class); + intent.putExtra(Constants.RECEIVER, mResultReceiver); + intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation); + startService(intent); + } +} +</pre> + +<p>Call the above {@code startIntentService()} method when the + user takes an action that requires a geocoding address lookup. For example, + the user may press a <em>Fetch address</em> button on your app's UI. Before + starting the intent service, you need to check that the connection to Google + Play services is present. The following code snippet shows the call to the + {@code startIntentService()} method in the button handler:</p> + +<pre> +public void fetchAddressButtonHandler(View view) { + // Only start the service to fetch the address if GoogleApiClient is + // connected. + if (mGoogleApiClient.isConnected() && mLastLocation != null) { + startIntentService(); + } + // If GoogleApiClient isn't connected, process the user's request by + // setting mAddressRequested to true. Later, when GoogleApiClient connects, + // launch the service to fetch the address. As far as the user is + // concerned, pressing the Fetch Address button + // immediately kicks off the process of getting the address. + mAddressRequested = true; + updateUIWidgets(); +} +</pre> + +<p>You must also start the intent service when the connection to Google Play + services is established, if the user has already clicked the button on your + app's UI. The following code snippet shows the call to the + {@code startIntentService()} method in the + <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a> + callback provided by the Google API Client:</p> + +<pre> +public class MainActivity extends ActionBarActivity implements + ConnectionCallbacks, OnConnectionFailedListener { + ... + @Override + public void onConnected(Bundle connectionHint) { + // Gets the best and most recent location currently available, + // which may be null in rare cases when a location is not available. + mLastLocation = LocationServices.FusedLocationApi.getLastLocation( + mGoogleApiClient); + + if (mLastLocation != null) { + // Determine whether a Geocoder is available. + if (!Geocoder.isPresent()) { + Toast.makeText(this, R.string.no_geocoder_available, + Toast.LENGTH_LONG).show(); + return; + } + + if (mAddressRequested) { + startIntentService(); + } } - ... } +} +</pre> + +<h2 id="result-receiver">Receive the Geocoding Results</h2> + +<p>The intent service has handled the geocoding request, and uses a + {@link android.os.ResultReceiver} to return the results to the activity that + made the request. In the activity that makes the request, define an + {@code AddressResultReceiver} that extends {@link android.os.ResultReceiver} + to handle the response from {@code FetchAddressIntentService}.</p> + +<p>The result includes a numeric result code (<code>resultCode</code>) as well + as a message containing the result data (<code>resultData</code>). If the + reverse geocoding process was successful, the <code>resultData</code> contains + the address. In the case of a failure, the <code>resultData</code> contains + text describing the reason for failure. For details of the possible errors, + see <a href="#return-address">Return the address to the requestor</a>.</p> + +<p>Override the + {@link android.os.ResultReceiver#onReceiveResult onReceiveResult()} method + to handle the results delivered to the result receiver, as shown in the + following code sample:</p> + +<pre> +public class MainActivity extends ActionBarActivity implements + ConnectionCallbacks, OnConnectionFailedListener { ... + class AddressResultReceiver extends ResultReceiver { + public AddressResultReceiver(Handler handler) { + super(handler); + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + + // Display the address string + // or an error message sent from the intent service. + mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY); + displayAddressOutput(); + + // Show a toast message if an address was found. + if (resultCode == Constants.SUCCESS_RESULT) { + showToast(getString(R.string.address_found)); + } + + } + } } </pre> -<p> - The next lesson, <a href="geofencing.html">Creating and Monitoring Geofences</a>, demonstrates - how to define locations of interest called <b>geofences</b> and how to use geofence monitoring - to detect the user's proximity to a location of interest. -</p> diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 0fcfb9c7b2b5..f5999a51b1b8 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -437,6 +437,35 @@ include the action bar on devices running Android 2.1 or higher." </li> </ul> </li> + + <li class="nav-section"> + <div class="nav-section-header"> + <a href="<?cs var:toroot?>training/transitions/index.html" + description= + "How to animate state changes in a view hierarchy using transitions." + >Animating Views Using Scenes and Transitions</a> + </div> + <ul> + <li><a href="<?cs var:toroot ?>training/transitions/overview.html"> + The Transitions Framework + </a> + </li> + <li><a href="<?cs var:toroot ?>training/transitions/scenes.html"> + Creating a Scene + </a> + </li> + <li><a href="<?cs var:toroot ?>training/transitions/transitions.html"> + Applying a Transition + </a> + </li> + <li><a href="<?cs var:toroot ?>training/transitions/custom-transitions.html"> + Creating Custom Transitions + </a> + </li> + + </ul> + </li> + <li class="nav-section"> <div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html" description= diff --git a/docs/html/training/transitions/custom-transitions.jd b/docs/html/training/transitions/custom-transitions.jd new file mode 100644 index 000000000000..b64daaeb0481 --- /dev/null +++ b/docs/html/training/transitions/custom-transitions.jd @@ -0,0 +1,189 @@ +page.title=Creating Custom Transitions + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#Extend">Extend the Transition Class</a></li> + <li><a href="#CaptureProperties">Capture View Property Values</a></li> + <li><a href="#CreateAnimator">Create a Custom Animator</a></li> + <li><a href="#Apply">Apply a Custom Transition</a></li> +</ol> +</div> +</div> + +<p>A custom transition enables you to create an animation that is not available from any of +the built-in transition classes. For example, you can define a custom transition that turns +the foreground color of text and input fields to gray to indicate that the fields are disabled +in the new screen. This type of change helps users see the fields you disabled.</p> + +<p>A custom transition, like one of the built-in transition types, applies animations to +child views of both the starting and ending scenes. Unlike built-in transition types, +however, you have to provide the code that captures property values and generates animations. +You may also want to define a subset of target views for your animation.</p> + +<p>This lesson teaches you to capture property values and generate animations to create +custom transitions.</p> + + + +<h2 id="Extend">Extend the Transition Class</h2> + +<p>To create a custom transition, add a class to your project that extends the {@link +android.transition.Transition} class and override the methods shown in the following snippet:</p> + +<pre> +public class CustomTransition extends Transition { + + @Override + public void captureStartValues(TransitionValues values) {} + + @Override + public void captureEndValues(TransitionValues values) {} + + @Override + public Animator createAnimator(ViewGroup sceneRoot, + TransitionValues startValues, + TransitionValues endValues) {} +} +</pre> + +<p>The following sections explain how to override these methods.</p> + + + +<h2 id="CaptureProperties">Capture View Property Values</h2> + +<p>Transition animations use the property animation system described in +<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>. Property +animations change a view property between a starting and ending value over a specified +period of time, so the framework needs to have both the starting and ending value of +the property to construct the animation.</p> + +<p>However, a property animation usually needs only a small subset of all the view's property +values. For example, a color animation needs color property values, while a movement +animation needs position property values. Since the property values needed for an animation +are specific to a transition, the transitions framework does not provide every property value +to a transition. Instead, the framework invokes callback methods that allow a transition to +capture only the property values it needs and store them in the framework.</p> + + +<h3 id="StartingValues">Capturing Starting Values</h3> + +<p>To pass the starting view values to the framework, implement the +{@link android.transition.Transition#captureStartValues captureStartValues(transitionValues)} +method. The framework calls this method for every view in the starting scene. The method +argument is a {@link android.transition.TransitionValues} object that contains a reference +to the view and a {@link java.util.Map} instance in which you can store the view values you +want. In your implementation, retrieve these property values and pass them back to the +framework by storing them in the map.</p> + +<p>To ensure that the key for a property value does not conflict with other {@link +android.transition.TransitionValues} keys, use the following naming scheme:</p> + +<pre> +package_name:transition_name:property_name +</pre> + +<p>The following snippet shows an implementation of the {@link +android.transition.Transition#captureStartValues captureStartValues()} method:</p> + +<pre> +public class CustomTransition extends Transition { + + // Define a key for storing a property value in + // TransitionValues.values with the syntax + // package_name:transition_class:property_name to avoid collisions + private static final String PROPNAME_BACKGROUND = + "com.example.android.customtransition:CustomTransition:background"; + + @Override + public void captureStartValues(TransitionValues transitionValues) { + // Call the convenience method captureValues + captureValues(transitionValues); + } + + + // For the view in transitionValues.view, get the values you + // want and put them in transitionValues.values + private void captureValues(TransitionValues transitionValues) { + // Get a reference to the view + View view = transitionValues.view; + // Store its background property in the values map + transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground()); + } + ... +} +</pre> + + +<h3 id="EndingValues">Capture Ending Values</h3> + +<p>The framework calls the {@link android.transition.Transition#captureEndValues} method +once for every target view in the ending scene. In all other respects, {@link +android.transition.Transition#captureEndValues captureEndValues()} works the same as {@link +android.transition.Transition#captureStartValues captureStartValues()}.</p> + +<p>The following code snippet shows an implementation of the {@link +android.transition.Transition#captureEndValues captureEndValues()} method:</p> + +<pre> +@Override +public void captureEndValues(TransitionValues transitionValues) { + captureValues(transitionValues); +} +</pre> + +<p>In this example, both the {@link android.transition.Transition#captureStartValues +captureStartValues()} and {@link android.transition.Transition#captureEndValues captureEndValues()} +methods invoke <code>captureValues()</code> to retrieve and store values. The view property +that <code>captureValues()</code> retrieves is the same, but it has different values in the +starting and ending scenes. The framework maintains separate maps for the starting and ending +states of a view.</p> + + + +<h2 id="CreateAnimator">Create a Custom Animator</h2> + +<p>To animate the changes to a view between its state in the starting scene and its state in +the ending scene, you provide an animator by overriding the {@link +android.transition.Transition#createAnimator createAnimator()} method. When the +framework calls this method, it passes in the scene root view and the {@link +android.transition.TransitionValues} objects that contain the starting and ending values +you captured.</p> + +<p>The number of times the framework calls the {@link +android.transition.Transition#createAnimator createAnimator()} method depends on the changes that +occur between the starting and ending scenes. For example, consider a fade out/fade in animation +implemented as a custom transition. If the starting scene has five targets of which two are +removed from the ending scene, and the ending scene has the three targets from the starting +scene plus a new target, then the framework calls {@link +android.transition.Transition#createAnimator createAnimator()} six times: three of the calls +animate the fading out and fading in of the targets that stay in both scene objects; two more calls +animate the fading out of the targets removed from the ending scene; and one call +animates the fading in of the new target in the ending scene.</p> + +<p>For target views that exist on both the starting and ending scenes, the framework provides +a {@link android.transition.TransitionValues} object for both the <code>startValues</code> and +<code>endValues</code> arguments. For target views that only exist in the starting or the +ending scene, the framework provides a {@link android.transition.TransitionValues} object +for the corresponding argument and <code>null</code> for the other.</p> + +<p>To implement the {@link android.transition.Transition#createAnimator} method when you create +a custom transition, use the view property values you captured to create an {@link +android.animation.Animator} object and return it to the framework. For an example implementation, +see the <a +href="{@docRoot}samples/CustomTransition/src/com.example.android.customtransition/ChangeColor.html"> +<code>ChangeColor</code></a> class in the <a href="{@docRoot}samples/CustomTransition/index.html"> +CustomTransition</a> sample. For more information about property animators, see +<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>.</p> + + + +<h2 id="Apply">Apply a Custom Transition</h2> + +<p>Custom transitions work the same as built-in transitions. You can apply a custom transition +using a transition manager, as described in <a +href="{@docRoot}training/transitions/transitions.html#Apply">Applying a Transition</a>.</p> diff --git a/docs/html/training/transitions/index.jd b/docs/html/training/transitions/index.jd new file mode 100644 index 000000000000..53faa01aad8b --- /dev/null +++ b/docs/html/training/transitions/index.jd @@ -0,0 +1,80 @@ +page.title=Animating Views Using Scenes and Transitions + +@jd:body + +<!-- Sidebox --> +<div id="tb-wrapper"> +<div id="tb"> + <h2>Dependencies and Prerequisites</h2> + <ul> + <li>Android 4.4.2 (API level 19) or higher</li> + </ul> + <h2>You should also read</h2> + <ul> + <li><a href="{@docRoot}guide/topics/ui/how-android-draws.html"> + How Android Draws Views</a></li> + </ul> + <h2>Try it out</h2> + <ul> + <li><a href="{@docRoot}samples/BasicTransition/index.html">BasicTransition</a> sample</li> + <li><a href="{@docRoot}samples/CustomTransition/index.html">CustomTransition</a> sample</li> + </ul> +</div> +</div> + +<!-- Video box --> +<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=S3H7nJ4QaD8"> +<div> + <h3>Video</h3> + <p>DevBytes: Android 4.4 Transitions</p> +</div> +</a> + +<p>The user interface of an activity often changes in response to user input and other events. +For example, an activity that contains a form where users can type search queries can hide +the form when the user submits it and show a list of search results in its place.</p> + +<p>To provide visual continuity in these situations, you can animate changes between +different view hierarchies in your user interface. These animations give users feedback on +their actions and help them learn how your app works.</p> + +<p>Android includes the <em>transitions framework</em>, which enables you to easily +animate changes between two view hierarchies. The framework animates the views at runtime by +changing some of their property values over time. The framework includes built-in animations +for common effects and lets you create custom animations and transition lifecycle callbacks.</p> + +<p>This class teaches you to use the built-in animations in the transitions framework to +animate changes between view hierarchies. This class also covers how to create custom +animations.</p> + +<p class="note"><strong>Note:</strong> For Android versions earlier than 4.4.2 (API level 19) +but greater than or equal to Android 4.0 (API level 14), use the <code>animateLayoutChanges</code> +attribute to animate layouts. To learn more, see +<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a> and +<a href="{@docRoot}training/animation/layout.html">Animating Layout Changes</a>.</p> + + +<h2>Lessons</h2> + +<dl> +<dt><a href="{@docRoot}training/transitions/overview.html"> +The Transitions Framework</a></dt> +<dd> + Learn the main features and components of the transitions framework. +</dd> +<dt><a href="{@docRoot}training/transitions/scenes.html"> +Creating a Scene</a></dt> +<dd> + Learn how to create a scene to store the state of a view hierarchy. +</dd> +<dt><a href="{@docRoot}training/transitions/transitions.html"> +Applying a Transition</a></dt> +<dd> + Learn how to apply a transition between two scenes of a view hierarchy. +</dd> +<dt><a href="{@docRoot}training/transitions/custom-transitions.html"> +Creating Custom Transitions</a></dt> +<dd> + Learn how to create other animation effects not included in the transitions framework. +</dd> +</dl> diff --git a/docs/html/training/transitions/overview.jd b/docs/html/training/transitions/overview.jd new file mode 100644 index 000000000000..044cf1691781 --- /dev/null +++ b/docs/html/training/transitions/overview.jd @@ -0,0 +1,165 @@ +page.title=The Transitions Framework + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson covers</h2> +<ol> + <li><a href="#Overview">Overview</a></li> + <li><a href="#Scenes">Scenes</a></li> + <li><a href="#Transitions">Transitions</a></li> + <li><a href="#Limitations">Limitations</a></li> +</ol> +</div> +</div> + +<p>Animating your app's user interface provides more than just visual appeal. Animations +highlight changes and provide visual cues that help users learn how your app works.</p> + +<p>To help you animate a change between one view hierarchy and another, Android provides the +transitions framework. This framework applies one or more animations to all the views in the +hierarchies as it changes between them.</p> + +<p>The framework has the following features:</p> + +<dl> +<dt><em>Group-level animations</em></dt> +<dd>Applies one or more animation effects to all of the views in a view hierarchy.</dd> +<dt><em>Transition-based animation</em></dt> +<dd>Runs animations based on the changes between starting and ending view property values.</dd> +<dt><em>Built-in animations</em></dt> +<dd>Includes predefined animations for common effects such as fade out or movement.</dd> + +<!-- Figure 1 - Transitions video --> +<div style="float:right;margin-left:30px;margin-top:10px"> +<div class="framed-nexus5-port-span-5" style="clear:left;"> +<video class="play-on-hover" height="442" autoplay="" poster=""> +<source src="{@docRoot}images/transitions/transition_sample_video.mp4" type="video/mp4"> +<source src="{@docRoot}images/transitions/transition_sample_video.ogv" type="video/ogg"> +<source src="{@docRoot}images/transitions/transition_sample_video.webm" type="video/webm"> +</video> +</div> +<p class="img-caption" style="margin-top:7px;margin-bottom:0px"> +<strong>Figure 1.</strong> Visual cues using user interface animation.</p> +<div style="margin-top:5px;margin-bottom:20px;font-size:10pt" class="video-instructions"> </div> +</div> + +<dt><em>Resource file support</em></dt> +<dd>Loads view hierarchies and built-in animations from layout resource files.</dd> +<dt><em>Lifecycle callbacks</em></dt> +<dd>Defines callbacks that provide finer control over the animation and hierarchy change +process.</dd> +</dl> + + + +<h2 id="Overview">Overview</h2> + +<p>The example in Figure 1 shows how an animation provides visual cues to help the user. As the +app changes from its search entry screen to its search results screen, it fades out views that +are no longer in use and fades in new views.</p> + +<p>This animation is an example of using the transitions framework. The framework +animates changes to all the views in two view hierarchies. A view hierarchy can be as simple +as a single view or as complex as a {@link android.view.ViewGroup} containing an elaborate +tree of views. The framework animates each view by changing one or more of its property values +over time between the initial or <em>starting</em> view hierarchy and the final or <em>ending</em> +view hierarchy.</p> + +<p>The transitions framework works in parallel with view hierarchies and animations. The +purpose of the framework is to store the state of view hierarchies, change between these +hierarchies in order to modify the appearance of the device screen, and animate the change by +storing and applying animation definitions.</p> + +<p>The diagram in Figure 2 illustrates the relationship between view hierarchies, framework +objects, and animations:</p> + +<!-- Figure 2 - diagram --> +<img src="{@docRoot}images/transitions/transitions_diagram.png" + width="506" height="234" alt="" style="margin-top:7px" /> +<p class="img-caption"><strong>Figure 2.</strong> Relationships in the transitions framework.</p> + +<p>The transitions framework provides abstractions for scenes, transitions, and transition +managers. These are described in detail in the following sections. To use the framework, you +create scenes for the view hierarchies in your app that you plan to change between. Next, you +create a transition for each animation you want to use. To start the animation between two +view hierarchies, you use a transition manager specifying the transition to use and the ending +scene. This procedure is described in detail in the remaining lessons in this class.</p> + + + +<h2 id="Scenes">Scenes</h2> + +<p>A scene stores the state of a view hierarchy, including all its views and their property +values. A view hierarchy can be a simple view or a complex tree of views and child layouts. +Storing the view hierarchy state in a scene enables you to transition into that state from +another scene. The framework provides the {@link android.transition.Scene} class to represent +a scene.</p> + +<p>The transitions framework lets you create scenes from layout resource files or from +{@link android.view.ViewGroup} objects in your code. Creating a scene in your code is useful +if you generated a view hierarchy dynamically or if you are modifying it at runtime.</p> + +<p>In most cases, you do not create a starting scene explicitly. If you have applied a +transition, the framework uses the previous ending scene as the starting scene for any +subsequent transitions. If you have not applied a transition, the framework collects information +about the views from the current state of the screen.</p> + +<p>A scene can also define its own actions that run when you make a scene change. For example, +this feature is useful for cleaning up view settings after you transition to a scene.</p> + +<p>In addition to the view hierarchy and its property values, a scene also stores a reference +to the parent of the view hierarchy. This root view is called a <strong>scene root</strong>. +Changes to the scene and animations that affect the scene occur within the scene root.</p> + +<p>To learn how to create scenes, see +<a href="{@docRoot}training/transitions/scenes.html">Creating a Scene</a>.</p> + + + +<h2 id="Transitions">Transitions</h2> + +<p>In the transitions framework, animations create a series of frames that depict a change +between the view hierarchies in the starting and ending scenes. Information about the animation +is stored in a {@link android.transition.Transition} object. To run the animation, you apply the +transition using a {@link android.transition.TransitionManager} instance. The framework can +transition between two different scenes or transition to a different state for the current +scene.</p> + +<p>The framework includes a set of built-in transitions for commonly-used animation effects, +such as fading and resizing views. You can also define your own custom transitions to create +an animation effect using the APIs in the animations framework. The transitions framework also +enables you to combine different animation effects in a transition set that contains a group +of individual built-in or custom transitions.</p> + +<p>The transition lifecycle is similar to the activity lifecycle, and it represents the +transition states that the framework monitors between the start and the completion of an +animation. At important lifecycle states, the framework invokes callback methods that you can +implement to make adjustments to your user interface at different phases of the transition.</p> + +<p>To learn more about transitions, see +<a href="{@docRoot}training/transitions/transitions.html">Applying a Transition</a> and +<a href="{@docRoot}training/transitions/custom-transitions.html">Creating Custom +Transitions</a>.</p> + + + +<h2 id="Limitations">Limitations</h2> + +<p>This section lists some known limitations of the transitions framework:</p> + +<ul> +<li>Animations applied to a {@link android.view.SurfaceView} may not appear correctly. +{@link android.view.SurfaceView} instances are updated from a non-UI thread, so the updates +may be out of sync with the animations of other views.</li> +<li>Some specific transition types may not produce the desired animation effect when applied +to a {@link android.view.TextureView}.</li> +<li>Classes that extend {@link android.widget.AdapterView}, such as +{@link android.widget.ListView}, manage their child views in ways that are incompatible with +the transitions framework. If you try to animate a view based on +{@link android.widget.AdapterView}, the device display may hang.</li> +<li>If you try to resize a {@link android.widget.TextView} with an animation, the text will +pop to a new location before the object has completely resized. To avoid this problem, do not +animate the resizing of views that contain text.</li> +</ul> diff --git a/docs/html/training/transitions/scenes.jd b/docs/html/training/transitions/scenes.jd new file mode 100644 index 000000000000..4bf7d0eac929 --- /dev/null +++ b/docs/html/training/transitions/scenes.jd @@ -0,0 +1,211 @@ +page.title=Creating a Scene + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#FromLayout">Create a Scene From a Layout Resource</a></li> + <li><a href="#FromCode">Create a Scene in Your Code</a></li> + <li><a href="#Actions">Create Scene Actions</a></li> +</ol> +</div> +</div> + +<p>Scenes store the state of a view hierarchy, including all its views and their property +values. The transitions framework can run animations between a starting and an ending scene. +The starting scene is often determined automatically from the current state of the user +interface. For the ending scene, the framework enables you to create a scene from a layout +resource file or from a group of views in your code.</p> + +<p>This lesson shows you how to create scenes in your app and how to define scene actions. +The next lesson shows you how to transition between two scenes.</p> + +<p class="note"><strong>Note:</strong> The framework can animate changes in a single view +hierarchy without using scenes, as described in +<a href="{@docRoot}training/transitions/transitions.html#NoScenes">Apply a Transition Without +Scenes</a>. However, understanding this lesson is essential to work with transitions.</p> + + + +<h2 id="FromLayout">Create a Scene From a Layout Resource</h2> + +<p>You can create a {@link android.transition.Scene} instance directly from a layout resource +file. Use this technique when the view hierarchy in the file is mostly static. The resulting +scene represents the state of the view hierarchy at the time you created the +{@link android.transition.Scene} instance. If you change the view hierarchy, you have to +recreate the scene. The framework creates the scene from the entire view hierarchy in the +file; you can not create a scene from part of a layout file.</p> + +<p>To create a {@link android.transition.Scene} instance from a layout resource file, retrieve +the scene root from your layout as a {@link android.view.ViewGroup} instance and then call the +{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method with the +scene root and the resource ID of the layout file that contains the view hierarchy for the +scene.</p> + +<h3>Define Layouts for Scenes</h3> + +<p>The code snippets in the rest of this section show you how to create two different scenes +with the same scene root element. The snippets also demonstrate that you can load multiple +unrelated {@link android.transition.Scene} objects without implying that they are related to +each other.</p> + +<p>The example consists of the following layout definitions:</p> + +<ul> +<li>The main layout of an activity with a text label and a child layout.</li> +<li>A relative layout for the first scene with two text fields.</li> +<li>A relative layout for the second scene with the same two text fields in different order.</li> +</ul> + +<p>The example is designed so that all of the animation occurs within the child layout of the +main layout for the activity. The text label in the main layout remains static.</p> + +<p>The main layout for the activity is defined as follows:</p> + +<p class="code-caption">res/layout/activity_main.xml</p> + +<pre> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/master_layout"> + <TextView + android:id="@+id/title" + ... + android:text="Title"/> + <FrameLayout + android:id="@+id/scene_root"> + <include layout="@layout/a_scene" /> + </FrameLayout> +</LinearLayout> +</pre> + +<p>This layout definition contains a text field and a child layout for the scene root. The +layout for the first scene is included in the main layout file. This allows the app to display +it as part of the initial user interface and also to load it into a scene, since the framework +can load only a whole layout file into a scene.</p> + +<p>The layout for the first scene is defined as follows:</p> + +<p class="code-caption">res/layout/a_scene.xml</p> + +<pre> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/scene_container" + android:layout_width="match_parent" + android:layout_height="match_parent" > + <TextView + android:id="@+id/text_view1 + android:text="Text Line 1" /> + <TextView + android:id="@+id/text_view2 + android:text="Text Line 2" /> +</RelativeLayout> +</pre> + +<p>The layout for the second scene contains the same two text fields (with the same IDs) +placed in a different order and is defined as follows:</p> + +<p class="code-caption">res/layout/another_scene.xml</p> + +<pre> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/scene_container" + android:layout_width="match_parent" + android:layout_height="match_parent" > + <TextView + android:id="@+id/text_view2 + android:text="Text Line 2" /> + <TextView + android:id="@+id/text_view1 + android:text="Text Line 1" /> +</RelativeLayout> +</pre> + +<h3>Generate Scenes from Layouts</h3> + +<p>After you create definitions for the two relative layouts, you can obtain an scene for +each of them. This enables you to later transition between the two UI configurations. +To obtain a scene, you need a reference to the scene root and the layout resource ID.</p> + +<p>The following code snippet shows you how to get a reference to the scene root and create +two {@link android.transition.Scene} objects from the layout files:</p> + +<pre> +Scene mAScene; +Scene mAnotherScene; + +// Create the scene root for the scenes in this app +mSceneRoot = (ViewGroup) findViewById(R.id.scene_root); + +// Create the scenes +mAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this); +mAnotherScene = + Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this); +</pre> + +<p>In the app, there are now two {@link android.transition.Scene} objects based on view +hierarchies. Both scenes use the scene root defined by the +{@link android.widget.FrameLayout} element in <code>res/layout/activity_main.xml</code>.</p> + + + +<h2 id="FromCode">Create a Scene in Your Code</h2> + +<p>You can also create a {@link android.transition.Scene} instance in your code from a +{@link android.view.ViewGroup} object. Use this technique when you modify the view hierarchies +directly in your code or when you generate them dynamically.</p> + +<p>To create a scene from a view hierarchy in your code, use the +{@link android.transition.Scene#Scene(android.view.ViewGroup, android.view.View) Scene(sceneRoot, viewHierarchy)} +constructor. Calling this constructor is equivalent to calling the +{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method when you +have already inflated a layout file.</p> + +<p>The following code snippet demonstrates how to create a {@link android.transition.Scene} +instance from the scene root element and the view hierarchy for the scene in your code:</p> + +<pre> +Scene mScene; + +// Obtain the scene root element +mSceneRoot = (ViewGroup) mSomeLayoutElement; + +// Obtain the view hierarchy to add as a child of +// the scene root when this scene is entered +mViewHierarchy = (ViewGroup) someOtherLayoutElement; + +// Create a scene +mScene = new Scene(mSceneRoot, mViewHierarchy); +</pre> + + + +<h2 id="Actions">Create Scene Actions</h2> + +<p>The framework enables you to define custom scene actions that the system runs when entering +or exiting a scene. In many cases, defining custom scene actions is not necessary, since the +framework animates the change between scenes automatically.</p> + +<p>Scene actions are useful for handling these cases:</p> + +<ul> +<li>Animate views that are not in the same hierarchy. You can animate views for both the +starting and ending scenes using exit and entry scene actions.</li> +<li>Animate views that the transitions framework cannot animate automatically, such as +{@link android.widget.ListView} objects. For more information, see +<a href="{@docRoot}training/transitions/overview.html#Limitations">Limitations</a>.</li> +</ul> + +<p>To provide custom scene actions, define your actions as {@link java.lang.Runnable} objects +and pass them to the {@link android.transition.Scene#setExitAction Scene.setExitAction()} or +{@link android.transition.Scene#setEnterAction Scene.setEnterAction()} methods. The framework +calls the {@link android.transition.Scene#setExitAction setExitAction()} method on the starting +scene before running the transition animation and the {@link +android.transition.Scene#setEnterAction setEnterAction()} method on the ending scene after +running the transition animation.</p> + +<p class="note"><strong>Note:</strong> Do not use scene actions to pass data between views in +the starting and ending scenes. For more information, see +<a href="{@docRoot}training/transitions/transitions.html#Callbacks">Defining Transition +Lifecycle Callbacks</a>.</p> diff --git a/docs/html/training/transitions/transitions.jd b/docs/html/training/transitions/transitions.jd new file mode 100644 index 000000000000..489e291c79e0 --- /dev/null +++ b/docs/html/training/transitions/transitions.jd @@ -0,0 +1,315 @@ +page.title=Applying a Transition + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#Create">Create a Transition</a></li> + <li><a href="#Apply">Apply a Transition</a></li> + <li><a href="#Targets">Choose Specific Target Views</a></li> + <li><a href="#Multiple">Specify Multiple Transitions</a></li> + <li><a href="#NoScenes">Apply a Transition Without Scenes</a></li> + <li><a href="#Callbacks">Define Transition Lifecycle Callbacks</a></li> +</ol> +</div> +</div> + +<p>In the transitions framework, animations create a series of frames that depict a change +between the view hierarchies in the starting and ending scenes. The framework represents +these animations as transition objects, which contain information about an animation. To +run an animation, you provide the transition to use and the ending scene to a transition +manager.</p> + +<p>This lesson teaches you run an animation between two scenes using built-in transitions +to move, resize, and fade views. The next lesson shows you how to define custom transitions.</p> + + + +<h2 id="Create">Create a Transition</h2> + +<p>In the previous lesson, you learned how to create scenes that represent the state of +different view hierarchies. Once you have defined the starting scene and the ending scene you +want to change between, you need to create a {@link android.transition.Transition} object +that defines an animation. The framework enables you to specify a built-in transition in a +resource file and inflate it in your code or to create an instance of a built-in transition +directly in your code.</p> + +<!-- Built in transition table --> +<p class="table-caption" id="table1"><strong>Table 1.</strong> Built-in transition types.</p> +<table> +<tr> + <th scope="col">Class</th> + <th scope="col">Tag</th> + <th scope="col">Attributes</th> + <th scope="col">Effect</th> +</tr> +<tr> + <td><code><a href="/reference/android/transition/AutoTransition.html">AutoTransition</a></code></td> + <td><autoTransition/></td> + <td style="text-align=center;"> - </td> + <td>Default transition. Fade out, move and resize, and fade in views, in that order.</td> +</tr> +<tr> + <td><code><a href="/reference/android/transition/Fade.html">Fade</a></code></td> + <td><fade/></td> + <td><code>android:fadingMode="[fade_in |<br> fade_out |<br> fade_in_out]"</code></td> + <td> + <code>fade_in</code> fades in views<br> + <code>fade_out</code> fades out views<br> + <code>fade_in_out</code> (default) does a <code>fade_out</code> followed by a <code>fade_in</code>. + </td> +</tr> +<tr> + <td><code><a href="/reference/android/transition/ChangeBounds.html">ChangeBounds</a></code></td> + <td><changeBounds/></td> + <td style="text-align=center;"> - </td> + <td>Moves and resizes views.</td> +</tr> +</table> + + +<h3 id="FromFile">Create a transition instance from a resource file</h3> + +<p>This technique enables you to modify your transition definition without having to change +the code of your activity. This technique is also useful to separate complex transition +definitions from your application code, as shown in <a href="#Multiple">Specify Multiple +Transitions</a>.</p> + +<p>To specify a built-in transition in a resource file, follow these steps:</p> + +<ol> +<li>Add the <code>res/transition/</code> directory to your project.</li> +<li>Create a new XML resource file inside this directory.</li> +<li>Add an XML node for one of the built-in transitions.</li> +</ol> + +<p>For example, the following resource file specifies the {@link android.transition.Fade} +transition:</p> + +<p class="code-caption">res/transition/fade_transition.xml</p> + +<pre> +<fade xmlns:android="http://schemas.android.com/apk/res/android" /> +</pre> + +<p>The following code snippet shows how to inflate a {@link android.transition.Transition} +instance inside your activity from a resource file:</p> + +<pre> +Transition mFadeTransition = + TransitionInflater.from(this). + inflateTransition(R.transition.fade_transition); +</pre> + + +<h3 id="FromCode">Create a transition instance in your code</h3> + +<p>This technique is useful for creating transition objects dynamically if you modify the user +interface in your code, and to create simple built-in transition instances with few or +no parameters.</p> + +<p>To create an instance of a built-in transition, invoke one of the public constructors in +the subclasses of the {@link android.transition.Transition} class. For example, the following +code snippet creates an instance of the {@link android.transition.Fade} transition:</p> + +<pre> +Transition mFadeTransition = new Fade(); +</pre> + + + +<h2 id="Apply">Apply a Transition</h2> + +<p>You typically apply a transition to change between different view hierarchies in response +to an event, such as a user action. For example, consider a search app: when the user enters +a search term and clicks the search button, the app changes to the scene that represents the +results layout while applying a transition that fades out the search button and fades in the +search results.</p> + +<p>To make a scene change while applying a transition in response to some event in your +activity, call the {@link android.transition.TransitionManager#go TransitionManager.go()} +static method with the ending scene and the transition instance to use for the animation, +as shown in the following snippet:</p> + +<pre> +TransitionManager.go(mEndingScene, mFadeTransition); +</pre> + +<p>The framework changes the view hierarchy inside the scene root with the view hierarchy +from the ending scene while running the animation specified by the transition instance. The +starting scene is the ending scene from the last transition. If there was no previous +transition, the starting scene is determined automatically from the current state of the +user interface.</p> + +<p>If you do not specify a transition instance, the transition manager can apply an automatic +transition that does something reasonable for most situations. For more information, see the +API reference for the {@link android.transition.TransitionManager} class.</p> + + + +<h2 id="Targets">Choose Specific Target Views</h2> + +<p>The framework applies transitions to all views in the starting and ending scenes by +default. In some cases, you may only want to apply an animation to a subset of views in a +scene. For example, the framework does not support animating changes to +{@link android.widget.ListView} objects, so you should not try to animate them during a +transition. The framework enables you to select specific views you want to animate.</p> + +<p>Each view that the transition animates is called a <em>target</em>. You can only +select targets that are part of the view hierarchy associated with a scene.</p> + +<p>To remove one or more views from the list of targets, call the {@link +android.transition.Transition#removeTarget removeTarget()} method before starting +the transition. To add only the views you specify to the list of targets, call the +{@link android.transition.Transition#addTarget addTarget()} method. For more +information, see the API reference for the {@link android.transition.Transition} class.</p> + + + +<h2 id="Multiple">Specify Multiple Transitions</h2> + +<p>To get the most impact from an animation, you should match it to the type of changes +that occur between the scenes. For example, if you are removing some views and adding others +between scenes, a fade out/fade in animation provides a noticeable indication that some views +are no longer available. If you are moving views to different points on the screen, a better +choice would be to animate the movement so that users notice the new location of the views.</p> + +<p>You do not have to choose only one animation, since the transitions framework enables you +to combine animation effects in a transition set that contains a group of individual built-in +or custom transitions.</p> + +<p>To define a transition set from a collection of transitions in XML, create a resource file +in the <code>res/transitions/</code> directory and list the transitions under the +<code>transitionSet</code> element. For example, the following snippet shows how to specify a +transition set that has the same behaviour as the {@link android.transition.AutoTransition} +class:</p> + +<pre> +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" + android:transitionOrdering="sequential"> + <fade android:fadingMode="fade_out" /> + <changeBounds /> + <fade android:fadingMode="fade_in" /> +</transitionSet> +</pre> + +<p>To inflate the transition set into a {@link android.transition.TransitionSet} object in +your code, call the {@link android.transition.TransitionInflater#from TransitionInflater.from()} +method in your activity. The {@link android.transition.TransitionSet} class extends from the +{@link android.transition.Transition} class, so you can use it with a transition manager just +like any other {@link android.transition.Transition} instance.</p> + + + +<h2 id="NoScenes">Apply a Transition Without Scenes</h2> + +<p>Changing view hierarchies is not the only way to modify your user interface. You can also +make changes by adding, modifying, and removing child views within the current hierarchy. For +example, you can implement a search interaction with just a single layout. Start with the +layout showing a search entry field and a search icon. To change the user interface to show +the results, remove the search button when the user clicks it by calling the {@link +android.view.ViewGroup#removeView ViewGroup.removeView()} method, and add the search results by +calling {@link android.view.ViewGroup#addView ViewGroup.addView()} method.</p> + +<p>You may want to use this approach if the alternative is to have two hierarchies that are +nearly identical. Rather than having to create and maintain two separate layout files for a +minor difference in the user interface, you can have one layout file containing a view +hierarchy that you modify in code.</p> + +<p>If you make changes within the current view hierarchy in this fashion, you do not need to +create a scene. Instead, you can create and apply a transition between two states of a view +hierarchy using a <em>delayed transition</em>. This feature of the transitions framework +starts with the current view hierarchy state, records changes you make to its views, and applies +a transition that animates the changes when the system redraws the user interface.</p> + +<p>To create a delayed transition within a single view hierarchy, follow these steps:</p> + +<ol> +<li>When the event that triggers the transition occurs, call the {@link +android.transition.TransitionManager#beginDelayedTransition +TransitionManager.beginDelayedTransition()} method providing the parent view of all the views +you want to change and the transition to use. The framework stores the current state of the +child views and their property values.</li> +<li>Make changes to the child views as required by your use case. The framework records +the changes you make to the child views and their properties.</li> +<li>When the system redraws the user interface according to your changes, the framework +animates the changes between the original state and the new state.</li> +</ol> + +<p>The following example shows how to animate the addition of a text view to a view hierarchy +using a delayed transition. The first snippet shows the layout definition file:</p> + +<p class="code-caption">res/layout/activity_main.xml</p> + +<pre> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/mainLayout" + android:layout_width="match_parent" + android:layout_height="match_parent" > + <EditText + android:id="@+id/inputText" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + ... +</RelativeLayout> +</pre> + +<p>The next snippet shows the code that animates the addition of the text view:</p> + +<p class="code-caption">MainActivity.java</p> + +<pre> +private TextView mLabelText; +private Fade mFade; +private ViewGroup mRootView; +... + +// Load the layout +this.setContentView(R.layout.activity_main); +... + +// Create a new TextView and set some View properties +mLabelText = new TextView(); +mLabelText.setText("Label").setId("1"); + +// Get the root view and create a transition +mRootView = (ViewGroup) findViewById(R.id.mainLayout); +mFade = new Fade(IN); + +// Start recording changes to the view hierarchy +TransitionManager.beginDelayedTransition(mRootView, mFade); + +// Add the new TextView to the view hierarchy +mRootView.addView(mLabelText); + +// When the system redraws the screen to show this update, +// the framework will animate the addition as a fade in +</pre> + + + +<h2 id="Callbacks">Define Transition Lifecycle Callbacks</h2> + +<p>The transition lifecycle is similar to the activity lifecycle. It represents the transition +states that the framework monitors during the time between a call to the {@link +android.transition.TransitionManager#go TransitionManager.go()} method and the completion of +the animation. At important lifecycle states, the framework invokes callbacks defined by +the {@link android.transition.Transition.TransitionListener TransitionListener} +interface.</p> + +<p>Transition lifecycle callbacks are useful, for example, for copying a view property value +from the starting view hierarchy to the ending view hierarchy during a scene change. You +cannot simply copy the value from its starting view to the view in the ending view hierarchy, +because the ending view hierarchy is not inflated until the transition is completed. +Instead, you need to store the value in a variable and then copy it into the ending view +hierarchy when the framework has finished the transition. To get notified when the transition +is completed, you can implement the {@link +android.transition.Transition.TransitionListener#onTransitionEnd +TransitionListener.onTransitionEnd()} method in your activity.</p> + +<p>For more information, see the API reference for the {@link +android.transition.Transition.TransitionListener TransitionListener} class.</p> diff --git a/docs/html/training/wearables/data-layer/data-items.jd b/docs/html/training/wearables/data-layer/data-items.jd index 12babbfa4082..49a8d32f9792 100644 --- a/docs/html/training/wearables/data-layer/data-items.jd +++ b/docs/html/training/wearables/data-layer/data-items.jd @@ -46,7 +46,7 @@ directly. Instead, you: </ol> <p> -However, instead of working with raw bytes using <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])">setData()</a>, +However, instead of working with raw bytes using <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])"><code>setData()</code></a>, we recommend you <a href="#SyncData">use a data map</a>, which exposes a data item in an easy-to-use {@link android.os.Bundle}-like interface. </p> @@ -88,39 +88,121 @@ app, you should create a path scheme that matches the structure of the data. </li> </ol> -<p>The following example shows how to create a data map and put data on it:</p> +<p>The <code>increaseCounter()</code> method in the following example shows how to create a +data map and put data in it:</p> <pre> -PutDataMapRequest dataMap = PutDataMapRequest.create("/count"); -dataMap.getDataMap().putInt(COUNT_KEY, count++); -PutDataRequest request = dataMap.asPutDataRequest(); -PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi - .putDataItem(mGoogleApiClient, request); +public class MainActivity extends Activity implements + DataApi.DataListener, + GoogleApiClient.ConnectionCallbacks, + GoogleApiClient.OnConnectionFailedListener { + + private static final String COUNT_KEY = "com.example.key.count"; + + private GoogleApiClient mGoogleApiClient; + private int count = 0; + + ... + + // Create a data map and put data in it + private void <strong>increaseCounter</strong>() { + PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/count"); + putDataMapReq.getDataMap().putInt(COUNT_KEY, count++); + PutDataRequest putDataReq = putDataMapReq.asPutDataRequest(); + PendingResult<DataApi.DataItemResult> pendingResult = + Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq); + } + + ... +} </pre> +<p>For more information about handling the +<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"> +<code>PendingResult</code></a> object, see +<a href="{@docRoot}training/wearables/data-layer/events.html#Wait">Wait for the Status of Data +Layer Calls</a>.</p> + + <h2 id="ListenEvents">Listen for Data Item Events</h2> -If one side of the data layer connection changes a data item, you probably want + +<p>If one side of the data layer connection changes a data item, you probably want to be notified of any changes on the other side of the connection. -You can do this by implementing a listener for data item events. +You can do this by implementing a listener for data item events.</p> -<p>For example, here's what a typical callback looks like to carry out certain actions -when data changes:</p> +<p>The code snippet in the following example notifies your app when the value of the +counter defined in the previous example changes:</p> <pre> -@Override -public void onDataChanged(DataEventBuffer dataEvents) { - for (DataEvent event : dataEvents) { - if (event.getType() == DataEvent.TYPE_DELETED) { - Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri()); - } else if (event.getType() == DataEvent.TYPE_CHANGED) { - Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri()); +public class MainActivity extends Activity implements + DataApi.DataListener, + GoogleApiClient.ConnectionCallbacks, + GoogleApiClient.OnConnectionFailedListener { + + private static final String COUNT_KEY = "com.example.key.count"; + + private GoogleApiClient mGoogleApiClient; + private int count = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + mGoogleApiClient = new GoogleApiClient.Builder(this) + .addApi(Wearable.API) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .build(); + } + + @Override + protected void onResume() { + super.onStart(); + mGoogleApiClient.connect(); + } + + @Override + public void onConnected(Bundle bundle) { + <strong>Wearable.DataApi.addListener</strong>(mGoogleApiClient, this); + } + + @Override + protected void onPause() { + super.onPause(); + <strong>Wearable.DataApi.removeListener</strong>(mGoogleApiClient, this); + mGoogleApiClient.disconnect(); + } + + @Override + public void <strong>onDataChanged</strong>(DataEventBuffer dataEvents) { + for (DataEvent event : dataEvents) { + if (event.getType() == DataEvent.TYPE_CHANGED) { + // DataItem changed + DataItem item = event.getDataItem(); + if (item.getUri().getPath().compareTo("/count") == 0) { + DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap(); + updateCount(dataMap.getInt(COUNT_KEY)); + } + } else if (event.getType() == DataEvent.TYPE_DELETED) { + // DataItem deleted + } } } + + // Our method to update the count + private void updateCount(int c) { ... } + + ... } </pre> -<p> -This is just a snippet that requires more implementation details. Learn about -how to implement a full listener service or activity in + +<p>This activity implements the +<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"> +<code>DataItem.DataListener</code></a> interface. This activity adds itself as a listener +for data item events inside the <code>onConnected()</code> method and removes the listener +in the <code>onPause()</code> method.</p> + +<p>You can also implement the listener as a service. For more information, see <a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listen for Data Layer -Events</a>. -</p>
\ No newline at end of file +Events</a>.</p> diff --git a/docs/html/training/wearables/data-layer/events.jd b/docs/html/training/wearables/data-layer/events.jd index 6a3949a6fc24..c797f6882fff 100644 --- a/docs/html/training/wearables/data-layer/events.jd +++ b/docs/html/training/wearables/data-layer/events.jd @@ -267,6 +267,8 @@ or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#rem public class MainActivity extends Activity implements DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener { + private GoogleApiClient mGoogleApiClient; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -314,4 +316,5 @@ public class MainActivity extends Activity implements } } } +} </pre> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 3f79c2daab5d..72f6118bce05 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -304,7 +304,7 @@ public final class Bitmap implements Parcelable { * there are no more references to this bitmap. */ public void recycle() { - if (!mRecycled) { + if (!mRecycled && mFinalizer.mNativeBitmap != 0) { if (nativeRecycle(mNativeBitmap)) { // return value indicates whether native pixel object was actually recycled. // false indicates that it is still in use at the native level and these @@ -1571,7 +1571,7 @@ public final class Bitmap implements Parcelable { } private static class BitmapFinalizer { - private final long mNativeBitmap; + private long mNativeBitmap; // Native memory allocated for the duration of the Bitmap, // if pixel data allocated into native memory, instead of java byte[] @@ -1597,6 +1597,7 @@ public final class Bitmap implements Parcelable { VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount); } nativeDestructor(mNativeBitmap); + mNativeBitmap = 0; } } } diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index f2d85b4c9388..bf993f4f07c8 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -118,9 +118,9 @@ struct Res_png_9patch yDivsOffset(0), colorsOffset(0) { } int8_t wasDeserialized; - int8_t numXDivs; - int8_t numYDivs; - int8_t numColors; + uint8_t numXDivs; + uint8_t numYDivs; + uint8_t numColors; // The offset (from the start of this structure) to the xDivs & yDivs // array for this 9patch. To get a pointer to this array, call diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 532314d1bb11..38a5b404ae65 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -4714,7 +4714,7 @@ public class AudioService extends IAudioService.Stub { synchronized (mLastDeviceConnectMsgTime) { long time = SystemClock.uptimeMillis(); if (mLastDeviceConnectMsgTime > time) { - delay = (int)(mLastDeviceConnectMsgTime - time); + delay = (int)(mLastDeviceConnectMsgTime - time) + 30; } } } diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 13cdc693d0b4..5d9355aae00b 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -88,6 +88,10 @@ public class MtpDatabase { Files.FileColumns._ID, // 0 Files.FileColumns.DATA, // 1 }; + private static final String[] FORMAT_PROJECTION = new String[] { + Files.FileColumns._ID, // 0 + Files.FileColumns.FORMAT, // 1 + }; private static final String[] PATH_FORMAT_PROJECTION = new String[] { Files.FileColumns._ID, // 0 Files.FileColumns.DATA, // 1 @@ -597,6 +601,7 @@ public class MtpDatabase { MtpConstants.PROPERTY_PARENT_OBJECT, MtpConstants.PROPERTY_PERSISTENT_UID, MtpConstants.PROPERTY_NAME, + MtpConstants.PROPERTY_DISPLAY_NAME, MtpConstants.PROPERTY_DATE_ADDED, }; @@ -669,43 +674,6 @@ public class MtpDatabase { MtpConstants.PROPERTY_DESCRIPTION, }; - static final int[] ALL_PROPERTIES = { - // NOTE must match FILE_PROPERTIES above - MtpConstants.PROPERTY_STORAGE_ID, - MtpConstants.PROPERTY_OBJECT_FORMAT, - MtpConstants.PROPERTY_PROTECTION_STATUS, - MtpConstants.PROPERTY_OBJECT_SIZE, - MtpConstants.PROPERTY_OBJECT_FILE_NAME, - MtpConstants.PROPERTY_DATE_MODIFIED, - MtpConstants.PROPERTY_PARENT_OBJECT, - MtpConstants.PROPERTY_PERSISTENT_UID, - MtpConstants.PROPERTY_NAME, - MtpConstants.PROPERTY_DISPLAY_NAME, - MtpConstants.PROPERTY_DATE_ADDED, - - // image specific properties - MtpConstants.PROPERTY_DESCRIPTION, - - // audio specific properties - MtpConstants.PROPERTY_ARTIST, - MtpConstants.PROPERTY_ALBUM_NAME, - MtpConstants.PROPERTY_ALBUM_ARTIST, - MtpConstants.PROPERTY_TRACK, - MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE, - MtpConstants.PROPERTY_DURATION, - MtpConstants.PROPERTY_GENRE, - MtpConstants.PROPERTY_COMPOSER, - - // video specific properties - MtpConstants.PROPERTY_ARTIST, - MtpConstants.PROPERTY_ALBUM_NAME, - MtpConstants.PROPERTY_DURATION, - MtpConstants.PROPERTY_DESCRIPTION, - - // image specific properties - MtpConstants.PROPERTY_DESCRIPTION, - }; - private int[] getSupportedObjectProperties(int format) { switch (format) { case MtpConstants.FORMAT_MP3: @@ -723,8 +691,6 @@ public class MtpDatabase { case MtpConstants.FORMAT_PNG: case MtpConstants.FORMAT_BMP: return IMAGE_PROPERTIES; - case 0: - return ALL_PROPERTIES; default: return FILE_PROPERTIES; } @@ -749,6 +715,10 @@ public class MtpDatabase { MtpPropertyGroup propertyGroup; if (property == 0xFFFFFFFFL) { + if (format == 0 && handle > 0) { + // return properties based on the object's format + format = getObjectFormat((int)handle); + } propertyGroup = mPropertyGroupsByFormat.get(format); if (propertyGroup == null) { int[] propertyList = getSupportedObjectProperties(format); @@ -988,6 +958,26 @@ public class MtpDatabase { } } + private int getObjectFormat(int handle) { + Cursor c = null; + try { + c = mMediaProvider.query(mPackageName, mObjectsUri, FORMAT_PROJECTION, + ID_WHERE, new String[] { Integer.toString(handle) }, null, null); + if (c != null && c.moveToNext()) { + return c.getInt(1); + } else { + return -1; + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getObjectFilePath", e); + return -1; + } finally { + if (c != null) { + c.close(); + } + } + } + private int deleteFile(int handle) { mDatabaseModified = true; String path = null; diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java index 781988d1910a..c80adfa80fc9 100644 --- a/media/java/android/mtp/MtpPropertyGroup.java +++ b/media/java/android/mtp/MtpPropertyGroup.java @@ -172,6 +172,17 @@ class MtpPropertyGroup { column = Images.ImageColumns.DESCRIPTION; type = MtpConstants.TYPE_STR; break; + case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC: + case MtpConstants.PROPERTY_AUDIO_BITRATE: + case MtpConstants.PROPERTY_SAMPLE_RATE: + // these are special cased + type = MtpConstants.TYPE_UINT32; + break; + case MtpConstants.PROPERTY_BITRATE_TYPE: + case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS: + // these are special cased + type = MtpConstants.TYPE_UINT16; + break; default: type = MtpConstants.TYPE_UNDEFINED; Log.e(TAG, "unsupported property " + code); @@ -420,6 +431,17 @@ class MtpPropertyGroup { result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); } break; + case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC: + case MtpConstants.PROPERTY_AUDIO_BITRATE: + case MtpConstants.PROPERTY_SAMPLE_RATE: + // we don't have these in our database, so return 0 + result.append(handle, propertyCode, MtpConstants.TYPE_UINT32, 0); + break; + case MtpConstants.PROPERTY_BITRATE_TYPE: + case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS: + // we don't have these in our database, so return 0 + result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0); + break; default: if (property.type == MtpConstants.TYPE_STR) { result.append(handle, propertyCode, c.getString(column)); diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index b8e850a32bbb..8b2e2034bcef 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -930,6 +930,11 @@ static const PropertyTableEntry kObjectPropertyTable[] = { { MTP_PROPERTY_COMPOSER, MTP_TYPE_STR }, { MTP_PROPERTY_DURATION, MTP_TYPE_UINT32 }, { MTP_PROPERTY_DESCRIPTION, MTP_TYPE_STR }, + { MTP_PROPERTY_AUDIO_WAVE_CODEC, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_BITRATE_TYPE, MTP_TYPE_UINT16 }, + { MTP_PROPERTY_AUDIO_BITRATE, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_NUMBER_OF_CHANNELS,MTP_TYPE_UINT16 }, + { MTP_PROPERTY_SAMPLE_RATE, MTP_TYPE_UINT32 }, }; static const PropertyTableEntry kDevicePropertyTable[] = { diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java index c2bb90c612ce..0b7b99f4b150 100644 --- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java +++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java @@ -29,7 +29,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.storage.IMountService; import android.os.storage.StorageManager; -import android.util.Log; +import android.text.Editable; +import android.text.TextWatcher; import android.util.Slog; import android.view.View; import android.widget.Button; @@ -180,20 +181,6 @@ public class BackupRestoreConfirmation extends Activity { mEncPassword = (TextView) findViewById(R.id.enc_password); TextView curPwDesc = (TextView) findViewById(R.id.password_desc); - // We vary the password prompt depending on whether one is predefined, and whether - // the device is encrypted. - mIsEncrypted = deviceIsEncrypted(); - if (!haveBackupPassword()) { - curPwDesc.setVisibility(View.GONE); - mCurPassword.setVisibility(View.GONE); - if (layoutId == R.layout.confirm_backup) { - TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc); - encPwDesc.setText(mIsEncrypted - ? R.string.backup_enc_password_required - : R.string.backup_enc_password_optional); - } - } - mAllowButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -209,6 +196,7 @@ public class BackupRestoreConfirmation extends Activity { sendAcknowledgement(mToken, false, mObserver); mAllowButton.setEnabled(false); mDenyButton.setEnabled(false); + finish(); } }); @@ -218,6 +206,39 @@ public class BackupRestoreConfirmation extends Activity { mAllowButton.setEnabled(!mDidAcknowledge); mDenyButton.setEnabled(!mDidAcknowledge); } + + // We vary the password prompt depending on whether one is predefined, and whether + // the device is encrypted. + mIsEncrypted = deviceIsEncrypted(); + if (!haveBackupPassword()) { + curPwDesc.setVisibility(View.GONE); + mCurPassword.setVisibility(View.GONE); + if (layoutId == R.layout.confirm_backup) { + TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc); + if (mIsEncrypted) { + encPwDesc.setText(R.string.backup_enc_password_required); + monitorEncryptionPassword(); + } else { + encPwDesc.setText(R.string.backup_enc_password_optional); + } + } + } + } + + private void monitorEncryptionPassword() { + mAllowButton.setEnabled(false); + mEncPassword.addTextChangedListener(new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } + + @Override + public void afterTextChanged(Editable s) { + mAllowButton.setEnabled(mEncPassword.getText().length() > 0); + } + }); } // Preserve the restore observer callback binder across activity relaunch diff --git a/packages/PrintSpooler/res/values-ca/arrays.xml b/packages/PrintSpooler/res/values-ca/arrays.xml new file mode 100644 index 000000000000..c1b149c59960 --- /dev/null +++ b/packages/PrintSpooler/res/values-ca/arrays.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<resources> + + <string-array name="pdf_printer_media_sizes" translatable="false"> + <item>NA_LETTER</item> + <item>NA_GOVT_LETTER</item> + <item>NA_LEGAL</item> + <item>NA_JUNIOR_LEGAL</item> + <item>NA_LEDGER</item> + <item>NA_TABLOID</item> + <item>NA_INDEX_3X5</item> + <item>NA_INDEX_4X6</item> + <item>NA_INDEX_5X8</item> + <item>NA_MONARCH</item> + <item>NA_QUARTO</item> + <item>NA_FOOLSCAP</item> + </string-array> + +</resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 5b18b24be49e..2b665c30b8d2 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -255,7 +255,7 @@ <integer name="doze_pickup_vibration_threshold">2000</integer> <!-- Doze: can we assume the pickup sensor includes a proximity check? --> - <bool name="doze_pickup_performs_proximity_check">true</bool> + <bool name="doze_pickup_performs_proximity_check">false</bool> <!-- Doze: pulse parameter - how long does it take to fade in? --> <integer name="doze_pulse_duration_in">900</integer> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index ddb96a2a1739..30f92b97346a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -97,9 +97,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { state.icon = ResourceIcon.get(iconId); state.isOverlayIconWide = cb.isDataTypeIconWide; state.autoMirrorDrawable = !cb.noSim; - state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiConnected - ? cb.dataTypeIconId - : 0; + state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) ? cb.dataTypeIconId : 0; state.filter = iconId != R.drawable.ic_qs_no_sim; state.activityIn = cb.enabled && cb.activityIn; state.activityOut = cb.enabled && cb.activityOut; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 6d38d38816a0..9a7f21ea130b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -1187,10 +1187,13 @@ public class NetworkControllerImpl extends BroadcastReceiver String contentDescription = getStringIfExists(getContentDescription()); String dataContentDescription = getStringIfExists(icons.mDataContentDescription); + + boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0 + || mCurrentState.iconGroup == TelephonyIcons.ROAMING; + // Only send data sim callbacks to QS. if (mCurrentState.dataSim) { - int qsTypeIcon = mCurrentState.dataConnected ? - icons.mQsDataType[mCurrentState.inetForNetwork] : 0; + int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0; int length = mSignalsChangedCallbacks.size(); for (int i = 0; i < length; i++) { mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled @@ -1205,8 +1208,6 @@ public class NetworkControllerImpl extends BroadcastReceiver icons.mIsWide && qsTypeIcon != 0); } } - boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0 - || mCurrentState.iconGroup == TelephonyIcons.ROAMING; int typeIcon = showDataIcon ? icons.mDataType : 0; int signalClustersLength = mSignalClusters.size(); for (int i = 0; i < signalClustersLength; i++) { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 1f9867008ba3..f5d43d84f609 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -894,7 +894,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } void doInvalidatePanelMenu(int featureId) { - PanelFeatureState st = getPanelState(featureId, true); + PanelFeatureState st = getPanelState(featureId, false); + if (st == null) { + return; + } Bundle savedActionViewStates = null; if (st.menu != null) { savedActionViewStates = new Bundle(); @@ -933,8 +936,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // The panel key was pushed, so set the chording key mPanelChordingKey = keyCode; - PanelFeatureState st = getPanelState(featureId, true); - if (!st.isOpen) { + PanelFeatureState st = getPanelState(featureId, false); + if (st != null && !st.isOpen) { return preparePanel(st, event); } } @@ -952,12 +955,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mPanelChordingKey != 0) { mPanelChordingKey = 0; - if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null)) { + final PanelFeatureState st = getPanelState(featureId, false); + + if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null) || + (st == null)) { return; } boolean playSoundEffect = false; - final PanelFeatureState st = getPanelState(featureId, true); if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() && !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) { @@ -1056,7 +1061,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { - return performPanelShortcut(getPanelState(featureId, true), keyCode, event, flags); + return performPanelShortcut(getPanelState(featureId, false), keyCode, event, flags); } private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event, @@ -1149,11 +1154,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mInvalidatePanelMenuRunnable.run(); } - final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); + final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); // If we don't have a menu or we're waiting for a full content refresh, // forget it. This is a lingering event that no longer matters. - if (st.menu != null && !st.refreshMenuContent && + if (st != null && st.menu != null && !st.refreshMenuContent && cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) { cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu); mDecorContentParent.showOverflowMenu(); @@ -1161,15 +1166,19 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } else { mDecorContentParent.hideOverflowMenu(); - if (cb != null && !isDestroyed()) { - final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); + final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); + if (st != null && cb != null && !isDestroyed()) { cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu); } } return; } - PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); + PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); + + if (st == null) { + return; + } // Save the future expanded mode state since closePanel will reset it boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode; @@ -2302,8 +2311,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // combination such as Control+C. Temporarily prepare the panel then mark it // unprepared again when finished to ensure that the panel will again be prepared // the next time it is shown for real. - if (mPreparedPanel == null) { - PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); + PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); + if (st != null && mPreparedPanel == null) { preparePanel(st, ev); handled = performPanelShortcut(st, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE); @@ -3156,7 +3165,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // If the user is chording a menu shortcut, release the chord since // this window lost focus - if (!hasWindowFocus && mPanelChordingKey != 0) { + if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) { closePanel(FEATURE_OPTIONS_PANEL); } @@ -3906,8 +3915,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public boolean isShortcutKey(int keyCode, KeyEvent event) { - PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); - return st.menu != null && st.menu.isShortcutKey(keyCode, event); + PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); + return st != null && st.menu != null && st.menu.isShortcutKey(keyCode, event); } private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) { diff --git a/rs/java/android/renderscript/ScriptIntrinsicHistogram.java b/rs/java/android/renderscript/ScriptIntrinsicHistogram.java index 4ecac99bea3c..f8c0c16b8d15 100644 --- a/rs/java/android/renderscript/ScriptIntrinsicHistogram.java +++ b/rs/java/android/renderscript/ScriptIntrinsicHistogram.java @@ -92,8 +92,10 @@ public final class ScriptIntrinsicHistogram extends ScriptIntrinsic { "Input vector size must be >= output vector size."); } if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) && + !ain.getType().getElement().isCompatible(Element.U8_2(mRS)) && + !ain.getType().getElement().isCompatible(Element.U8_3(mRS)) && !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) { - throw new RSIllegalArgumentException("Input type must be U8 or U8_4."); + throw new RSIllegalArgumentException("Input type must be U8, U8_1, U8_2 or U8_4."); } forEach(0, ain, null, null, opt); @@ -188,8 +190,10 @@ public final class ScriptIntrinsicHistogram extends ScriptIntrinsic { throw new RSIllegalArgumentException("Output vector size must be one."); } if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) && + !ain.getType().getElement().isCompatible(Element.U8_2(mRS)) && + !ain.getType().getElement().isCompatible(Element.U8_3(mRS)) && !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) { - throw new RSIllegalArgumentException("Input type must be U8 or U8_4."); + throw new RSIllegalArgumentException("Input type must be U8, U8_1, U8_2 or U8_4."); } forEach(1, ain, null, null, opt); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 5ae26ef58eba..bbf3644a9aaa 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -765,16 +765,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } /** - * Gets the bounds of the active window. + * Gets the bounds of a window. * * @param outBounds The output to which to write the bounds. */ - boolean getActiveWindowBounds(Rect outBounds) { - // TODO: This should be refactored to work with accessibility - // focus in multiple windows. + boolean getWindowBounds(int windowId, Rect outBounds) { IBinder token; synchronized (mLock) { - final int windowId = mSecurityPolicy.mActiveWindowId; token = mGlobalWindowTokens.get(windowId); if (token == null) { token = getCurrentUserStateLocked().mWindowTokens.get(windowId); @@ -3255,7 +3252,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // Make sure the point is within the window. Rect windowBounds = mTempRect; - getActiveWindowBounds(windowBounds); + getWindowBounds(focus.getWindowId(), windowBounds); if (!windowBounds.contains(point.x, point.y)) { return false; } diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index fddb54e138e2..d6abce9584ec 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -55,7 +55,7 @@ public class NetworkTimeUpdateService { private static final int EVENT_AUTO_TIME_CHANGED = 1; private static final int EVENT_POLL_NETWORK_TIME = 2; - private static final int EVENT_NETWORK_CONNECTED = 3; + private static final int EVENT_NETWORK_CHANGED = 3; private static final String ACTION_POLL = "com.android.server.NetworkTimeUpdateService.action.POLL"; @@ -248,18 +248,8 @@ public class NetworkTimeUpdateService { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { - // There is connectivity - final ConnectivityManager connManager = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo netInfo = connManager.getActiveNetworkInfo(); - if (netInfo != null) { - // Verify that it's a WIFI connection - if (netInfo.getState() == NetworkInfo.State.CONNECTED && - (netInfo.getType() == ConnectivityManager.TYPE_WIFI || - netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) { - mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget(); - } - } + // Don't bother checking if we have connectivity, NtpTrustedTime does that for us. + mHandler.obtainMessage(EVENT_NETWORK_CHANGED).sendToTarget(); } } }; @@ -276,7 +266,7 @@ public class NetworkTimeUpdateService { switch (msg.what) { case EVENT_AUTO_TIME_CHANGED: case EVENT_POLL_NETWORK_TIME: - case EVENT_NETWORK_CONNECTED: + case EVENT_NETWORK_CHANGED: onPollNetworkTime(msg.what); break; } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 6d28382e29f2..aefbf602ef7a 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -72,7 +72,7 @@ public final class ActiveServices { static final boolean DEBUG_DELAYED_SERVICE = ActivityManagerService.DEBUG_SERVICE; static final boolean DEBUG_DELAYED_STARTS = DEBUG_DELAYED_SERVICE; static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU; - static final boolean LOG_SERVICE_START_STOP = true; + static final boolean LOG_SERVICE_START_STOP = false; static final String TAG = ActivityManagerService.TAG; static final String TAG_MU = ActivityManagerService.TAG_MU; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 40bcf8edd4c7..e8f37572b079 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2358,7 +2358,7 @@ public final class ActivityManagerService extends ActivityManagerNative return mAppBindArgs; } - final void setFocusedActivityLocked(ActivityRecord r) { + final void setFocusedActivityLocked(ActivityRecord r, String reason) { if (mFocusedActivity != r) { if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivityLocked: r=" + r); mFocusedActivity = r; @@ -2367,7 +2367,7 @@ public final class ActivityManagerService extends ActivityManagerNative } else { finishRunningVoiceLocked(); } - mStackSupervisor.setFocusedStack(r); + mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity"); if (r != null) { mWindowManager.setFocusedApp(r.appToken, true); } @@ -2391,7 +2391,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (stack != null) { ActivityRecord r = stack.topRunningActivityLocked(null); if (r != null) { - setFocusedActivityLocked(r); + setFocusedActivityLocked(r, "setFocusedStack"); } } } @@ -2435,7 +2435,7 @@ public final class ActivityManagerService extends ActivityManagerNative mHandler.sendMessage(msg); } - private final int updateLruProcessInternalLocked(ProcessRecord app, long now, int index, + private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index, String what, Object obj, ProcessRecord srcApp) { app.lastActivityTime = now; @@ -3109,7 +3109,7 @@ public final class ActivityManagerService extends ActivityManagerNative return intent; } - boolean startHomeActivityLocked(int userId) { + boolean startHomeActivityLocked(int userId, String reason) { if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopAction == null) { // We are running in factory test mode, but unable to find @@ -3131,7 +3131,7 @@ public final class ActivityManagerService extends ActivityManagerNative aInfo.applicationInfo.uid, true); if (app == null || app.instrumentationClass == null) { intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); - mStackSupervisor.startHomeActivity(intent, aInfo); + mStackSupervisor.startHomeActivity(intent, aInfo, reason); } } @@ -6203,7 +6203,7 @@ public final class ActivityManagerService extends ActivityManagerNative startProcessLocked(procs.get(ip), "on-hold", null); } } - + if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { // Start looking for apps that are abusing wake locks. Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); @@ -6343,7 +6343,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { - stack.activityDestroyedLocked(token); + stack.activityDestroyedLocked(token, "activityDestroyed"); } } } @@ -6454,7 +6454,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("File descriptors passed in options"); } } - + synchronized(this) { int callingUid = Binder.getCallingUid(); int origUserId = userId; @@ -6484,7 +6484,7 @@ public final class ActivityManagerService extends ActivityManagerNative return getIntentSenderLocked(type, packageName, callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags, options); - + } catch (RemoteException e) { throw new SecurityException(e); } @@ -8536,7 +8536,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (prev != null && prev.isRecentsActivity()) { task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE); } - mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options); + mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront"); } finally { Binder.restoreCallingIdentity(origId); } @@ -8565,7 +8565,7 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); try { - stack.moveTaskToBackLocked(taskId, null); + stack.moveTaskToBackLocked(taskId); } finally { Binder.restoreCallingIdentity(origId); } @@ -8595,7 +8595,7 @@ public final class ActivityManagerService extends ActivityManagerNative mStackSupervisor.showLockTaskToast(); return false; } - return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId, null); + return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId); } } finally { Binder.restoreCallingIdentity(origId); @@ -8687,7 +8687,7 @@ public final class ActivityManagerService extends ActivityManagerNative try { if (DEBUG_STACK) Slog.d(TAG, "moveTaskToStack: moving task=" + taskId + " to stackId=" + stackId + " toTop=" + toTop); - mStackSupervisor.moveTaskToStack(taskId, stackId, toTop); + mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop); } finally { Binder.restoreCallingIdentity(ident); } @@ -8793,7 +8793,8 @@ public final class ActivityManagerService extends ActivityManagerNative || (task != mStackSupervisor.getFocusedStack().topTask()))) { throw new IllegalArgumentException("Invalid task, not in foreground"); } - mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated); + mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated, + "startLockTask"); } } } finally { @@ -8878,7 +8879,7 @@ public final class ActivityManagerService extends ActivityManagerNative Log.d(TAG, "stopLockTaskMode"); // Stop lock task synchronized (this) { - mStackSupervisor.setLockTaskModeLocked(null, false); + mStackSupervisor.setLockTaskModeLocked(null, false, "stopLockTask"); } } finally { Binder.restoreCallingIdentity(ident); @@ -11338,7 +11339,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Start up initial activity. mBooting = true; - startHomeActivityLocked(mCurrentUserId); + startHomeActivityLocked(mCurrentUserId, "systemReady"); try { if (AppGlobals.getPackageManager().hasSystemUidErrors()) { @@ -18885,7 +18886,7 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } - mStackSupervisor.setLockTaskModeLocked(null, false); + mStackSupervisor.setLockTaskModeLocked(null, false, "startUser"); final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId); if (userInfo == null) { @@ -19142,7 +19143,7 @@ public final class ActivityManagerService extends ActivityManagerNative void moveUserToForeground(UserStartedState uss, int oldUserId, int newUserId) { boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss); if (homeInFront) { - startHomeActivityLocked(newUserId); + startHomeActivityLocked(newUserId, "moveUserToFroreground"); } else { mStackSupervisor.resumeTopActivitiesLocked(); } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 0b49c9cf7e71..b1b2a5cbda3c 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -516,7 +516,7 @@ final class ActivityRecord { void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) { if (task != null && task.removeActivity(this)) { if (task != newTask) { - task.stack.removeTask(task); + task.stack.removeTask(task, "setTask"); } else { Slog.d(TAG, "!!! REMOVE THIS LOG !!! setTask: nearly removed stack=" + (newTask == null ? null : newTask.stack)); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 4e932c1d9b4d..6497cd7dae6c 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -291,7 +291,7 @@ final class ActivityStack { // so we need to be conservative and assume it isn't. Slog.w(TAG, "Activity destroy timeout for " + r); synchronized (mService) { - activityDestroyedLocked(r != null ? r.appToken : null); + activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout"); } } break; case STOP_TIMEOUT_MSG: { @@ -473,10 +473,10 @@ final class ActivityStack { mActivityContainer.mActivityDisplay.mDisplayId == Display.DEFAULT_DISPLAY; } - final void moveToFront() { + final void moveToFront(String reason) { if (isAttached()) { if (isOnHomeDisplay()) { - mStackSupervisor.moveHomeStack(isHomeStack()); + mStackSupervisor.moveHomeStack(isHomeStack(), reason); } mStacks.remove(this); mStacks.add(this); @@ -1496,7 +1496,7 @@ final class ActivityStack { final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo(); return isOnHomeDisplay() && - mStackSupervisor.resumeHomeStackTask(returnTaskType, prev); + mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "noMoreActivities"); } next.delayedResume = false; @@ -1532,7 +1532,7 @@ final class ActivityStack { "resumeTopActivityLocked: Launching home next"); final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo(); - return mStackSupervisor.resumeHomeStackTask(returnTaskType, prev); + return mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "prevFinished"); } } @@ -1817,11 +1817,8 @@ final class ActivityStack { next.app.thread.scheduleNewIntent(next.newIntents, next.appToken); } - EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, - next.userId, System.identityHashCode(next), - next.task.taskId, next.shortComponentName + " top=" - + mStacks.get(mStacks.size() - 1).mStackId + " Callers=" - + Debug.getCallers(6)); + EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, + System.identityHashCode(next), next.task.taskId, next.shortComponentName); next.sleeping = false; mService.showAskCompatModeDialogLocked(next); @@ -2468,18 +2465,19 @@ final class ActivityStack { r.addResultLocked(null, resultWho, requestCode, resultCode, data); } - private void adjustFocusedActivityLocked(ActivityRecord r) { + private void adjustFocusedActivityLocked(ActivityRecord r, String reason) { if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) { ActivityRecord next = topRunningActivityLocked(null); if (next != r) { final TaskRecord task = r.task; if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) { - mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo()); + mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), + reason + " adjustFocus"); } } ActivityRecord top = mStackSupervisor.topRunningActivityLocked(); if (top != null) { - mService.setFocusedActivityLocked(top); + mService.setFocusedActivityLocked(top, reason + " adjustTopFocus"); } } } @@ -2503,7 +2501,7 @@ final class ActivityStack { } if (r.app != null && r.app.thread != null) { - adjustFocusedActivityLocked(r); + adjustFocusedActivityLocked(r, "stopActivity"); r.resumeKeyDispatchingLocked(); try { r.stopped = false; @@ -2707,7 +2705,7 @@ final class ActivityStack { r.pauseKeyDispatchingLocked(); - adjustFocusedActivityLocked(r); + adjustFocusedActivityLocked(r, "finishActivity"); finishActivityResultsLocked(r, resultCode, resultData); @@ -3010,7 +3008,7 @@ final class ActivityStack { r.finishLaunchTickingLocked(); } - private void removeActivityFromHistoryLocked(ActivityRecord r) { + private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) { mStackSupervisor.removeChildActivityContainers(r); finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null); r.makeFinishing(); @@ -3035,9 +3033,9 @@ final class ActivityStack { "removeActivityFromHistoryLocked: last activity removed from " + this); if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.isOverHomeStack()) { - mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo()); + mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason); } - removeTask(task); + removeTask(task, reason); } cleanUpActivityServicesLocked(r); r.removeUriPermissionsLocked(); @@ -3202,7 +3200,7 @@ final class ActivityStack { // up. //Slog.w(TAG, "Exception thrown during finish", e); if (r.finishing) { - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy"); removedFromHistory = true; skipDestroy = true; } @@ -3232,7 +3230,7 @@ final class ActivityStack { } else { // remove this record from the history. if (r.finishing) { - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, reason + " hadNoApp"); removedFromHistory = true; } else { if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (no app)"); @@ -3251,7 +3249,7 @@ final class ActivityStack { return removedFromHistory; } - final void activityDestroyedLocked(IBinder token) { + final void activityDestroyedLocked(IBinder token, String reason) { final long origId = Binder.clearCallingIdentity(); try { ActivityRecord r = ActivityRecord.forToken(token); @@ -3263,7 +3261,7 @@ final class ActivityStack { if (isInStackLocked(token) != null) { if (r.state == ActivityState.DESTROYING) { cleanUpActivityLocked(r, true, false); - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, reason); } } mStackSupervisor.resumeTopActivitiesLocked(); @@ -3399,7 +3397,7 @@ final class ActivityStack { mService.updateUsageStats(r, false); } } - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, "appDied"); } else { // We have the current state for this activity, so @@ -3468,15 +3466,16 @@ final class ActivityStack { } } - final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) { + final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options, + String reason) { if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); final int numTasks = mTaskHistory.size(); final int index = mTaskHistory.indexOf(tr); if (numTasks == 0 || index < 0) { // nothing to do! - if (reason != null && - (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + if (source != null && + (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { ActivityOptions.abort(options); } else { updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options); @@ -3487,11 +3486,11 @@ final class ActivityStack { // Shift all activities with this task up to the top // of the stack, keeping them in the same internal order. insertTaskAtTop(tr); - moveToFront(); + moveToFront(reason); if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr); - if (reason != null && - (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + if (source != null && + (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); ActivityRecord r = topRunningActivityLocked(null); if (r != null) { @@ -3521,7 +3520,7 @@ final class ActivityStack { * @param taskId The taskId to collect and move to the bottom. * @return Returns true if the move completed, false if not. */ - final boolean moveTaskToBackLocked(int taskId, ActivityRecord reason) { + final boolean moveTaskToBackLocked(int taskId) { final TaskRecord tr = taskForIdLocked(taskId); if (tr == null) { Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId); @@ -3576,16 +3575,7 @@ final class ActivityStack { } } - if (reason != null && - (reason.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); - ActivityRecord r = topRunningActivityLocked(null); - if (r != null) { - mNoAnimActivities.add(r); - } - } else { - mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false); - } + mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false); mWindowManager.moveTaskToBottom(taskId); if (VALIDATE_TOKENS) { @@ -3600,7 +3590,7 @@ final class ActivityStack { } final int taskToReturnTo = tr.getTaskToReturnTo(); tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); - return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null); + return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null, "moveTaskToBack"); } mStackSupervisor.resumeTopActivitiesLocked(); @@ -4042,7 +4032,7 @@ final class ActivityStack { return starting; } - void removeTask(TaskRecord task) { + void removeTask(TaskRecord task, String reason) { mStackSupervisor.endLockTaskModeIfTaskEnding(task); mWindowManager.removeTask(task.taskId); final ActivityRecord r = mResumedActivity; @@ -4080,7 +4070,7 @@ final class ActivityStack { if (mTaskHistory.isEmpty()) { if (DEBUG_STACK) Slog.i(TAG, "removeTask: moving to back stack=" + this); if (isOnHomeDisplay()) { - mStackSupervisor.moveHomeStack(!isHomeStack()); + mStackSupervisor.moveHomeStack(!isHomeStack(), reason + " leftTaskHistoryEmpty"); } if (mStacks != null) { mStacks.remove(this); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 5c8e19137705..32787d8e9aed 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -398,7 +398,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - void moveHomeStack(boolean toFront) { + void moveHomeStack(boolean toFront, String reason) { ArrayList<ActivityStack> stacks = mHomeStack.mStacks; final int topNdx = stacks.size() - 1; if (topNdx <= 0) { @@ -416,7 +416,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } EventLog.writeEvent(EventLogTags.AM_HOME_STACK_MOVED, mCurrentUser, toFront ? 1 : 0, stacks.get(topNdx).getStackId(), - mFocusedStack == null ? -1 : mFocusedStack.getStackId()); + mFocusedStack == null ? -1 : mFocusedStack.getStackId(), reason); if (mService.mBooting || !mService.mBooted) { final ActivityRecord r = topRunningActivityLocked(); @@ -426,16 +426,16 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - void moveHomeStackTaskToTop(int homeStackTaskType) { + void moveHomeStackTaskToTop(int homeStackTaskType, String reason) { if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) { mWindowManager.showRecentApps(); return; } - moveHomeStack(true); + moveHomeStack(true, reason); mHomeStack.moveHomeStackTaskToTop(homeStackTaskType); } - boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) { + boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev, String reason) { if (!mService.mBooting && !mService.mBooted) { // Not ready yet! return false; @@ -445,7 +445,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mWindowManager.showRecentApps(); return false; } - moveHomeStackTaskToTop(homeStackTaskType); + moveHomeStackTaskToTop(homeStackTaskType, reason); if (prev != null) { prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); } @@ -453,10 +453,10 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityRecord r = mHomeStack.topRunningActivityLocked(null); // if (r != null && (r.isHomeActivity() || r.isRecentsActivity())) { if (r != null && r.isHomeActivity()) { - mService.setFocusedActivityLocked(r); + mService.setFocusedActivityLocked(r, reason); return resumeTopActivitiesLocked(mHomeStack, prev, null); } - return mService.startHomeActivityLocked(mCurrentUser); + return mService.startHomeActivityLocked(mCurrentUser, reason); } TaskRecord anyTaskForIdLocked(int id) { @@ -828,8 +828,8 @@ public final class ActivityStackSupervisor implements DisplayListener { return aInfo; } - void startHomeActivity(Intent intent, ActivityInfo aInfo) { - moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE); + void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) { + moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason); startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0, 0, 0, null, false, null, null, null); } @@ -1581,7 +1581,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return mHomeStack; } - void setFocusedStack(ActivityRecord r) { + void setFocusedStack(ActivityRecord r, String reason) { if (r != null) { final TaskRecord task = r.task; boolean isHomeActivity = !r.isApplicationActivity(); @@ -1592,7 +1592,7 @@ public final class ActivityStackSupervisor implements DisplayListener { final ActivityRecord parent = task.stack.mActivityContainer.mParentActivity; isHomeActivity = parent != null && parent.isHomeActivity(); } - moveHomeStack(isHomeActivity); + moveHomeStack(isHomeActivity, reason); } } @@ -1840,7 +1840,7 @@ public final class ActivityStackSupervisor implements DisplayListener { targetStack.mLastPausedActivity = null; if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack + " from " + intentActivity); - targetStack.moveToFront(); + targetStack.moveToFront("intentActivityFound"); if (intentActivity.task.intent == null) { // This task was started because of movement of // the activity based on affinity... now that we @@ -1869,7 +1869,8 @@ public final class ActivityStackSupervisor implements DisplayListener { intentActivity.setTaskToAffiliateWith(sourceRecord.task); } movedHome = true; - targetStack.moveTaskToFrontLocked(intentActivity.task, r, options); + targetStack.moveTaskToFrontLocked(intentActivity.task, r, options, + "bringingFoundTaskToFront"); if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) { @@ -2067,7 +2068,7 @@ public final class ActivityStackSupervisor implements DisplayListener { newTask = true; targetStack = adjustStackFocus(r, newTask); if (!launchTaskBehind) { - targetStack.moveToFront(); + targetStack.moveToFront("startingNewTask"); } if (reuseTask == null) { r.setTask(targetStack.createTaskRecord(getNextTaskId(), @@ -2096,10 +2097,10 @@ public final class ActivityStackSupervisor implements DisplayListener { return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; } targetStack = sourceTask.stack; - targetStack.moveToFront(); + targetStack.moveToFront("sourceStackToFront"); final TaskRecord topTask = targetStack.topTask(); if (topTask != sourceTask) { - targetStack.moveTaskToFrontLocked(sourceTask, r, options); + targetStack.moveTaskToFrontLocked(sourceTask, r, options, "sourceTaskToFront"); } if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { // In this case, we are adding the activity to an existing @@ -2153,7 +2154,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; } targetStack = inTask.stack; - targetStack.moveTaskToFrontLocked(inTask, r, options); + targetStack.moveTaskToFrontLocked(inTask, r, options, "inTaskToFront"); // Check whether we should actually launch the new activity in to the task, // or just reuse the current activity on top. @@ -2189,7 +2190,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // of a new task... just put it in the top task, though these days // this case should never happen. targetStack = adjustStackFocus(r, newTask); - targetStack.moveToFront(); + targetStack.moveToFront("addingToTopTask"); ActivityRecord prev = targetStack.topActivity(); r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, null, null, true), null); @@ -2212,7 +2213,7 @@ public final class ActivityStackSupervisor implements DisplayListener { targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options); if (!launchTaskBehind) { // Don't set focus on an activity that's going to the back. - mService.setFocusedActivityLocked(r); + mService.setFocusedActivityLocked(r, "startedActivity"); } return ActivityManager.START_SUCCESS; } @@ -2509,7 +2510,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options) { + void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options, String reason) { if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { mUserLeaving = true; } @@ -2518,7 +2519,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // we'll just indicate that this task returns to the home task. task.setTaskToReturnTo(HOME_ACTIVITY_TYPE); } - task.stack.moveTaskToFrontLocked(task, null, options); + task.stack.moveTaskToFrontLocked(task, null, options, reason); if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack=" + task.stack); } @@ -2643,7 +2644,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // display. stack = getStack(createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY)); // Restore home stack to top. - moveHomeStack(true); + moveHomeStack(true, "restoreRecentTask"); if (DEBUG_RECENTS) Slog.v(TAG, "Created stack=" + stack + " for recents restoration."); } @@ -2670,7 +2671,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return true; } - void moveTaskToStack(int taskId, int stackId, boolean toTop) { + void moveTaskToStackLocked(int taskId, int stackId, boolean toTop) { final TaskRecord task = anyTaskForIdLocked(taskId); if (task == null) { return; @@ -2680,7 +2681,7 @@ public final class ActivityStackSupervisor implements DisplayListener { Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId); return; } - task.stack.removeTask(task); + task.stack.removeTask(task, "moveTaskToStack"); stack.addTask(task, toTop, true); mWindowManager.addTask(taskId, stackId, toTop); resumeTopActivitiesLocked(); @@ -3046,14 +3047,14 @@ public final class ActivityStackSupervisor implements DisplayListener { } final boolean homeInFront = stack.isHomeStack(); if (stack.isOnHomeDisplay()) { - moveHomeStack(homeInFront); + moveHomeStack(homeInFront, "switchUserOnHomeDisplay"); TaskRecord task = stack.topTask(); if (task != null) { mWindowManager.moveTaskToTop(task.taskId); } } else { // Stack was moved to another display while user was swapped out. - resumeHomeStackTask(HOME_ACTIVITY_TYPE, null); + resumeHomeStackTask(HOME_ACTIVITY_TYPE, null, "switchUserOnOtherDisplay"); } return homeInFront; } @@ -3454,7 +3455,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mLockTaskNotify.showToast(mLockTaskIsLocked); } - void setLockTaskModeLocked(TaskRecord task, boolean isLocked) { + void setLockTaskModeLocked(TaskRecord task, boolean isLocked, String reason) { if (task == null) { // Take out of lock task mode if necessary if (mLockTaskModeTask != null) { @@ -3471,7 +3472,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return; } mLockTaskModeTask = task; - findTaskToMoveToFrontLocked(task, 0, null); + findTaskToMoveToFrontLocked(task, 0, null, reason); resumeTopActivitiesLocked(); final Message lockTaskMsg = Message.obtain(); diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 41499bee3989..c376744df496 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -94,4 +94,4 @@ option java_package com.android.server.am 30043 am_focused_activity (User|1|5),(Component Name|3) # Home Stack brought to front or rear -30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5) +30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5),(Reason|3) diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index af5ed8387721..f900d0db0308 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -163,6 +163,7 @@ public class NetworkMonitor extends StateMachine { /** * Force evaluation even if it has succeeded in the past. * arg1 = UID responsible for requesting this reeval. Will be billed for data. + * arg2 = Number of evaluation attempts to make. (If 0, make INITIAL_ATTEMPTS attempts.) */ public static final int CMD_FORCE_REEVALUATION = BASE + 8; @@ -212,11 +213,14 @@ public class NetworkMonitor extends StateMachine { // Negative values disable reevaluation. private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay"; - // Default to 5s reevaluation delay. + // When connecting, attempt to validate 3 times, pausing 5s between them. private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000; - private static final int MAX_RETRIES = 10; - // Between groups of MAX_RETRIES evaluation attempts, pause 10 mins in hopes ISP outage passes. + private static final int INITIAL_ATTEMPTS = 3; + // If a network is not validated, make one attempt every 10 mins to see if it starts working. private static final int REEVALUATE_PAUSE_MS = 10*60*1000; + private static final int PERIODIC_ATTEMPTS = 1; + // When an application calls reportBadNetwork, only make one attempt. + private static final int REEVALUATE_ATTEMPTS = 1; private final int mReevaluateDelayMs; private int mReevaluateToken = 0; private static final int INVALID_UID = -1; @@ -236,6 +240,14 @@ public class NetworkMonitor extends StateMachine { // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. private boolean mUserDoesNotWant = false; + // How many times we should attempt validation. Only checked in EvaluatingState; must be set + // before entering EvaluatingState. Note that whatever code causes us to transition to + // EvaluatingState last decides how many attempts will be made, so if one codepath were to + // enter EvaluatingState with a specific number of attempts, and then another were to enter it + // with a different number of attempts, the second number would be used. This is not currently + // a problem because EvaluatingState is not reentrant. + private int mMaxAttempts; + public boolean systemReady = false; private final State mDefaultState = new DefaultState(); @@ -305,6 +317,7 @@ public class NetworkMonitor extends StateMachine { return HANDLED; case CMD_NETWORK_CONNECTED: if (DBG) log("Connected"); + mMaxAttempts = INITIAL_ATTEMPTS; transitionTo(mEvaluatingState); return HANDLED; case CMD_NETWORK_DISCONNECTED: @@ -318,6 +331,7 @@ public class NetworkMonitor extends StateMachine { case CMD_FORCE_REEVALUATION: if (DBG) log("Forcing reevaluation"); mUidResponsibleForReeval = message.arg1; + mMaxAttempts = message.arg2 != 0 ? message.arg2 : REEVALUATE_ATTEMPTS; transitionTo(mEvaluatingState); return HANDLED; case CMD_CAPTIVE_PORTAL_APP_FINISHED: @@ -347,7 +361,10 @@ public class NetworkMonitor extends StateMachine { public void enter() { mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo)); - if (!mUserDoesNotWant) sendMessageDelayed(CMD_FORCE_REEVALUATION, REEVALUATE_PAUSE_MS); + if (!mUserDoesNotWant) { + sendMessageDelayed(CMD_FORCE_REEVALUATION, 0 /* no UID */, + PERIODIC_ATTEMPTS, REEVALUATE_PAUSE_MS); + } } @Override @@ -413,11 +430,11 @@ public class NetworkMonitor extends StateMachine { // Being in the EvaluatingState State indicates the Network is being evaluated for internet // connectivity. private class EvaluatingState extends State { - private int mRetries; + private int mAttempt; @Override public void enter() { - mRetries = 0; + mAttempt = 1; sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); if (mUidResponsibleForReeval != INVALID_UID) { TrafficStats.setThreadStatsUid(mUidResponsibleForReeval); @@ -454,18 +471,18 @@ public class NetworkMonitor extends StateMachine { transitionTo(mValidatedState); return HANDLED; } - // Note: This call to isCaptivePortal() could take minutes. Resolving the - // server's IP addresses could hit the DNS timeout and attempting connections - // to each of the server's several (e.g. 11) IP addresses could each take - // SOCKET_TIMEOUT_MS. During this time this StateMachine will be unresponsive. - // isCaptivePortal() could be executed on another Thread if this is found to - // cause problems. + // Note: This call to isCaptivePortal() could take up to a minute. Resolving the + // server's IP addresses could hit the DNS timeout, and attempting connections + // to each of the server's several IP addresses (currently one IPv4 and one + // IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine + // will be unresponsive. isCaptivePortal() could be executed on another Thread + // if this is found to cause problems. int httpResponseCode = isCaptivePortal(); if (httpResponseCode == 204) { transitionTo(mValidatedState); } else if (httpResponseCode >= 200 && httpResponseCode <= 399) { transitionTo(mCaptivePortalState); - } else if (++mRetries > MAX_RETRIES) { + } else if (++mAttempt > mMaxAttempts) { transitionTo(mOfflineState); } else if (mReevaluateDelayMs >= 0) { Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java index eef5010f00d2..40d2583691c6 100644 --- a/services/core/java/com/android/server/hdmi/SendKeyAction.java +++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java @@ -35,14 +35,25 @@ import android.view.KeyEvent; final class SendKeyAction extends HdmiCecFeatureAction { private static final String TAG = "SendKeyAction"; + // If the first key press lasts this much amount of time without any other key event + // coming down, we trigger the press-and-hold operation. Set to the value slightly + // shorter than the threshold(500ms) between two successive key press events + // as specified in the standard for the operation. + private static final int AWAIT_LONGPRESS_MS = 400; + // Amount of time this action waits for a new release key input event. When timed out, // the action sends out UCR and finishes its lifecycle. Used to deal with missing key release // event, which can lead the device on the receiving end to generating unintended key repeats. private static final int AWAIT_RELEASE_KEY_MS = 1000; - // State in which the action is at work. The state is set in {@link #start()} and - // persists throughout the process till it is set back to {@code STATE_NONE} at the end. - private static final int STATE_PROCESSING_KEYCODE = 1; + // State in which the long press is being checked at the beginning. The state is set in + // {@link #start()} and lasts for {@link #AWAIT_LONGPRESS_MS}. + private static final int STATE_CHECKING_LONGPRESS = 1; + + // State in which the action is handling incoming keys. Persists throughout the process + // till it is set back to {@code STATE_NONE} at the end when a release key event for + // the last key is processed. + private static final int STATE_PROCESSING_KEYCODE = 2; // Logical address of the device to which the UCP/UCP commands are sent. private final int mTargetAddress; @@ -77,8 +88,8 @@ final class SendKeyAction extends HdmiCecFeatureAction { finish(); return true; } - mState = STATE_PROCESSING_KEYCODE; - addTimer(mState, AWAIT_RELEASE_KEY_MS); + mState = STATE_CHECKING_LONGPRESS; + addTimer(mState, AWAIT_LONGPRESS_MS); return true; } @@ -93,7 +104,7 @@ final class SendKeyAction extends HdmiCecFeatureAction { * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN} */ void processKeyEvent(int keycode, boolean isPressed) { - if (mState != STATE_PROCESSING_KEYCODE) { + if (mState != STATE_CHECKING_LONGPRESS && mState != STATE_PROCESSING_KEYCODE) { Slog.w(TAG, "Not in a valid state"); return; } @@ -152,12 +163,23 @@ final class SendKeyAction extends HdmiCecFeatureAction { @Override public void handleTimerEvent(int state) { - // Timeout on waiting for the release key event. Send UCR and quit the action. - if (mState != STATE_PROCESSING_KEYCODE) { - Slog.w(TAG, "Not in a valid state"); - return; + switch (mState) { + case STATE_CHECKING_LONGPRESS: + // The first key press lasts long enough to start press-and-hold. + mActionTimer.clearTimerMessage(); + mState = STATE_PROCESSING_KEYCODE; + sendKeyDown(mLastKeycode); + mLastSendKeyTime = getCurrentTime(); + addTimer(mState, AWAIT_RELEASE_KEY_MS); + break; + case STATE_PROCESSING_KEYCODE: + // Timeout on waiting for the release key event. Send UCR and quit the action. + sendKeyUp(); + finish(); + break; + default: + Slog.w(TAG, "Not in a valid state"); + break; } - sendKeyUp(); - finish(); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 91637e32ef10..5b17eaa861fc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3281,7 +3281,7 @@ public class PackageManagerService extends IPackageManager.Stub { // If the result set is different from when this // was created, we need to clear it and re-ask the // user their preference, if we're looking for an "always" type entry. - if (always && !pa.mPref.sameSet(query, priority)) { + if (always && !pa.mPref.sameSet(query)) { Slog.i(TAG, "Result set changed, dropping preferred activity for " + intent + " type " + resolvedType); if (DEBUG_PREFERRED) { diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index 69c1909df6ad..8e2e0cde273f 100644 --- a/services/core/java/com/android/server/pm/PreferredComponent.java +++ b/services/core/java/com/android/server/pm/PreferredComponent.java @@ -192,7 +192,7 @@ public class PreferredComponent { } } - public boolean sameSet(List<ResolveInfo> query, int priority) { + public boolean sameSet(List<ResolveInfo> query) { if (mSetPackages == null) { return query == null; } @@ -201,10 +201,10 @@ public class PreferredComponent { } final int NQ = query.size(); final int NS = mSetPackages.length; + int numMatch = 0; for (int i=0; i<NQ; i++) { ResolveInfo ri = query.get(i); - if (ri.priority != priority) continue; ActivityInfo ai = ri.activityInfo; boolean good = false; for (int j=0; j<NS; j++) { diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 2af56fe3e119..50b22624421b 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -677,7 +677,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { private AudioDevicePort mAudioSource; private List<AudioDevicePort> mAudioSink = new ArrayList<>(); private AudioPatch mAudioPatch = null; - private float mCommittedVolume = 0.0f; + // Set to an invalid value for a volume, so that current volume can be applied at the + // first call to updateAudioConfigLocked(). + private float mCommittedVolume = -1f; private float mSourceVolume = 0.0f; private TvStreamConfig mActiveConfig = null; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1c2fbb1668f3..089d8976781a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -764,7 +764,7 @@ public class WindowManagerService extends IWindowManager.Stub * Whether the UI is currently running in touch mode (not showing * navigational focus because the user is directly pressing the screen). */ - boolean mInTouchMode = true; + boolean mInTouchMode; private ViewServer mViewServer; private final ArrayList<WindowChangeListener> mWindowChangeListeners = @@ -825,6 +825,8 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_sf_limitedAlpha); mHasPermanentDpad = context.getResources().getBoolean( com.android.internal.R.bool.config_hasPermanentDpad); + mInTouchMode = context.getResources().getBoolean( + com.android.internal.R.bool.config_defaultInTouchMode); mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplaySettings = new DisplaySettings(); @@ -8060,16 +8062,16 @@ public class WindowManagerService extends IWindowManager.Stub break; case TAP_OUTSIDE_STACK: { - int stackId; - synchronized (mWindowMap) { - stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2); - } - if (stackId >= 0) { - try { - mActivityManager.setFocusedStack(stackId); - } catch (RemoteException e) { - } - } +// int stackId; +// synchronized (mWindowMap) { +// stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2); +// } +// if (stackId >= 0) { +// try { +// mActivityManager.setFocusedStack(stackId); +// } catch (RemoteException e) { +// } +// } } break; case NOTIFY_ACTIVITY_DRAWN: diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 2ca562910f57..fd4c01641ab5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4734,6 +4734,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public UserHandle createAndInitializeUser(ComponentName who, String name, String ownerName, ComponentName profileOwnerComponent, Bundle adminExtras) { UserHandle user = createUser(who, name); + if (user == null) { + return null; + } long id = Binder.clearCallingIdentity(); try { String profileOwnerPkg = profileOwnerComponent.getPackageName(); @@ -4893,6 +4896,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } mUserManager.setUserRestriction(key, enabled, user); + if (enabled != alreadyRestricted) { + if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) { + // Send out notifications however as some clients may want to reread the + // value which actually changed due to a restriction having been applied. + final String property = Settings.Secure.SYS_PROP_SETTING_VERSION; + long version = SystemProperties.getLong(property, 0) + 1; + SystemProperties.set(property, Long.toString(version)); + + final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED; + Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name); + mContext.getContentResolver().notifyChange(url, null, true, userHandle); + } + } } finally { restoreCallingIdentity(id); } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 662172662fdf..1a6b2928af8e 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -691,7 +691,7 @@ public class TelecomManager { getTelecomService().clearAccounts(packageName); } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelecomService#clearAccountsForPackage()", e); + Log.e(TAG, "Error calling ITelecomService#clearAccountsForPackage", e); } } @@ -726,7 +726,7 @@ public class TelecomManager { return getTelecomService().isVoiceMailNumber(accountHandle, number); } } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling isInCall().", e); + Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e); } return false; } @@ -746,12 +746,32 @@ public class TelecomManager { return getTelecomService().hasVoiceMailNumber(accountHandle); } } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling isInCall().", e); + Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e); } return false; } /** + * Return the line 1 phone number for given phone account. + * + * @param accountHandle The handle for the account retrieve a number for. + * @return A string representation of the line 1 phone number. + * + * @hide + */ + @SystemApi + public String getLine1Number(PhoneAccountHandle accountHandle) { + try { + if (isServiceConnected()) { + return getTelecomService().getLine1Number(accountHandle); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e); + } + return null; + } + + /** * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding * states). * <p> diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index f8d753943138..d2030f24e6e2 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -126,6 +126,11 @@ interface ITelecomService { boolean hasVoiceMailNumber(in PhoneAccountHandle accountHandle); /** + * @see TelecomServiceImpl#getLine1Number + */ + String getLine1Number(in PhoneAccountHandle accountHandle); + + /** * @see TelecomServiceImpl#getDefaultPhoneApp */ ComponentName getDefaultPhoneApp(); |