diff options
69 files changed, 967 insertions, 5065 deletions
diff --git a/api/current.txt b/api/current.txt index e6d088d..120025d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -482,7 +482,7 @@ package android { field public static final int dialogTitle = 16843250; // 0x10101f2 field public static final int digits = 16843110; // 0x1010166 field public static final int direction = 16843217; // 0x10101d1 - field public static final int directionDescriptions = 16843681; // 0x10103a1 + field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1 field public static final int directionPriority = 16843218; // 0x10101d2 field public static final int disableDependentsState = 16843249; // 0x10101f1 field public static final int disabledAlpha = 16842803; // 0x1010033 @@ -1200,7 +1200,7 @@ package android { field public static final int tag = 16842961; // 0x10100d1 field public static final int targetActivity = 16843266; // 0x1010202 field public static final int targetClass = 16842799; // 0x101002f - field public static final int targetDescriptions = 16843680; // 0x10103a0 + field public static final deprecated int targetDescriptions = 16843680; // 0x10103a0 field public static final int targetId = 16843740; // 0x10103dc field public static final int targetName = 16843853; // 0x101044d field public static final int targetPackage = 16842785; // 0x1010021 @@ -24813,6 +24813,8 @@ package android.provider { field public static final java.lang.String EMAIL = "email"; field public static final java.lang.String EMAIL_ISPRIMARY = "email_isprimary"; field public static final java.lang.String EMAIL_TYPE = "email_type"; + field public static final java.lang.String EXTRA_ACCOUNT = "android.provider.extra.ACCOUNT"; + field public static final java.lang.String EXTRA_DATA_SET = "android.provider.extra.DATA_SET"; field public static final java.lang.String FULL_MODE = "full_mode"; field public static final java.lang.String IM_HANDLE = "im_handle"; field public static final java.lang.String IM_ISPRIMARY = "im_isprimary"; @@ -26139,6 +26141,8 @@ package android.provider { } public static final class Telephony.Threads implements android.provider.Telephony.ThreadsColumns { + method public static long getOrCreateThreadId(android.content.Context, java.lang.String); + method public static long getOrCreateThreadId(android.content.Context, java.util.Set<java.lang.String>); field public static final int BROADCAST_THREAD = 1; // 0x1 field public static final int COMMON_THREAD = 0; // 0x0 field public static final android.net.Uri CONTENT_URI; diff --git a/api/system-current.txt b/api/system-current.txt index e6ddb4f..0d51f5a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -554,7 +554,7 @@ package android { field public static final int dialogTitle = 16843250; // 0x10101f2 field public static final int digits = 16843110; // 0x1010166 field public static final int direction = 16843217; // 0x10101d1 - field public static final int directionDescriptions = 16843681; // 0x10103a1 + field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1 field public static final int directionPriority = 16843218; // 0x10101d2 field public static final int disableDependentsState = 16843249; // 0x10101f1 field public static final int disabledAlpha = 16842803; // 0x1010033 @@ -1276,7 +1276,7 @@ package android { field public static final int tag = 16842961; // 0x10100d1 field public static final int targetActivity = 16843266; // 0x1010202 field public static final int targetClass = 16842799; // 0x101002f - field public static final int targetDescriptions = 16843680; // 0x10103a0 + field public static final deprecated int targetDescriptions = 16843680; // 0x10103a0 field public static final int targetId = 16843740; // 0x10103dc field public static final int targetName = 16843853; // 0x101044d field public static final int targetPackage = 16842785; // 0x1010021 @@ -26402,6 +26402,8 @@ package android.provider { field public static final java.lang.String EMAIL = "email"; field public static final java.lang.String EMAIL_ISPRIMARY = "email_isprimary"; field public static final java.lang.String EMAIL_TYPE = "email_type"; + field public static final java.lang.String EXTRA_ACCOUNT = "android.provider.extra.ACCOUNT"; + field public static final java.lang.String EXTRA_DATA_SET = "android.provider.extra.DATA_SET"; field public static final java.lang.String FULL_MODE = "full_mode"; field public static final java.lang.String IM_HANDLE = "im_handle"; field public static final java.lang.String IM_ISPRIMARY = "im_isprimary"; @@ -27728,6 +27730,8 @@ package android.provider { } public static final class Telephony.Threads implements android.provider.Telephony.ThreadsColumns { + method public static long getOrCreateThreadId(android.content.Context, java.lang.String); + method public static long getOrCreateThreadId(android.content.Context, java.util.Set<java.lang.String>); field public static final int BROADCAST_THREAD = 1; // 0x1 field public static final int COMMON_THREAD = 0; // 0x0 field public static final android.net.Uri CONTENT_URI; @@ -30677,6 +30681,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 a597d5e..74c468d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2373,8 +2373,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; @@ -2939,7 +2941,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); } } @@ -3151,7 +3154,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); } } @@ -3161,7 +3165,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); + } } /** diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 12d4513..067073a 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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index d24e614..7dd559c 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -8709,10 +8709,8 @@ public final class ContactsContract { * dialog to chose an account * <p> * Type: {@link Account} - * - * @hide */ - public static final String ACCOUNT = "com.android.contacts.extra.ACCOUNT"; + public static final String EXTRA_ACCOUNT = "android.provider.extra.ACCOUNT"; /** * Used to specify the data set within the account in which to create the @@ -8722,10 +8720,8 @@ public final class ContactsContract { * created in the base account, with no data set. * <p> * Type: String - * - * @hide */ - public static final String DATA_SET = "com.android.contacts.extra.DATA_SET"; + public static final String EXTRA_DATA_SET = "android.provider.extra.DATA_SET"; } } } diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index b18cc3b..ed2d3c6 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; @@ -35,10 +37,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; @@ -67,6 +72,7 @@ public class NtpTrustedTime implements TrustedTime { final String server = secureServer != null ? secureServer : defaultServer; sSingleton = new NtpTrustedTime(server, timeout); + sContext = context; } return sSingleton; @@ -79,6 +85,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/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 7b20e72..743f6b7 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -81,7 +81,7 @@ interface IWindowManager void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind); - void setAppGroupId(IBinder token, int groupId); + void setAppTask(IBinder token, int taskId); void setAppOrientation(IApplicationToken token, int requestedOrientation); int getAppOrientation(IApplicationToken token); void setFocusedApp(IBinder token, boolean moveFocusNow); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1e6f6c9..f55892d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16642,6 +16642,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (changed) { requestLayout(); + invalidateOutline(); } } diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index fe8b08b..d85bbb9 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 094a712..4b061d3 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -1271,11 +1271,19 @@ public class RadialTimePickerView extends View { @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, true); + 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(); @@ -1404,6 +1412,16 @@ public class RadialTimePickerView extends View { 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/com/android/internal/widget/FaceUnlockView.java b/core/java/com/android/internal/widget/FaceUnlockView.java deleted file mode 100644 index 121e601..0000000 --- a/core/java/com/android/internal/widget/FaceUnlockView.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.RelativeLayout; - -public class FaceUnlockView extends RelativeLayout { - private static final String TAG = "FaceUnlockView"; - - public FaceUnlockView(Context context) { - this(context, null); - } - - public FaceUnlockView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - private int resolveMeasured(int measureSpec, int desired) - { - int result = 0; - int specSize = MeasureSpec.getSize(measureSpec); - switch (MeasureSpec.getMode(measureSpec)) { - case MeasureSpec.UNSPECIFIED: - result = desired; - break; - case MeasureSpec.AT_MOST: - result = Math.max(specSize, desired); - break; - case MeasureSpec.EXACTLY: - default: - result = specSize; - } - return result; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int minimumWidth = getSuggestedMinimumWidth(); - final int minimumHeight = getSuggestedMinimumHeight(); - int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth); - int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight); - - final int chosenSize = Math.min(viewWidth, viewHeight); - final int newWidthMeasureSpec = - MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST); - final int newHeightMeasureSpec = - MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST); - - super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); - } -} diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java deleted file mode 100644 index 5f3c5f9..0000000 --- a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget; - -import java.lang.Math; - -import com.android.internal.R; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.StateListDrawable; -import android.util.AttributeSet; -import android.util.Log; -import android.util.StateSet; -import android.view.View; -import android.view.ViewDebug; -import android.view.ViewGroup; -import android.widget.RemoteViews.RemoteView; - -/** - * A layout that switches between its children based on the requested layout height. - * Each child specifies its minimum and maximum valid height. Results are undefined - * if children specify overlapping ranges. A child may specify the maximum height - * as 'unbounded' to indicate that it is willing to be displayed arbitrarily tall. - * - * <p> - * See {@link SizeAdaptiveLayout.LayoutParams} for a full description of the - * layout parameters used by SizeAdaptiveLayout. - */ -@RemoteView -public class SizeAdaptiveLayout extends ViewGroup { - - private static final String TAG = "SizeAdaptiveLayout"; - private static final boolean DEBUG = false; - private static final boolean REPORT_BAD_BOUNDS = true; - private static final long CROSSFADE_TIME = 250; - - // TypedArray indices - private static final int MIN_VALID_HEIGHT = - R.styleable.SizeAdaptiveLayout_Layout_layout_minHeight; - private static final int MAX_VALID_HEIGHT = - R.styleable.SizeAdaptiveLayout_Layout_layout_maxHeight; - - // view state - private View mActiveChild; - private View mLastActive; - - // animation state - private AnimatorSet mTransitionAnimation; - private AnimatorListener mAnimatorListener; - private ObjectAnimator mFadePanel; - private ObjectAnimator mFadeView; - private int mCanceledAnimationCount; - private View mEnteringView; - private View mLeavingView; - // View used to hide larger views under smaller ones to create a uniform crossfade - private View mModestyPanel; - private int mModestyPanelTop; - - public SizeAdaptiveLayout(Context context) { - this(context, null); - } - - public SizeAdaptiveLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SizeAdaptiveLayout(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public SizeAdaptiveLayout( - Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - initialize(); - } - - private void initialize() { - mModestyPanel = new View(getContext()); - // If the SizeAdaptiveLayout has a solid background, use it as a transition hint. - Drawable background = getBackground(); - if (background instanceof StateListDrawable) { - StateListDrawable sld = (StateListDrawable) background; - sld.setState(StateSet.WILD_CARD); - background = sld.getCurrent(); - } - if (background instanceof ColorDrawable) { - mModestyPanel.setBackgroundDrawable(background); - } - SizeAdaptiveLayout.LayoutParams layout = - new SizeAdaptiveLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - mModestyPanel.setLayoutParams(layout); - addView(mModestyPanel); - mFadePanel = ObjectAnimator.ofFloat(mModestyPanel, "alpha", 0f); - mFadeView = ObjectAnimator.ofFloat(null, "alpha", 0f); - mAnimatorListener = new BringToFrontOnEnd(); - mTransitionAnimation = new AnimatorSet(); - mTransitionAnimation.play(mFadeView).with(mFadePanel); - mTransitionAnimation.setDuration(CROSSFADE_TIME); - mTransitionAnimation.addListener(mAnimatorListener); - } - - /** - * Visible for testing - * @hide - */ - public Animator getTransitionAnimation() { - return mTransitionAnimation; - } - - /** - * Visible for testing - * @hide - */ - public View getModestyPanel() { - return mModestyPanel; - } - - @Override - public void onAttachedToWindow() { - mLastActive = null; - // make sure all views start off invisible. - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).setVisibility(View.GONE); - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (DEBUG) Log.d(TAG, this + " measure spec: " + - MeasureSpec.toString(heightMeasureSpec)); - View model = selectActiveChild(heightMeasureSpec); - if (model == null) { - setMeasuredDimension(0, 0); - return; - } - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) model.getLayoutParams(); - if (DEBUG) Log.d(TAG, "active min: " + lp.minHeight + " max: " + lp.maxHeight); - measureChild(model, widthMeasureSpec, heightMeasureSpec); - int childHeight = model.getMeasuredHeight(); - int childWidth = model.getMeasuredHeight(); - int childState = combineMeasuredStates(0, model.getMeasuredState()); - if (DEBUG) Log.d(TAG, "measured child at: " + childHeight); - int resolvedWidth = resolveSizeAndState(childWidth, widthMeasureSpec, childState); - int resolvedHeight = resolveSizeAndState(childHeight, heightMeasureSpec, childState); - if (DEBUG) Log.d(TAG, "resolved to: " + resolvedHeight); - int boundedHeight = clampSizeToBounds(resolvedHeight, model); - if (DEBUG) Log.d(TAG, "bounded to: " + boundedHeight); - setMeasuredDimension(resolvedWidth, boundedHeight); - } - - private int clampSizeToBounds(int measuredHeight, View child) { - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams(); - int heightIn = View.MEASURED_SIZE_MASK & measuredHeight; - int height = Math.max(heightIn, lp.minHeight); - if (lp.maxHeight != SizeAdaptiveLayout.LayoutParams.UNBOUNDED) { - height = Math.min(height, lp.maxHeight); - } - - if (REPORT_BAD_BOUNDS && heightIn != height) { - Log.d(TAG, this + "child view " + child + " " + - "measured out of bounds at " + heightIn +"px " + - "clamped to " + height + "px"); - } - - return height; - } - - //TODO extend to width and height - private View selectActiveChild(int heightMeasureSpec) { - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - final int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - View unboundedView = null; - View tallestView = null; - int tallestViewSize = 0; - View smallestView = null; - int smallestViewSize = Integer.MAX_VALUE; - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child != mModestyPanel) { - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams(); - if (DEBUG) Log.d(TAG, "looking at " + i + - " with min: " + lp.minHeight + - " max: " + lp.maxHeight); - if (lp.maxHeight == SizeAdaptiveLayout.LayoutParams.UNBOUNDED && - unboundedView == null) { - unboundedView = child; - } - if (lp.maxHeight > tallestViewSize) { - tallestViewSize = lp.maxHeight; - tallestView = child; - } - if (lp.minHeight < smallestViewSize) { - smallestViewSize = lp.minHeight; - smallestView = child; - } - if (heightMode != MeasureSpec.UNSPECIFIED && - heightSize >= lp.minHeight && heightSize <= lp.maxHeight) { - if (DEBUG) Log.d(TAG, " found exact match, finishing early"); - return child; - } - } - } - if (unboundedView != null) { - tallestView = unboundedView; - } - if (heightMode == MeasureSpec.UNSPECIFIED || heightSize > tallestViewSize) { - return tallestView; - } else { - return smallestView; - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (DEBUG) Log.d(TAG, this + " onlayout height: " + (bottom - top)); - mLastActive = mActiveChild; - int measureSpec = View.MeasureSpec.makeMeasureSpec(bottom - top, - View.MeasureSpec.EXACTLY); - mActiveChild = selectActiveChild(measureSpec); - if (mActiveChild == null) return; - - mActiveChild.setVisibility(View.VISIBLE); - - if (mLastActive != mActiveChild && mLastActive != null) { - if (DEBUG) Log.d(TAG, this + " changed children from: " + mLastActive + - " to: " + mActiveChild); - - mEnteringView = mActiveChild; - mLeavingView = mLastActive; - - mEnteringView.setAlpha(1f); - - mModestyPanel.setAlpha(1f); - mModestyPanel.bringToFront(); - mModestyPanelTop = mLeavingView.getHeight(); - mModestyPanel.setVisibility(View.VISIBLE); - // TODO: mModestyPanel background should be compatible with mLeavingView - - mLeavingView.bringToFront(); - - if (mTransitionAnimation.isRunning()) { - mTransitionAnimation.cancel(); - } - mFadeView.setTarget(mLeavingView); - mFadeView.setFloatValues(0f); - mFadePanel.setFloatValues(0f); - mTransitionAnimation.setupStartValues(); - mTransitionAnimation.start(); - } - final int childWidth = mActiveChild.getMeasuredWidth(); - final int childHeight = mActiveChild.getMeasuredHeight(); - // TODO investigate setting LAYER_TYPE_HARDWARE on mLastActive - mActiveChild.layout(0, 0, childWidth, childHeight); - - if (DEBUG) Log.d(TAG, "got modesty offset of " + mModestyPanelTop); - mModestyPanel.layout(0, mModestyPanelTop, childWidth, mModestyPanelTop + childHeight); - } - - @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - if (DEBUG) Log.d(TAG, "generate layout from attrs"); - return new SizeAdaptiveLayout.LayoutParams(getContext(), attrs); - } - - @Override - protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - if (DEBUG) Log.d(TAG, "generate default layout from viewgroup"); - return new SizeAdaptiveLayout.LayoutParams(p); - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - if (DEBUG) Log.d(TAG, "generate default layout from null"); - return new SizeAdaptiveLayout.LayoutParams(); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof SizeAdaptiveLayout.LayoutParams; - } - - /** - * Per-child layout information associated with ViewSizeAdaptiveLayout. - * - * TODO extend to width and height - * - * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_minHeight - * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_maxHeight - */ - public static class LayoutParams extends ViewGroup.LayoutParams { - - /** - * Indicates the minimum valid height for the child. - */ - @ViewDebug.ExportedProperty(category = "layout") - public int minHeight; - - /** - * Indicates the maximum valid height for the child. - */ - @ViewDebug.ExportedProperty(category = "layout") - public int maxHeight; - - /** - * Constant value for maxHeight that indicates there is not maximum height. - */ - public static final int UNBOUNDED = -1; - - /** - * {@inheritDoc} - */ - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - if (DEBUG) { - Log.d(TAG, "construct layout from attrs"); - for (int i = 0; i < attrs.getAttributeCount(); i++) { - Log.d(TAG, " " + attrs.getAttributeName(i) + " = " + - attrs.getAttributeValue(i)); - } - } - TypedArray a = - c.obtainStyledAttributes(attrs, - R.styleable.SizeAdaptiveLayout_Layout); - - minHeight = a.getDimensionPixelSize(MIN_VALID_HEIGHT, 0); - if (DEBUG) Log.d(TAG, "got minHeight of: " + minHeight); - - try { - maxHeight = a.getLayoutDimension(MAX_VALID_HEIGHT, UNBOUNDED); - if (DEBUG) Log.d(TAG, "got maxHeight of: " + maxHeight); - } catch (Exception e) { - if (DEBUG) Log.d(TAG, "caught exception looking for maxValidHeight " + e); - } - - a.recycle(); - } - - /** - * Creates a new set of layout parameters with the specified width, height - * and valid height bounds. - * - * @param width the width, either {@link #MATCH_PARENT}, - * {@link #WRAP_CONTENT} or a fixed size in pixels - * @param height the height, either {@link #MATCH_PARENT}, - * {@link #WRAP_CONTENT} or a fixed size in pixels - * @param minHeight the minimum height of this child - * @param maxHeight the maximum height of this child - * or {@link #UNBOUNDED} if the child can grow forever - */ - public LayoutParams(int width, int height, int minHeight, int maxHeight) { - super(width, height); - this.minHeight = minHeight; - this.maxHeight = maxHeight; - } - - /** - * {@inheritDoc} - */ - public LayoutParams(int width, int height) { - this(width, height, UNBOUNDED, UNBOUNDED); - } - - /** - * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}. - */ - public LayoutParams() { - this(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - } - - /** - * {@inheritDoc} - */ - public LayoutParams(ViewGroup.LayoutParams p) { - super(p); - minHeight = UNBOUNDED; - maxHeight = UNBOUNDED; - } - - public String debug(String output) { - return output + "SizeAdaptiveLayout.LayoutParams={" + - ", max=" + maxHeight + - ", max=" + minHeight + "}"; - } - } - - class BringToFrontOnEnd implements AnimatorListener { - @Override - public void onAnimationEnd(Animator animation) { - if (mCanceledAnimationCount == 0) { - mLeavingView.setVisibility(View.GONE); - mModestyPanel.setVisibility(View.GONE); - mEnteringView.bringToFront(); - mEnteringView = null; - mLeavingView = null; - } else { - mCanceledAnimationCount--; - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mCanceledAnimationCount++; - } - - @Override - public void onAnimationRepeat(Animator animation) { - if (DEBUG) Log.d(TAG, "fade animation repeated: should never happen."); - assert(false); - } - - @Override - public void onAnimationStart(Animator animation) { - } - } -} diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java deleted file mode 100644 index 9e7a649..0000000 --- a/core/java/com/android/internal/widget/WaveView.java +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget; - -import java.util.ArrayList; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; -import android.media.AudioAttributes; -import android.os.UserHandle; -import android.os.Vibrator; -import android.provider.Settings; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import com.android.internal.R; - -/** - * A special widget containing a center and outer ring. Moving the center ring to the outer ring - * causes an event that can be caught by implementing OnTriggerListener. - */ -public class WaveView extends View implements ValueAnimator.AnimatorUpdateListener { - private static final String TAG = "WaveView"; - private static final boolean DBG = false; - private static final int WAVE_COUNT = 20; // default wave count - private static final long VIBRATE_SHORT = 20; // msec - private static final long VIBRATE_LONG = 20; // msec - - // Lock state machine states - private static final int STATE_RESET_LOCK = 0; - private static final int STATE_READY = 1; - private static final int STATE_START_ATTEMPT = 2; - private static final int STATE_ATTEMPTING = 3; - private static final int STATE_UNLOCK_ATTEMPT = 4; - private static final int STATE_UNLOCK_SUCCESS = 5; - - // Animation properties. - private static final long DURATION = 300; // duration of transitional animations - private static final long FINAL_DURATION = 200; // duration of final animations when unlocking - private static final long RING_DELAY = 1300; // when to start fading animated rings - private static final long FINAL_DELAY = 200; // delay for unlock success animation - private static final long SHORT_DELAY = 100; // for starting one animation after another. - private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay - private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset - private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion - private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking - private static final long WAVE_DELAY = WAVE_DURATION / WAVE_COUNT; // initial propagation delay - - /** - * The scale by which to multiply the unlock handle width to compute the radius - * in which it can be grabbed when accessibility is disabled. - */ - private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED = 0.5f; - - /** - * The scale by which to multiply the unlock handle width to compute the radius - * in which it can be grabbed when accessibility is enabled (more generous). - */ - private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.0f; - - private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) - .build(); - - private Vibrator mVibrator; - private OnTriggerListener mOnTriggerListener; - private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3); - private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT); - private boolean mFingerDown = false; - private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it - private int mSnapRadius = 136; // minimum threshold for drag unlock - private int mWaveCount = WAVE_COUNT; // number of waves - private long mWaveTimerDelay = WAVE_DELAY; - private int mCurrentWave = 0; - private float mLockCenterX; // center of widget as dictated by widget size - private float mLockCenterY; - private float mMouseX; // current mouse position as of last touch event - private float mMouseY; - private DrawableHolder mUnlockRing; - private DrawableHolder mUnlockDefault; - private DrawableHolder mUnlockHalo; - private int mLockState = STATE_RESET_LOCK; - private int mGrabbedState = OnTriggerListener.NO_HANDLE; - private boolean mWavesRunning; - private boolean mFinishWaves; - - public WaveView(Context context) { - this(context, null); - } - - public WaveView(Context context, AttributeSet attrs) { - super(context, attrs); - - // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView); - // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL); - // a.recycle(); - - initDrawables(); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mLockCenterX = 0.5f * w; - mLockCenterY = 0.5f * h; - super.onSizeChanged(w, h, oldw, oldh); - } - - @Override - protected int getSuggestedMinimumWidth() { - // View should be large enough to contain the unlock ring + halo - return mUnlockRing.getWidth() + mUnlockHalo.getWidth(); - } - - @Override - protected int getSuggestedMinimumHeight() { - // View should be large enough to contain the unlock ring + halo - return mUnlockRing.getHeight() + mUnlockHalo.getHeight(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - int width; - int height; - - if (widthSpecMode == MeasureSpec.AT_MOST) { - width = Math.min(widthSpecSize, getSuggestedMinimumWidth()); - } else if (widthSpecMode == MeasureSpec.EXACTLY) { - width = widthSpecSize; - } else { - width = getSuggestedMinimumWidth(); - } - - if (heightSpecMode == MeasureSpec.AT_MOST) { - height = Math.min(heightSpecSize, getSuggestedMinimumWidth()); - } else if (heightSpecMode == MeasureSpec.EXACTLY) { - height = heightSpecSize; - } else { - height = getSuggestedMinimumHeight(); - } - - setMeasuredDimension(width, height); - } - - private void initDrawables() { - mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring)); - mUnlockRing.setX(mLockCenterX); - mUnlockRing.setY(mLockCenterY); - mUnlockRing.setScaleX(0.1f); - mUnlockRing.setScaleY(0.1f); - mUnlockRing.setAlpha(0.0f); - mDrawables.add(mUnlockRing); - - mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default)); - mUnlockDefault.setX(mLockCenterX); - mUnlockDefault.setY(mLockCenterY); - mUnlockDefault.setScaleX(0.1f); - mUnlockDefault.setScaleY(0.1f); - mUnlockDefault.setAlpha(0.0f); - mDrawables.add(mUnlockDefault); - - mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo)); - mUnlockHalo.setX(mLockCenterX); - mUnlockHalo.setY(mLockCenterY); - mUnlockHalo.setScaleX(0.1f); - mUnlockHalo.setScaleY(0.1f); - mUnlockHalo.setAlpha(0.0f); - mDrawables.add(mUnlockHalo); - - BitmapDrawable wave = createDrawable(R.drawable.unlock_wave); - for (int i = 0; i < mWaveCount; i++) { - DrawableHolder holder = new DrawableHolder(wave); - mLightWaves.add(holder); - holder.setAlpha(0.0f); - } - } - - private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) { - double distX = mouseX - mLockCenterX; - double distY = mouseY - mLockCenterY; - int dragDistance = (int) Math.ceil(Math.hypot(distX, distY)); - double touchA = Math.atan2(distX, distY); - float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA)); - float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA)); - - switch (mLockState) { - case STATE_RESET_LOCK: - if (DBG) Log.v(TAG, "State RESET_LOCK"); - mWaveTimerDelay = WAVE_DELAY; - for (int i = 0; i < mLightWaves.size(); i++) { - DrawableHolder holder = mLightWaves.get(i); - holder.addAnimTo(300, 0, "alpha", 0.0f, false); - } - for (int i = 0; i < mLightWaves.size(); i++) { - mLightWaves.get(i).startAnimations(this); - } - - mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true); - mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true); - mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true); - mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true); - mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true); - - mUnlockDefault.removeAnimationFor("x"); - mUnlockDefault.removeAnimationFor("y"); - mUnlockDefault.removeAnimationFor("scaleX"); - mUnlockDefault.removeAnimationFor("scaleY"); - mUnlockDefault.removeAnimationFor("alpha"); - mUnlockDefault.setX(mLockCenterX); - mUnlockDefault.setY(mLockCenterY); - mUnlockDefault.setScaleX(0.1f); - mUnlockDefault.setScaleY(0.1f); - mUnlockDefault.setAlpha(0.0f); - mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true); - mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true); - mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true); - - mUnlockHalo.removeAnimationFor("x"); - mUnlockHalo.removeAnimationFor("y"); - mUnlockHalo.removeAnimationFor("scaleX"); - mUnlockHalo.removeAnimationFor("scaleY"); - mUnlockHalo.removeAnimationFor("alpha"); - mUnlockHalo.setX(mLockCenterX); - mUnlockHalo.setY(mLockCenterY); - mUnlockHalo.setScaleX(0.1f); - mUnlockHalo.setScaleY(0.1f); - mUnlockHalo.setAlpha(0.0f); - mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true); - mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true); - mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true); - mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true); - mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true); - - removeCallbacks(mLockTimerActions); - - mLockState = STATE_READY; - break; - - case STATE_READY: - if (DBG) Log.v(TAG, "State READY"); - mWaveTimerDelay = WAVE_DELAY; - break; - - case STATE_START_ATTEMPT: - if (DBG) Log.v(TAG, "State START_ATTEMPT"); - mUnlockDefault.removeAnimationFor("x"); - mUnlockDefault.removeAnimationFor("y"); - mUnlockDefault.removeAnimationFor("scaleX"); - mUnlockDefault.removeAnimationFor("scaleY"); - mUnlockDefault.removeAnimationFor("alpha"); - mUnlockDefault.setX(mLockCenterX + 182); - mUnlockDefault.setY(mLockCenterY); - mUnlockDefault.setScaleX(0.1f); - mUnlockDefault.setScaleY(0.1f); - mUnlockDefault.setAlpha(0.0f); - - mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false); - mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false); - mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false); - - mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true); - mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true); - mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true); - - mLockState = STATE_ATTEMPTING; - break; - - case STATE_ATTEMPTING: - if (DBG) Log.v(TAG, "State ATTEMPTING (fingerDown = " + fingerDown + ")"); - if (dragDistance > mSnapRadius) { - mFinishWaves = true; // don't start any more waves. - if (fingerDown) { - mUnlockHalo.addAnimTo(0, 0, "x", ringX, true); - mUnlockHalo.addAnimTo(0, 0, "y", ringY, true); - mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true); - mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true); - mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true); - } else { - if (DBG) Log.v(TAG, "up detected, moving to STATE_UNLOCK_ATTEMPT"); - mLockState = STATE_UNLOCK_ATTEMPT; - } - } else { - // If waves have stopped, we need to kick them off again... - if (!mWavesRunning) { - mWavesRunning = true; - mFinishWaves = false; - // mWaveTimerDelay = WAVE_DELAY; - postDelayed(mAddWaveAction, mWaveTimerDelay); - } - mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true); - mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true); - mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true); - mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true); - mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true); - } - break; - - case STATE_UNLOCK_ATTEMPT: - if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT"); - if (dragDistance > mSnapRadius) { - for (int n = 0; n < mLightWaves.size(); n++) { - DrawableHolder wave = mLightWaves.get(n); - long delay = 1000L*(6 + n - mCurrentWave)/10L; - wave.addAnimTo(FINAL_DURATION, delay, "x", ringX, true); - wave.addAnimTo(FINAL_DURATION, delay, "y", ringY, true); - wave.addAnimTo(FINAL_DURATION, delay, "scaleX", 0.1f, true); - wave.addAnimTo(FINAL_DURATION, delay, "scaleY", 0.1f, true); - wave.addAnimTo(FINAL_DURATION, delay, "alpha", 0.0f, true); - } - for (int i = 0; i < mLightWaves.size(); i++) { - mLightWaves.get(i).startAnimations(this); - } - - mUnlockRing.addAnimTo(FINAL_DURATION, 0, "x", ringX, false); - mUnlockRing.addAnimTo(FINAL_DURATION, 0, "y", ringY, false); - mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleX", 0.1f, false); - mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleY", 0.1f, false); - mUnlockRing.addAnimTo(FINAL_DURATION, 0, "alpha", 0.0f, false); - - mUnlockRing.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false); - - mUnlockDefault.removeAnimationFor("x"); - mUnlockDefault.removeAnimationFor("y"); - mUnlockDefault.removeAnimationFor("scaleX"); - mUnlockDefault.removeAnimationFor("scaleY"); - mUnlockDefault.removeAnimationFor("alpha"); - mUnlockDefault.setX(ringX); - mUnlockDefault.setY(ringY); - mUnlockDefault.setScaleX(0.1f); - mUnlockDefault.setScaleY(0.1f); - mUnlockDefault.setAlpha(0.0f); - - mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "x", ringX, true); - mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "y", ringY, true); - mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleX", 1.0f, true); - mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleY", 1.0f, true); - mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "alpha", 1.0f, true); - - mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false); - mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false); - mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false); - - mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "x", ringX, false); - mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "y", ringY, false); - - mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false); - mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false); - mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false); - - removeCallbacks(mLockTimerActions); - - postDelayed(mLockTimerActions, RESET_TIMEOUT); - - dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE); - mLockState = STATE_UNLOCK_SUCCESS; - } else { - mLockState = STATE_RESET_LOCK; - } - break; - - case STATE_UNLOCK_SUCCESS: - if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS"); - removeCallbacks(mAddWaveAction); - break; - - default: - if (DBG) Log.v(TAG, "Unknown state " + mLockState); - break; - } - mUnlockDefault.startAnimations(this); - mUnlockHalo.startAnimations(this); - mUnlockRing.startAnimations(this); - } - - BitmapDrawable createDrawable(int resId) { - Resources res = getResources(); - Bitmap bitmap = BitmapFactory.decodeResource(res, resId); - return new BitmapDrawable(res, bitmap); - } - - @Override - protected void onDraw(Canvas canvas) { - waveUpdateFrame(mMouseX, mMouseY, mFingerDown); - for (int i = 0; i < mDrawables.size(); ++i) { - mDrawables.get(i).draw(canvas); - } - for (int i = 0; i < mLightWaves.size(); ++i) { - mLightWaves.get(i).draw(canvas); - } - } - - private final Runnable mLockTimerActions = new Runnable() { - public void run() { - if (DBG) Log.v(TAG, "LockTimerActions"); - // reset lock after inactivity - if (mLockState == STATE_ATTEMPTING) { - if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK"); - mLockState = STATE_RESET_LOCK; - } - // for prototype, reset after successful unlock - if (mLockState == STATE_UNLOCK_SUCCESS) { - if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK after success"); - mLockState = STATE_RESET_LOCK; - } - invalidate(); - } - }; - - private final Runnable mAddWaveAction = new Runnable() { - public void run() { - double distX = mMouseX - mLockCenterX; - double distY = mMouseY - mLockCenterY; - int dragDistance = (int) Math.ceil(Math.hypot(distX, distY)); - if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius - && mWaveTimerDelay >= WAVE_DELAY) { - mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT); - - DrawableHolder wave = mLightWaves.get(mCurrentWave); - wave.setAlpha(0.0f); - wave.setScaleX(0.2f); - wave.setScaleY(0.2f); - wave.setX(mMouseX); - wave.setY(mMouseY); - - wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true); - wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true); - wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true); - wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true); - wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true); - - wave.addAnimTo(1000, RING_DELAY, "alpha", 0.0f, false); - wave.startAnimations(WaveView.this); - - mCurrentWave = (mCurrentWave+1) % mWaveCount; - if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay); - } else { - mWaveTimerDelay += DELAY_INCREMENT2; - } - if (mFinishWaves) { - // sentinel used to restart the waves after they've stopped - mWavesRunning = false; - } else { - postDelayed(mAddWaveAction, mWaveTimerDelay); - } - } - }; - - @Override - public boolean onHoverEvent(MotionEvent event) { - if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) { - final int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_HOVER_ENTER: - event.setAction(MotionEvent.ACTION_DOWN); - break; - case MotionEvent.ACTION_HOVER_MOVE: - event.setAction(MotionEvent.ACTION_MOVE); - break; - case MotionEvent.ACTION_HOVER_EXIT: - event.setAction(MotionEvent.ACTION_UP); - break; - } - onTouchEvent(event); - event.setAction(action); - } - return super.onHoverEvent(event); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - final int action = event.getAction(); - mMouseX = event.getX(); - mMouseY = event.getY(); - boolean handled = false; - switch (action) { - case MotionEvent.ACTION_DOWN: - removeCallbacks(mLockTimerActions); - mFingerDown = true; - tryTransitionToStartAttemptState(event); - handled = true; - break; - - case MotionEvent.ACTION_MOVE: - tryTransitionToStartAttemptState(event); - handled = true; - break; - - case MotionEvent.ACTION_UP: - if (DBG) Log.v(TAG, "ACTION_UP"); - mFingerDown = false; - postDelayed(mLockTimerActions, RESET_TIMEOUT); - setGrabbedState(OnTriggerListener.NO_HANDLE); - // Normally the state machine is driven by user interaction causing redraws. - // However, when there's no more user interaction and no running animations, - // the state machine stops advancing because onDraw() never gets called. - // The following ensures we advance to the next state in this case, - // either STATE_UNLOCK_ATTEMPT or STATE_RESET_LOCK. - waveUpdateFrame(mMouseX, mMouseY, mFingerDown); - handled = true; - break; - - case MotionEvent.ACTION_CANCEL: - mFingerDown = false; - handled = true; - break; - } - invalidate(); - return handled ? true : super.onTouchEvent(event); - } - - /** - * Tries to transition to start attempt state. - * - * @param event A motion event. - */ - private void tryTransitionToStartAttemptState(MotionEvent event) { - final float dx = event.getX() - mUnlockHalo.getX(); - final float dy = event.getY() - mUnlockHalo.getY(); - float dist = (float) Math.hypot(dx, dy); - if (dist <= getScaledGrabHandleRadius()) { - setGrabbedState(OnTriggerListener.CENTER_HANDLE); - if (mLockState == STATE_READY) { - mLockState = STATE_START_ATTEMPT; - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - announceUnlockHandle(); - } - } - } - } - - /** - * @return The radius in which the handle is grabbed scaled based on - * whether accessibility is enabled. - */ - private float getScaledGrabHandleRadius() { - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mUnlockHalo.getWidth(); - } else { - return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED * mUnlockHalo.getWidth(); - } - } - - /** - * Announces the unlock handle if accessibility is enabled. - */ - private void announceUnlockHandle() { - setContentDescription(mContext.getString(R.string.description_target_unlock_tablet)); - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - setContentDescription(null); - } - - /** - * Triggers haptic feedback. - */ - private synchronized void vibrate(long duration) { - final boolean hapticEnabled = Settings.System.getIntForUser( - mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, - UserHandle.USER_CURRENT) != 0; - if (hapticEnabled) { - if (mVibrator == null) { - mVibrator = (android.os.Vibrator) getContext() - .getSystemService(Context.VIBRATOR_SERVICE); - } - mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES); - } - } - - /** - * Registers a callback to be invoked when the user triggers an event. - * - * @param listener the OnDialTriggerListener to attach to this view - */ - public void setOnTriggerListener(OnTriggerListener listener) { - mOnTriggerListener = listener; - } - - /** - * Dispatches a trigger event to listener. Ignored if a listener is not set. - * @param whichHandle the handle that triggered the event. - */ - private void dispatchTriggerEvent(int whichHandle) { - vibrate(VIBRATE_LONG); - if (mOnTriggerListener != null) { - mOnTriggerListener.onTrigger(this, whichHandle); - } - } - - /** - * Sets the current grabbed state, and dispatches a grabbed state change - * event to our listener. - */ - private void setGrabbedState(int newState) { - if (newState != mGrabbedState) { - mGrabbedState = newState; - if (mOnTriggerListener != null) { - mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState); - } - } - } - - public interface OnTriggerListener { - /** - * Sent when the user releases the handle. - */ - public static final int NO_HANDLE = 0; - - /** - * Sent when the user grabs the center handle - */ - public static final int CENTER_HANDLE = 10; - - /** - * Called when the user drags the center ring beyond a threshold. - */ - void onTrigger(View v, int whichHandle); - - /** - * Called when the "grabbed state" changes (i.e. when the user either grabs or releases - * one of the handles.) - * - * @param v the view that was triggered - * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE}, - */ - void onGrabbedStateChange(View v, int grabbedState); - } - - public void onAnimationUpdate(ValueAnimator animation) { - invalidate(); - } - - public void reset() { - if (DBG) Log.v(TAG, "reset() : resets state to STATE_RESET_LOCK"); - mLockState = STATE_RESET_LOCK; - invalidate(); - } -} diff --git a/core/java/com/android/internal/widget/multiwaveview/Ease.java b/core/java/com/android/internal/widget/multiwaveview/Ease.java deleted file mode 100644 index 7f90c44..0000000 --- a/core/java/com/android/internal/widget/multiwaveview/Ease.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget.multiwaveview; - -import android.animation.TimeInterpolator; - -class Ease { - private static final float DOMAIN = 1.0f; - private static final float DURATION = 1.0f; - private static final float START = 0.0f; - - static class Linear { - public static final TimeInterpolator easeNone = new TimeInterpolator() { - public float getInterpolation(float input) { - return input; - } - }; - } - - static class Cubic { - public static final TimeInterpolator easeIn = new TimeInterpolator() { - public float getInterpolation(float input) { - return DOMAIN*(input/=DURATION)*input*input + START; - } - }; - public static final TimeInterpolator easeOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return DOMAIN*((input=input/DURATION-1)*input*input + 1) + START; - } - }; - public static final TimeInterpolator easeInOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return ((input/=DURATION/2) < 1.0f) ? - (DOMAIN/2*input*input*input + START) - : (DOMAIN/2*((input-=2)*input*input + 2) + START); - } - }; - } - - static class Quad { - public static final TimeInterpolator easeIn = new TimeInterpolator() { - public float getInterpolation (float input) { - return DOMAIN*(input/=DURATION)*input + START; - } - }; - public static final TimeInterpolator easeOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return -DOMAIN *(input/=DURATION)*(input-2) + START; - } - }; - public static final TimeInterpolator easeInOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return ((input/=DURATION/2) < 1) ? - (DOMAIN/2*input*input + START) - : (-DOMAIN/2 * ((--input)*(input-2) - 1) + START); - } - }; - } - - static class Quart { - public static final TimeInterpolator easeIn = new TimeInterpolator() { - public float getInterpolation(float input) { - return DOMAIN*(input/=DURATION)*input*input*input + START; - } - }; - public static final TimeInterpolator easeOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return -DOMAIN * ((input=input/DURATION-1)*input*input*input - 1) + START; - } - }; - public static final TimeInterpolator easeInOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return ((input/=DURATION/2) < 1) ? - (DOMAIN/2*input*input*input*input + START) - : (-DOMAIN/2 * ((input-=2)*input*input*input - 2) + START); - } - }; - } - - static class Quint { - public static final TimeInterpolator easeIn = new TimeInterpolator() { - public float getInterpolation(float input) { - return DOMAIN*(input/=DURATION)*input*input*input*input + START; - } - }; - public static final TimeInterpolator easeOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return DOMAIN*((input=input/DURATION-1)*input*input*input*input + 1) + START; - } - }; - public static final TimeInterpolator easeInOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return ((input/=DURATION/2) < 1) ? - (DOMAIN/2*input*input*input*input*input + START) - : (DOMAIN/2*((input-=2)*input*input*input*input + 2) + START); - } - }; - } - - static class Sine { - public static final TimeInterpolator easeIn = new TimeInterpolator() { - public float getInterpolation(float input) { - return -DOMAIN * (float) Math.cos(input/DURATION * (Math.PI/2)) + DOMAIN + START; - } - }; - public static final TimeInterpolator easeOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return DOMAIN * (float) Math.sin(input/DURATION * (Math.PI/2)) + START; - } - }; - public static final TimeInterpolator easeInOut = new TimeInterpolator() { - public float getInterpolation(float input) { - return -DOMAIN/2 * ((float)Math.cos(Math.PI*input/DURATION) - 1.0f) + START; - } - }; - } - -} diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java deleted file mode 100644 index 11ac19e..0000000 --- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java +++ /dev/null @@ -1,1383 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget.multiwaveview; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.AnimatorListenerAdapter; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.media.AudioAttributes; -import android.os.Bundle; -import android.os.UserHandle; -import android.os.Vibrator; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityManager; - -import com.android.internal.R; - -import java.util.ArrayList; - -/** - * A re-usable widget containing a center, outer ring and wave animation. - */ -public class GlowPadView extends View { - private static final String TAG = "GlowPadView"; - private static final boolean DEBUG = false; - - // Wave state machine - private static final int STATE_IDLE = 0; - private static final int STATE_START = 1; - private static final int STATE_FIRST_TOUCH = 2; - private static final int STATE_TRACKING = 3; - private static final int STATE_SNAP = 4; - private static final int STATE_FINISH = 5; - - // Animation properties. - private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it - - public interface OnTriggerListener { - int NO_HANDLE = 0; - int CENTER_HANDLE = 1; - public void onGrabbed(View v, int handle); - public void onReleased(View v, int handle); - public void onTrigger(View v, int target); - public void onGrabbedStateChange(View v, int handle); - public void onFinishFinalAnimation(); - } - - // Tuneable parameters for animation - private static final int WAVE_ANIMATION_DURATION = 1000; - private static final int RETURN_TO_HOME_DELAY = 1200; - private static final int RETURN_TO_HOME_DURATION = 200; - private static final int HIDE_ANIMATION_DELAY = 200; - private static final int HIDE_ANIMATION_DURATION = 200; - private static final int SHOW_ANIMATION_DURATION = 200; - private static final int SHOW_ANIMATION_DELAY = 50; - private static final int INITIAL_SHOW_HANDLE_DURATION = 200; - private static final int REVEAL_GLOW_DELAY = 0; - private static final int REVEAL_GLOW_DURATION = 0; - - private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f; - private static final float TARGET_SCALE_EXPANDED = 1.0f; - private static final float TARGET_SCALE_COLLAPSED = 0.8f; - private static final float RING_SCALE_EXPANDED = 1.0f; - private static final float RING_SCALE_COLLAPSED = 0.5f; - - private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) - .build(); - - private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>(); - private AnimationBundle mWaveAnimations = new AnimationBundle(); - private AnimationBundle mTargetAnimations = new AnimationBundle(); - private AnimationBundle mGlowAnimations = new AnimationBundle(); - private ArrayList<String> mTargetDescriptions; - private ArrayList<String> mDirectionDescriptions; - private OnTriggerListener mOnTriggerListener; - private TargetDrawable mHandleDrawable; - private TargetDrawable mOuterRing; - private Vibrator mVibrator; - - private int mFeedbackCount = 3; - private int mVibrationDuration = 0; - private int mGrabbedState; - private int mActiveTarget = -1; - private float mGlowRadius; - private float mWaveCenterX; - private float mWaveCenterY; - private int mMaxTargetHeight; - private int mMaxTargetWidth; - private float mRingScaleFactor = 1f; - private boolean mAllowScaling; - - private float mOuterRadius = 0.0f; - private float mSnapMargin = 0.0f; - private float mFirstItemOffset = 0.0f; - private boolean mMagneticTargets = false; - private boolean mDragging; - private int mNewTargetResources; - - private class AnimationBundle extends ArrayList<Tweener> { - private static final long serialVersionUID = 0xA84D78726F127468L; - private boolean mSuspended; - - public void start() { - if (mSuspended) return; // ignore attempts to start animations - final int count = size(); - for (int i = 0; i < count; i++) { - Tweener anim = get(i); - anim.animator.start(); - } - } - - public void cancel() { - final int count = size(); - for (int i = 0; i < count; i++) { - Tweener anim = get(i); - anim.animator.cancel(); - } - clear(); - } - - public void stop() { - final int count = size(); - for (int i = 0; i < count; i++) { - Tweener anim = get(i); - anim.animator.end(); - } - clear(); - } - - public void setSuspended(boolean suspend) { - mSuspended = suspend; - } - }; - - private AnimatorListener mResetListener = new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); - dispatchOnFinishFinalAnimation(); - } - }; - - private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - ping(); - switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); - dispatchOnFinishFinalAnimation(); - } - }; - - private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() { - public void onAnimationUpdate(ValueAnimator animation) { - invalidate(); - } - }; - - private boolean mAnimatingTargets; - private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - if (mNewTargetResources != 0) { - internalSetTargetResources(mNewTargetResources); - mNewTargetResources = 0; - hideTargets(false, false); - } - mAnimatingTargets = false; - } - }; - private int mTargetResourceId; - private int mTargetDescriptionsResourceId; - private int mDirectionDescriptionsResourceId; - private boolean mAlwaysTrackFinger; - private int mHorizontalInset; - private int mVerticalInset; - private int mGravity = Gravity.TOP; - private boolean mInitialLayout = true; - private Tweener mBackgroundAnimator; - private PointCloud mPointCloud; - private float mInnerRadius; - private int mPointerId; - - public GlowPadView(Context context) { - this(context, null); - } - - public GlowPadView(Context context, AttributeSet attrs) { - super(context, attrs); - Resources res = context.getResources(); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GlowPadView); - mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius); - mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius); - mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin); - mFirstItemOffset = (float) Math.toRadians( - a.getFloat(R.styleable.GlowPadView_firstItemOffset, - (float) Math.toDegrees(mFirstItemOffset))); - mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration, - mVibrationDuration); - mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount, - mFeedbackCount); - mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false); - TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable); - mHandleDrawable = new TargetDrawable(res, handle != null ? handle.resourceId : 0); - mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE); - mOuterRing = new TargetDrawable(res, - getResourceId(a, R.styleable.GlowPadView_outerRingDrawable)); - - mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false); - mMagneticTargets = a.getBoolean(R.styleable.GlowPadView_magneticTargets, mMagneticTargets); - - int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable); - Drawable pointDrawable = pointId != 0 ? context.getDrawable(pointId) : null; - mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f); - - mPointCloud = new PointCloud(pointDrawable); - mPointCloud.makePointCloud(mInnerRadius, mOuterRadius); - mPointCloud.glowManager.setRadius(mGlowRadius); - - TypedValue outValue = new TypedValue(); - - // Read array of target drawables - if (a.getValue(R.styleable.GlowPadView_targetDrawables, outValue)) { - internalSetTargetResources(outValue.resourceId); - } - if (mTargetDrawables == null || mTargetDrawables.size() == 0) { - throw new IllegalStateException("Must specify at least one target drawable"); - } - - // Read array of target descriptions - if (a.getValue(R.styleable.GlowPadView_targetDescriptions, outValue)) { - final int resourceId = outValue.resourceId; - if (resourceId == 0) { - throw new IllegalStateException("Must specify target descriptions"); - } - setTargetDescriptionsResourceId(resourceId); - } - - // Read array of direction descriptions - if (a.getValue(R.styleable.GlowPadView_directionDescriptions, outValue)) { - final int resourceId = outValue.resourceId; - if (resourceId == 0) { - throw new IllegalStateException("Must specify direction descriptions"); - } - setDirectionDescriptionsResourceId(resourceId); - } - - mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP); - - a.recycle(); - - setVibrateEnabled(mVibrationDuration > 0); - - assignDefaultsIfNeeded(); - } - - private int getResourceId(TypedArray a, int id) { - TypedValue tv = a.peekValue(id); - return tv == null ? 0 : tv.resourceId; - } - - private void dump() { - Log.v(TAG, "Outer Radius = " + mOuterRadius); - Log.v(TAG, "SnapMargin = " + mSnapMargin); - Log.v(TAG, "FeedbackCount = " + mFeedbackCount); - Log.v(TAG, "VibrationDuration = " + mVibrationDuration); - Log.v(TAG, "GlowRadius = " + mGlowRadius); - Log.v(TAG, "WaveCenterX = " + mWaveCenterX); - Log.v(TAG, "WaveCenterY = " + mWaveCenterY); - } - - public void suspendAnimations() { - mWaveAnimations.setSuspended(true); - mTargetAnimations.setSuspended(true); - mGlowAnimations.setSuspended(true); - } - - public void resumeAnimations() { - mWaveAnimations.setSuspended(false); - mTargetAnimations.setSuspended(false); - mGlowAnimations.setSuspended(false); - mWaveAnimations.start(); - mTargetAnimations.start(); - mGlowAnimations.start(); - } - - @Override - protected int getSuggestedMinimumWidth() { - // View should be large enough to contain the background + handle and - // target drawable on either edge. - return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth); - } - - @Override - protected int getSuggestedMinimumHeight() { - // View should be large enough to contain the unlock ring + target and - // target drawable on either edge - return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight); - } - - /** - * This gets the suggested width accounting for the ring's scale factor. - */ - protected int getScaledSuggestedMinimumWidth() { - return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) - + mMaxTargetWidth); - } - - /** - * This gets the suggested height accounting for the ring's scale factor. - */ - protected int getScaledSuggestedMinimumHeight() { - return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) - + mMaxTargetHeight); - } - - private int resolveMeasured(int measureSpec, int desired) - { - int result = 0; - int specSize = MeasureSpec.getSize(measureSpec); - switch (MeasureSpec.getMode(measureSpec)) { - case MeasureSpec.UNSPECIFIED: - result = desired; - break; - case MeasureSpec.AT_MOST: - result = Math.min(specSize, desired); - break; - case MeasureSpec.EXACTLY: - default: - result = specSize; - } - return result; - } - - private void switchToState(int state, float x, float y) { - switch (state) { - case STATE_IDLE: - deactivateTargets(); - hideGlow(0, 0, 0.0f, null); - startBackgroundAnimation(0, 0.0f); - mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE); - mHandleDrawable.setAlpha(1.0f); - break; - - case STATE_START: - startBackgroundAnimation(0, 0.0f); - break; - - case STATE_FIRST_TOUCH: - mHandleDrawable.setAlpha(0.0f); - deactivateTargets(); - showTargets(true); - startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f); - setGrabbedState(OnTriggerListener.CENTER_HANDLE); - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - announceTargets(); - } - break; - - case STATE_TRACKING: - mHandleDrawable.setAlpha(0.0f); - showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 1.0f, null); - break; - - case STATE_SNAP: - // TODO: Add transition states (see list_selector_background_transition.xml) - mHandleDrawable.setAlpha(0.0f); - showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 0.0f, null); - break; - - case STATE_FINISH: - doFinish(); - break; - } - } - - private void showGlow(int duration, int delay, float finalAlpha, - AnimatorListener finishListener) { - mGlowAnimations.cancel(); - mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration, - "ease", Ease.Cubic.easeIn, - "delay", delay, - "alpha", finalAlpha, - "onUpdate", mUpdateListener, - "onComplete", finishListener)); - mGlowAnimations.start(); - } - - private void hideGlow(int duration, int delay, float finalAlpha, - AnimatorListener finishListener) { - mGlowAnimations.cancel(); - mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration, - "ease", Ease.Quart.easeOut, - "delay", delay, - "alpha", finalAlpha, - "x", 0.0f, - "y", 0.0f, - "onUpdate", mUpdateListener, - "onComplete", finishListener)); - mGlowAnimations.start(); - } - - private void deactivateTargets() { - final int count = mTargetDrawables.size(); - for (int i = 0; i < count; i++) { - TargetDrawable target = mTargetDrawables.get(i); - target.setState(TargetDrawable.STATE_INACTIVE); - } - mActiveTarget = -1; - } - - /** - * Dispatches a trigger event to listener. Ignored if a listener is not set. - * @param whichTarget the target that was triggered. - */ - private void dispatchTriggerEvent(int whichTarget) { - vibrate(); - if (mOnTriggerListener != null) { - mOnTriggerListener.onTrigger(this, whichTarget); - } - } - - private void dispatchOnFinishFinalAnimation() { - if (mOnTriggerListener != null) { - mOnTriggerListener.onFinishFinalAnimation(); - } - } - - private void doFinish() { - final int activeTarget = mActiveTarget; - final boolean targetHit = activeTarget != -1; - - if (targetHit) { - if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit); - - highlightSelected(activeTarget); - - // Inform listener of any active targets. Typically only one will be active. - hideGlow(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener); - dispatchTriggerEvent(activeTarget); - if (!mAlwaysTrackFinger) { - // Force ring and targets to finish animation to final expanded state - mTargetAnimations.stop(); - } - } else { - // Animate handle back to the center based on current state. - hideGlow(HIDE_ANIMATION_DURATION, 0, 0.0f, mResetListenerWithPing); - hideTargets(true, false); - } - - setGrabbedState(OnTriggerListener.NO_HANDLE); - } - - private void highlightSelected(int activeTarget) { - // Highlight the given target and fade others - mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE); - hideUnselected(activeTarget); - } - - private void hideUnselected(int active) { - for (int i = 0; i < mTargetDrawables.size(); i++) { - if (i != active) { - mTargetDrawables.get(i).setAlpha(0.0f); - } - } - } - - private void hideTargets(boolean animate, boolean expanded) { - mTargetAnimations.cancel(); - // Note: these animations should complete at the same time so that we can swap out - // the target assets asynchronously from the setTargetResources() call. - mAnimatingTargets = animate; - final int duration = animate ? HIDE_ANIMATION_DURATION : 0; - final int delay = animate ? HIDE_ANIMATION_DELAY : 0; - - final float targetScale = expanded ? - TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED; - final int length = mTargetDrawables.size(); - final TimeInterpolator interpolator = Ease.Cubic.easeOut; - for (int i = 0; i < length; i++) { - TargetDrawable target = mTargetDrawables.get(i); - target.setState(TargetDrawable.STATE_INACTIVE); - mTargetAnimations.add(Tweener.to(target, duration, - "ease", interpolator, - "alpha", 0.0f, - "scaleX", targetScale, - "scaleY", targetScale, - "delay", delay, - "onUpdate", mUpdateListener)); - } - - float ringScaleTarget = expanded ? - RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED; - ringScaleTarget *= mRingScaleFactor; - mTargetAnimations.add(Tweener.to(mOuterRing, duration, - "ease", interpolator, - "alpha", 0.0f, - "scaleX", ringScaleTarget, - "scaleY", ringScaleTarget, - "delay", delay, - "onUpdate", mUpdateListener, - "onComplete", mTargetUpdateListener)); - - mTargetAnimations.start(); - } - - private void showTargets(boolean animate) { - mTargetAnimations.stop(); - mAnimatingTargets = animate; - final int delay = animate ? SHOW_ANIMATION_DELAY : 0; - final int duration = animate ? SHOW_ANIMATION_DURATION : 0; - final int length = mTargetDrawables.size(); - for (int i = 0; i < length; i++) { - TargetDrawable target = mTargetDrawables.get(i); - target.setState(TargetDrawable.STATE_INACTIVE); - mTargetAnimations.add(Tweener.to(target, duration, - "ease", Ease.Cubic.easeOut, - "alpha", 1.0f, - "scaleX", 1.0f, - "scaleY", 1.0f, - "delay", delay, - "onUpdate", mUpdateListener)); - } - - float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED; - mTargetAnimations.add(Tweener.to(mOuterRing, duration, - "ease", Ease.Cubic.easeOut, - "alpha", 1.0f, - "scaleX", ringScale, - "scaleY", ringScale, - "delay", delay, - "onUpdate", mUpdateListener, - "onComplete", mTargetUpdateListener)); - - mTargetAnimations.start(); - } - - private void vibrate() { - final boolean hapticEnabled = Settings.System.getIntForUser( - mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, - UserHandle.USER_CURRENT) != 0; - if (mVibrator != null && hapticEnabled) { - mVibrator.vibrate(mVibrationDuration, VIBRATION_ATTRIBUTES); - } - } - - private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) { - Resources res = getContext().getResources(); - TypedArray array = res.obtainTypedArray(resourceId); - final int count = array.length(); - ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count); - for (int i = 0; i < count; i++) { - TypedValue value = array.peekValue(i); - TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0); - drawables.add(target); - } - array.recycle(); - return drawables; - } - - private void internalSetTargetResources(int resourceId) { - final ArrayList<TargetDrawable> targets = loadDrawableArray(resourceId); - mTargetDrawables = targets; - mTargetResourceId = resourceId; - - int maxWidth = mHandleDrawable.getWidth(); - int maxHeight = mHandleDrawable.getHeight(); - final int count = targets.size(); - for (int i = 0; i < count; i++) { - TargetDrawable target = targets.get(i); - maxWidth = Math.max(maxWidth, target.getWidth()); - maxHeight = Math.max(maxHeight, target.getHeight()); - } - if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) { - mMaxTargetWidth = maxWidth; - mMaxTargetHeight = maxHeight; - requestLayout(); // required to resize layout and call updateTargetPositions() - } else { - updateTargetPositions(mWaveCenterX, mWaveCenterY); - updatePointCloudPosition(mWaveCenterX, mWaveCenterY); - } - } - - /** - * Loads an array of drawables from the given resourceId. - * - * @param resourceId - */ - public void setTargetResources(int resourceId) { - if (mAnimatingTargets) { - // postpone this change until we return to the initial state - mNewTargetResources = resourceId; - } else { - internalSetTargetResources(resourceId); - } - } - - public int getTargetResourceId() { - return mTargetResourceId; - } - - /** - * Sets the resource id specifying the target descriptions for accessibility. - * - * @param resourceId The resource id. - */ - public void setTargetDescriptionsResourceId(int resourceId) { - mTargetDescriptionsResourceId = resourceId; - if (mTargetDescriptions != null) { - mTargetDescriptions.clear(); - } - } - - /** - * Gets the resource id specifying the target descriptions for accessibility. - * - * @return The resource id. - */ - public int getTargetDescriptionsResourceId() { - return mTargetDescriptionsResourceId; - } - - /** - * Sets the resource id specifying the target direction descriptions for accessibility. - * - * @param resourceId The resource id. - */ - public void setDirectionDescriptionsResourceId(int resourceId) { - mDirectionDescriptionsResourceId = resourceId; - if (mDirectionDescriptions != null) { - mDirectionDescriptions.clear(); - } - } - - /** - * Gets the resource id specifying the target direction descriptions. - * - * @return The resource id. - */ - public int getDirectionDescriptionsResourceId() { - return mDirectionDescriptionsResourceId; - } - - /** - * Enable or disable vibrate on touch. - * - * @param enabled - */ - public void setVibrateEnabled(boolean enabled) { - if (enabled && mVibrator == null) { - mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); - } else { - mVibrator = null; - } - } - - /** - * Starts wave animation. - * - */ - public void ping() { - if (mFeedbackCount > 0) { - boolean doWaveAnimation = true; - final AnimationBundle waveAnimations = mWaveAnimations; - - // Don't do a wave if there's already one in progress - if (waveAnimations.size() > 0 && waveAnimations.get(0).animator.isRunning()) { - long t = waveAnimations.get(0).animator.getCurrentPlayTime(); - if (t < WAVE_ANIMATION_DURATION/2) { - doWaveAnimation = false; - } - } - - if (doWaveAnimation) { - startWaveAnimation(); - } - } - } - - private void stopAndHideWaveAnimation() { - mWaveAnimations.cancel(); - mPointCloud.waveManager.setAlpha(0.0f); - } - - private void startWaveAnimation() { - mWaveAnimations.cancel(); - mPointCloud.waveManager.setAlpha(1.0f); - mPointCloud.waveManager.setRadius(mHandleDrawable.getWidth()/2.0f); - mWaveAnimations.add(Tweener.to(mPointCloud.waveManager, WAVE_ANIMATION_DURATION, - "ease", Ease.Quad.easeOut, - "delay", 0, - "radius", 2.0f * mOuterRadius, - "onUpdate", mUpdateListener, - "onComplete", - new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - mPointCloud.waveManager.setRadius(0.0f); - mPointCloud.waveManager.setAlpha(0.0f); - } - })); - mWaveAnimations.start(); - } - - /** - * Resets the widget to default state and cancels all animation. If animate is 'true', will - * animate objects into place. Otherwise, objects will snap back to place. - * - * @param animate - */ - public void reset(boolean animate) { - mGlowAnimations.stop(); - mTargetAnimations.stop(); - startBackgroundAnimation(0, 0.0f); - stopAndHideWaveAnimation(); - hideTargets(animate, false); - hideGlow(0, 0, 0.0f, null); - Tweener.reset(); - } - - private void startBackgroundAnimation(int duration, float alpha) { - final Drawable background = getBackground(); - if (mAlwaysTrackFinger && background != null) { - if (mBackgroundAnimator != null) { - mBackgroundAnimator.animator.cancel(); - } - mBackgroundAnimator = Tweener.to(background, duration, - "ease", Ease.Cubic.easeIn, - "alpha", (int)(255.0f * alpha), - "delay", SHOW_ANIMATION_DELAY); - mBackgroundAnimator.animator.start(); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - final int action = event.getActionMasked(); - boolean handled = false; - switch (action) { - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_DOWN: - if (DEBUG) Log.v(TAG, "*** DOWN ***"); - handleDown(event); - handleMove(event); - handled = true; - break; - - case MotionEvent.ACTION_MOVE: - if (DEBUG) Log.v(TAG, "*** MOVE ***"); - handleMove(event); - handled = true; - break; - - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_UP: - if (DEBUG) Log.v(TAG, "*** UP ***"); - handleMove(event); - handleUp(event); - handled = true; - break; - - case MotionEvent.ACTION_CANCEL: - if (DEBUG) Log.v(TAG, "*** CANCEL ***"); - handleMove(event); - handleCancel(event); - handled = true; - break; - - } - invalidate(); - return handled ? true : super.onTouchEvent(event); - } - - private void updateGlowPosition(float x, float y) { - float dx = x - mOuterRing.getX(); - float dy = y - mOuterRing.getY(); - dx *= 1f / mRingScaleFactor; - dy *= 1f / mRingScaleFactor; - mPointCloud.glowManager.setX(mOuterRing.getX() + dx); - mPointCloud.glowManager.setY(mOuterRing.getY() + dy); - } - - private void handleDown(MotionEvent event) { - int actionIndex = event.getActionIndex(); - float eventX = event.getX(actionIndex); - float eventY = event.getY(actionIndex); - switchToState(STATE_START, eventX, eventY); - if (!trySwitchToFirstTouchState(eventX, eventY)) { - mDragging = false; - } else { - mPointerId = event.getPointerId(actionIndex); - updateGlowPosition(eventX, eventY); - } - } - - private void handleUp(MotionEvent event) { - if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE"); - int actionIndex = event.getActionIndex(); - if (event.getPointerId(actionIndex) == mPointerId) { - switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex)); - } - } - - private void handleCancel(MotionEvent event) { - if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL"); - - // Drop the active target if canceled. - mActiveTarget = -1; - - int actionIndex = event.findPointerIndex(mPointerId); - actionIndex = actionIndex == -1 ? 0 : actionIndex; - switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex)); - } - - private void handleMove(MotionEvent event) { - int activeTarget = -1; - final int historySize = event.getHistorySize(); - ArrayList<TargetDrawable> targets = mTargetDrawables; - int ntargets = targets.size(); - float x = 0.0f; - float y = 0.0f; - float activeAngle = 0.0f; - int actionIndex = event.findPointerIndex(mPointerId); - - if (actionIndex == -1) { - return; // no data for this pointer - } - - for (int k = 0; k < historySize + 1; k++) { - float eventX = k < historySize ? event.getHistoricalX(actionIndex, k) - : event.getX(actionIndex); - float eventY = k < historySize ? event.getHistoricalY(actionIndex, k) - : event.getY(actionIndex); - // tx and ty are relative to wave center - float tx = eventX - mWaveCenterX; - float ty = eventY - mWaveCenterY; - float touchRadius = (float) Math.hypot(tx, ty); - final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f; - float limitX = tx * scale; - float limitY = ty * scale; - double angleRad = Math.atan2(-ty, tx); - - if (!mDragging) { - trySwitchToFirstTouchState(eventX, eventY); - } - - if (mDragging) { - // For multiple targets, snap to the one that matches - final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin; - final float snapDistance2 = snapRadius * snapRadius; - // Find first target in range - for (int i = 0; i < ntargets; i++) { - TargetDrawable target = targets.get(i); - - double targetMinRad = mFirstItemOffset + (i - 0.5) * 2 * Math.PI / ntargets; - double targetMaxRad = mFirstItemOffset + (i + 0.5) * 2 * Math.PI / ntargets; - if (target.isEnabled()) { - boolean angleMatches = - (angleRad > targetMinRad && angleRad <= targetMaxRad) || - (angleRad + 2 * Math.PI > targetMinRad && - angleRad + 2 * Math.PI <= targetMaxRad) || - (angleRad - 2 * Math.PI > targetMinRad && - angleRad - 2 * Math.PI <= targetMaxRad); - if (angleMatches && (dist2(tx, ty) > snapDistance2)) { - activeTarget = i; - activeAngle = (float) -angleRad; - } - } - } - } - x = limitX; - y = limitY; - } - - if (!mDragging) { - return; - } - - if (activeTarget != -1) { - switchToState(STATE_SNAP, x,y); - updateGlowPosition(x, y); - } else { - switchToState(STATE_TRACKING, x, y); - updateGlowPosition(x, y); - } - - if (mActiveTarget != activeTarget) { - // Defocus the old target - if (mActiveTarget != -1) { - TargetDrawable target = targets.get(mActiveTarget); - if (target.hasState(TargetDrawable.STATE_FOCUSED)) { - target.setState(TargetDrawable.STATE_INACTIVE); - } - if (mMagneticTargets) { - updateTargetPosition(mActiveTarget, mWaveCenterX, mWaveCenterY); - } - } - // Focus the new target - if (activeTarget != -1) { - TargetDrawable target = targets.get(activeTarget); - if (target.hasState(TargetDrawable.STATE_FOCUSED)) { - target.setState(TargetDrawable.STATE_FOCUSED); - } - if (mMagneticTargets) { - updateTargetPosition(activeTarget, mWaveCenterX, mWaveCenterY, activeAngle); - } - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - String targetContentDescription = getTargetDescription(activeTarget); - announceForAccessibility(targetContentDescription); - } - } - } - mActiveTarget = activeTarget; - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) { - final int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_HOVER_ENTER: - event.setAction(MotionEvent.ACTION_DOWN); - break; - case MotionEvent.ACTION_HOVER_MOVE: - event.setAction(MotionEvent.ACTION_MOVE); - break; - case MotionEvent.ACTION_HOVER_EXIT: - event.setAction(MotionEvent.ACTION_UP); - break; - } - onTouchEvent(event); - event.setAction(action); - } - super.onHoverEvent(event); - return true; - } - - /** - * Sets the current grabbed state, and dispatches a grabbed state change - * event to our listener. - */ - private void setGrabbedState(int newState) { - if (newState != mGrabbedState) { - if (newState != OnTriggerListener.NO_HANDLE) { - vibrate(); - } - mGrabbedState = newState; - if (mOnTriggerListener != null) { - if (newState == OnTriggerListener.NO_HANDLE) { - mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE); - } else { - mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE); - } - mOnTriggerListener.onGrabbedStateChange(this, newState); - } - } - } - - private boolean trySwitchToFirstTouchState(float x, float y) { - final float tx = x - mWaveCenterX; - final float ty = y - mWaveCenterY; - if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledGlowRadiusSquared()) { - if (DEBUG) Log.v(TAG, "** Handle HIT"); - switchToState(STATE_FIRST_TOUCH, x, y); - updateGlowPosition(tx, ty); - mDragging = true; - return true; - } - return false; - } - - private void assignDefaultsIfNeeded() { - if (mOuterRadius == 0.0f) { - mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f; - } - if (mSnapMargin == 0.0f) { - mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics()); - } - if (mInnerRadius == 0.0f) { - mInnerRadius = mHandleDrawable.getWidth() / 10.0f; - } - } - - private void computeInsets(int dx, int dy) { - final int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - mHorizontalInset = 0; - break; - case Gravity.RIGHT: - mHorizontalInset = dx; - break; - case Gravity.CENTER_HORIZONTAL: - default: - mHorizontalInset = dx / 2; - break; - } - switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.TOP: - mVerticalInset = 0; - break; - case Gravity.BOTTOM: - mVerticalInset = dy; - break; - case Gravity.CENTER_VERTICAL: - default: - mVerticalInset = dy / 2; - break; - } - } - - /** - * Given the desired width and height of the ring and the allocated width and height, compute - * how much we need to scale the ring. - */ - private float computeScaleFactor(int desiredWidth, int desiredHeight, - int actualWidth, int actualHeight) { - - // Return unity if scaling is not allowed. - if (!mAllowScaling) return 1f; - - final int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - - float scaleX = 1f; - float scaleY = 1f; - - // We use the gravity as a cue for whether we want to scale on a particular axis. - // We only scale to fit horizontally if we're not pinned to the left or right. Likewise, - // we only scale to fit vertically if we're not pinned to the top or bottom. In these - // cases, we want the ring to hang off the side or top/bottom, respectively. - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - case Gravity.RIGHT: - break; - case Gravity.CENTER_HORIZONTAL: - default: - if (desiredWidth > actualWidth) { - scaleX = (1f * actualWidth - mMaxTargetWidth) / - (desiredWidth - mMaxTargetWidth); - } - break; - } - switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.TOP: - case Gravity.BOTTOM: - break; - case Gravity.CENTER_VERTICAL: - default: - if (desiredHeight > actualHeight) { - scaleY = (1f * actualHeight - mMaxTargetHeight) / - (desiredHeight - mMaxTargetHeight); - } - break; - } - return Math.min(scaleX, scaleY); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int minimumWidth = getSuggestedMinimumWidth(); - final int minimumHeight = getSuggestedMinimumHeight(); - int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth); - int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight); - - mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight, - computedWidth, computedHeight); - - int scaledWidth = getScaledSuggestedMinimumWidth(); - int scaledHeight = getScaledSuggestedMinimumHeight(); - - computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight); - setMeasuredDimension(computedWidth, computedHeight); - } - - private float getRingWidth() { - return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius); - } - - private float getRingHeight() { - return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - final int width = right - left; - final int height = bottom - top; - - // Target placement width/height. This puts the targets on the greater of the ring - // width or the specified outer radius. - final float placementWidth = getRingWidth(); - final float placementHeight = getRingHeight(); - float newWaveCenterX = mHorizontalInset - + Math.max(width, mMaxTargetWidth + placementWidth) / 2; - float newWaveCenterY = mVerticalInset - + Math.max(height, + mMaxTargetHeight + placementHeight) / 2; - - if (mInitialLayout) { - stopAndHideWaveAnimation(); - hideTargets(false, false); - mInitialLayout = false; - } - - mOuterRing.setPositionX(newWaveCenterX); - mOuterRing.setPositionY(newWaveCenterY); - - mPointCloud.setScale(mRingScaleFactor); - - mHandleDrawable.setPositionX(newWaveCenterX); - mHandleDrawable.setPositionY(newWaveCenterY); - - updateTargetPositions(newWaveCenterX, newWaveCenterY); - updatePointCloudPosition(newWaveCenterX, newWaveCenterY); - updateGlowPosition(newWaveCenterX, newWaveCenterY); - - mWaveCenterX = newWaveCenterX; - mWaveCenterY = newWaveCenterY; - - if (DEBUG) dump(); - } - - private void updateTargetPosition(int i, float centerX, float centerY) { - final float angle = getAngle(getSliceAngle(), i); - updateTargetPosition(i, centerX, centerY, angle); - } - - private void updateTargetPosition(int i, float centerX, float centerY, float angle) { - final float placementRadiusX = getRingWidth() / 2; - final float placementRadiusY = getRingHeight() / 2; - if (i >= 0) { - ArrayList<TargetDrawable> targets = mTargetDrawables; - final TargetDrawable targetIcon = targets.get(i); - targetIcon.setPositionX(centerX); - targetIcon.setPositionY(centerY); - targetIcon.setX(placementRadiusX * (float) Math.cos(angle)); - targetIcon.setY(placementRadiusY * (float) Math.sin(angle)); - } - } - - private void updateTargetPositions(float centerX, float centerY) { - updateTargetPositions(centerX, centerY, false); - } - - private void updateTargetPositions(float centerX, float centerY, boolean skipActive) { - final int size = mTargetDrawables.size(); - final float alpha = getSliceAngle(); - // Reposition the target drawables if the view changed. - for (int i = 0; i < size; i++) { - if (!skipActive || i != mActiveTarget) { - updateTargetPosition(i, centerX, centerY, getAngle(alpha, i)); - } - } - } - - private float getAngle(float alpha, int i) { - return mFirstItemOffset + alpha * i; - } - - private float getSliceAngle() { - return (float) (-2.0f * Math.PI / mTargetDrawables.size()); - } - - private void updatePointCloudPosition(float centerX, float centerY) { - mPointCloud.setCenter(centerX, centerY); - } - - @Override - protected void onDraw(Canvas canvas) { - mPointCloud.draw(canvas); - mOuterRing.draw(canvas); - final int ntargets = mTargetDrawables.size(); - for (int i = 0; i < ntargets; i++) { - TargetDrawable target = mTargetDrawables.get(i); - if (target != null) { - target.draw(canvas); - } - } - mHandleDrawable.draw(canvas); - } - - public void setOnTriggerListener(OnTriggerListener listener) { - mOnTriggerListener = listener; - } - - private float square(float d) { - return d * d; - } - - private float dist2(float dx, float dy) { - return dx*dx + dy*dy; - } - - private float getScaledGlowRadiusSquared() { - final float scaledTapRadius; - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mGlowRadius; - } else { - scaledTapRadius = mGlowRadius; - } - return square(scaledTapRadius); - } - - private void announceTargets() { - StringBuilder utterance = new StringBuilder(); - final int targetCount = mTargetDrawables.size(); - for (int i = 0; i < targetCount; i++) { - String targetDescription = getTargetDescription(i); - String directionDescription = getDirectionDescription(i); - if (!TextUtils.isEmpty(targetDescription) - && !TextUtils.isEmpty(directionDescription)) { - String text = String.format(directionDescription, targetDescription); - utterance.append(text); - } - } - if (utterance.length() > 0) { - announceForAccessibility(utterance.toString()); - } - } - - private String getTargetDescription(int index) { - if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) { - mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId); - if (mTargetDrawables.size() != mTargetDescriptions.size()) { - Log.w(TAG, "The number of target drawables must be" - + " equal to the number of target descriptions."); - return null; - } - } - return mTargetDescriptions.get(index); - } - - private String getDirectionDescription(int index) { - if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) { - mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId); - if (mTargetDrawables.size() != mDirectionDescriptions.size()) { - Log.w(TAG, "The number of target drawables must be" - + " equal to the number of direction descriptions."); - return null; - } - } - return mDirectionDescriptions.get(index); - } - - private ArrayList<String> loadDescriptions(int resourceId) { - TypedArray array = getContext().getResources().obtainTypedArray(resourceId); - final int count = array.length(); - ArrayList<String> targetContentDescriptions = new ArrayList<String>(count); - for (int i = 0; i < count; i++) { - String contentDescription = array.getString(i); - targetContentDescriptions.add(contentDescription); - } - array.recycle(); - return targetContentDescriptions; - } - - public int getResourceIdForTarget(int index) { - final TargetDrawable drawable = mTargetDrawables.get(index); - return drawable == null ? 0 : drawable.getResourceId(); - } - - public void setEnableTarget(int resourceId, boolean enabled) { - for (int i = 0; i < mTargetDrawables.size(); i++) { - final TargetDrawable target = mTargetDrawables.get(i); - if (target.getResourceId() == resourceId) { - target.setEnabled(enabled); - break; // should never be more than one match - } - } - } - - /** - * Gets the position of a target in the array that matches the given resource. - * @param resourceId - * @return the index or -1 if not found - */ - public int getTargetPosition(int resourceId) { - for (int i = 0; i < mTargetDrawables.size(); i++) { - final TargetDrawable target = mTargetDrawables.get(i); - if (target.getResourceId() == resourceId) { - return i; // should never be more than one match - } - } - return -1; - } - - private boolean replaceTargetDrawables(Resources res, int existingResourceId, - int newResourceId) { - if (existingResourceId == 0 || newResourceId == 0) { - return false; - } - - boolean result = false; - final ArrayList<TargetDrawable> drawables = mTargetDrawables; - final int size = drawables.size(); - for (int i = 0; i < size; i++) { - final TargetDrawable target = drawables.get(i); - if (target != null && target.getResourceId() == existingResourceId) { - target.setDrawable(res, newResourceId); - result = true; - } - } - - if (result) { - requestLayout(); // in case any given drawable's size changes - } - - return result; - } - - /** - * Searches the given package for a resource to use to replace the Drawable on the - * target with the given resource id - * @param component of the .apk that contains the resource - * @param name of the metadata in the .apk - * @param existingResId the resource id of the target to search for - * @return true if found in the given package and replaced at least one target Drawables - */ - public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name, - int existingResId) { - if (existingResId == 0) return false; - - boolean replaced = false; - if (component != null) { - try { - PackageManager packageManager = mContext.getPackageManager(); - // Look for the search icon specified in the activity meta-data - Bundle metaData = packageManager.getActivityInfo( - component, PackageManager.GET_META_DATA).metaData; - if (metaData != null) { - int iconResId = metaData.getInt(name); - if (iconResId != 0) { - Resources res = packageManager.getResourcesForActivity(component); - replaced = replaceTargetDrawables(res, existingResId, iconResId); - } - } - } catch (NameNotFoundException e) { - Log.w(TAG, "Failed to swap drawable; " - + component.flattenToShortString() + " not found", e); - } catch (Resources.NotFoundException nfe) { - Log.w(TAG, "Failed to swap drawable from " - + component.flattenToShortString(), nfe); - } - } - if (!replaced) { - // Restore the original drawable - replaceTargetDrawables(mContext.getResources(), existingResId, existingResId); - } - return replaced; - } -} diff --git a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java b/core/java/com/android/internal/widget/multiwaveview/PointCloud.java deleted file mode 100644 index 6f26b99..0000000 --- a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget.multiwaveview; - -import java.util.ArrayList; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.util.Log; - -public class PointCloud { - private static final float MIN_POINT_SIZE = 2.0f; - private static final float MAX_POINT_SIZE = 4.0f; - private static final int INNER_POINTS = 8; - private static final String TAG = "PointCloud"; - private ArrayList<Point> mPointCloud = new ArrayList<Point>(); - private Drawable mDrawable; - private float mCenterX; - private float mCenterY; - private Paint mPaint; - private float mScale = 1.0f; - private static final float PI = (float) Math.PI; - - // These allow us to have multiple concurrent animations. - WaveManager waveManager = new WaveManager(); - GlowManager glowManager = new GlowManager(); - private float mOuterRadius; - - public class WaveManager { - private float radius = 50; - private float alpha = 0.0f; - - public void setRadius(float r) { - radius = r; - } - - public float getRadius() { - return radius; - } - - public void setAlpha(float a) { - alpha = a; - } - - public float getAlpha() { - return alpha; - } - }; - - public class GlowManager { - private float x; - private float y; - private float radius = 0.0f; - private float alpha = 0.0f; - - public void setX(float x1) { - x = x1; - } - - public float getX() { - return x; - } - - public void setY(float y1) { - y = y1; - } - - public float getY() { - return y; - } - - public void setAlpha(float a) { - alpha = a; - } - - public float getAlpha() { - return alpha; - } - - public void setRadius(float r) { - radius = r; - } - - public float getRadius() { - return radius; - } - } - - class Point { - float x; - float y; - float radius; - - public Point(float x2, float y2, float r) { - x = (float) x2; - y = (float) y2; - radius = r; - } - } - - public PointCloud(Drawable drawable) { - mPaint = new Paint(); - mPaint.setFilterBitmap(true); - mPaint.setColor(Color.rgb(255, 255, 255)); // TODO: make configurable - mPaint.setAntiAlias(true); - mPaint.setDither(true); - - mDrawable = drawable; - if (mDrawable != null) { - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - } - } - - public void setCenter(float x, float y) { - mCenterX = x; - mCenterY = y; - } - - public void makePointCloud(float innerRadius, float outerRadius) { - if (innerRadius == 0) { - Log.w(TAG, "Must specify an inner radius"); - return; - } - mOuterRadius = outerRadius; - mPointCloud.clear(); - final float pointAreaRadius = (outerRadius - innerRadius); - final float ds = (2.0f * PI * innerRadius / INNER_POINTS); - final int bands = (int) Math.round(pointAreaRadius / ds); - final float dr = pointAreaRadius / bands; - float r = innerRadius; - for (int b = 0; b <= bands; b++, r += dr) { - float circumference = 2.0f * PI * r; - final int pointsInBand = (int) (circumference / ds); - float eta = PI/2.0f; - float dEta = 2.0f * PI / pointsInBand; - for (int i = 0; i < pointsInBand; i++) { - float x = r * (float) Math.cos(eta); - float y = r * (float) Math.sin(eta); - eta += dEta; - mPointCloud.add(new Point(x, y, r)); - } - } - } - - public void setScale(float scale) { - mScale = scale; - } - - public float getScale() { - return mScale; - } - - public int getAlphaForPoint(Point point) { - // Contribution from positional glow - float glowDistance = (float) Math.hypot(glowManager.x - point.x, glowManager.y - point.y); - float glowAlpha = 0.0f; - if (glowDistance < glowManager.radius) { - float cosf = (float) Math.cos(PI * 0.25f * glowDistance / glowManager.radius); - glowAlpha = glowManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 10.0f)); - } - - // Compute contribution from Wave - float radius = (float) Math.hypot(point.x, point.y); - float waveAlpha = 0.0f; - if (radius < waveManager.radius * 2) { - float distanceToWaveRing = (radius - waveManager.radius); - float cosf = (float) Math.cos(PI * 0.5f * distanceToWaveRing / waveManager.radius); - waveAlpha = waveManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 6.0f)); - } - return (int) (Math.max(glowAlpha, waveAlpha) * 255); - } - - private float interp(float min, float max, float f) { - return min + (max - min) * f; - } - - public void draw(Canvas canvas) { - ArrayList<Point> points = mPointCloud; - canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.scale(mScale, mScale, mCenterX, mCenterY); - for (int i = 0; i < points.size(); i++) { - Point point = points.get(i); - final float pointSize = interp(MAX_POINT_SIZE, MIN_POINT_SIZE, - point.radius / mOuterRadius); - final float px = point.x + mCenterX; - final float py = point.y + mCenterY; - int alpha = getAlphaForPoint(point); - - if (alpha == 0) continue; - - if (mDrawable != null) { - canvas.save(Canvas.MATRIX_SAVE_FLAG); - final float cx = mDrawable.getIntrinsicWidth() * 0.5f; - final float cy = mDrawable.getIntrinsicHeight() * 0.5f; - final float s = pointSize / MAX_POINT_SIZE; - canvas.scale(s, s, px, py); - canvas.translate(px - cx, py - cy); - mDrawable.setAlpha(alpha); - mDrawable.draw(canvas); - canvas.restore(); - } else { - mPaint.setAlpha(alpha); - canvas.drawCircle(px, py, pointSize, mPaint); - } - } - canvas.restore(); - } - -} diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java deleted file mode 100644 index 5a4c441..0000000 --- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget.multiwaveview; - -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.StateListDrawable; -import android.util.Log; - -public class TargetDrawable { - private static final String TAG = "TargetDrawable"; - private static final boolean DEBUG = false; - - public static final int[] STATE_ACTIVE = - { android.R.attr.state_enabled, android.R.attr.state_active }; - public static final int[] STATE_INACTIVE = - { android.R.attr.state_enabled, -android.R.attr.state_active }; - public static final int[] STATE_FOCUSED = - { android.R.attr.state_enabled, -android.R.attr.state_active, - android.R.attr.state_focused }; - - private float mTranslationX = 0.0f; - private float mTranslationY = 0.0f; - private float mPositionX = 0.0f; - private float mPositionY = 0.0f; - private float mScaleX = 1.0f; - private float mScaleY = 1.0f; - private float mAlpha = 1.0f; - private Drawable mDrawable; - private boolean mEnabled = true; - private final int mResourceId; - - public TargetDrawable(Resources res, int resId) { - mResourceId = resId; - setDrawable(res, resId); - } - - public void setDrawable(Resources res, int resId) { - // Note we explicitly don't set mResourceId to resId since we allow the drawable to be - // swapped at runtime and want to re-use the existing resource id for identification. - Drawable drawable = resId == 0 ? null : res.getDrawable(resId); - // Mutate the drawable so we can animate shared drawable properties. - mDrawable = drawable != null ? drawable.mutate() : null; - resizeDrawables(); - setState(STATE_INACTIVE); - } - - public TargetDrawable(TargetDrawable other) { - mResourceId = other.mResourceId; - // Mutate the drawable so we can animate shared drawable properties. - mDrawable = other.mDrawable != null ? other.mDrawable.mutate() : null; - resizeDrawables(); - setState(STATE_INACTIVE); - } - - public void setState(int [] state) { - if (mDrawable instanceof StateListDrawable) { - StateListDrawable d = (StateListDrawable) mDrawable; - d.setState(state); - } - } - - public boolean hasState(int [] state) { - if (mDrawable instanceof StateListDrawable) { - StateListDrawable d = (StateListDrawable) mDrawable; - // TODO: this doesn't seem to work - return d.getStateDrawableIndex(state) != -1; - } - return false; - } - - /** - * Returns true if the drawable is a StateListDrawable and is in the focused state. - * - * @return - */ - public boolean isActive() { - if (mDrawable instanceof StateListDrawable) { - StateListDrawable d = (StateListDrawable) mDrawable; - int[] states = d.getState(); - for (int i = 0; i < states.length; i++) { - if (states[i] == android.R.attr.state_focused) { - return true; - } - } - } - return false; - } - - /** - * Returns true if this target is enabled. Typically an enabled target contains a valid - * drawable in a valid state. Currently all targets with valid drawables are valid. - * - * @return - */ - public boolean isEnabled() { - return mDrawable != null && mEnabled; - } - - /** - * Makes drawables in a StateListDrawable all the same dimensions. - * If not a StateListDrawable, then justs sets the bounds to the intrinsic size of the - * drawable. - */ - private void resizeDrawables() { - if (mDrawable instanceof StateListDrawable) { - StateListDrawable d = (StateListDrawable) mDrawable; - int maxWidth = 0; - int maxHeight = 0; - for (int i = 0; i < d.getStateCount(); i++) { - Drawable childDrawable = d.getStateDrawable(i); - maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth()); - maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight()); - } - if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: " - + maxWidth + "x" + maxHeight); - d.setBounds(0, 0, maxWidth, maxHeight); - for (int i = 0; i < d.getStateCount(); i++) { - Drawable childDrawable = d.getStateDrawable(i); - if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: " - + maxWidth + "x" + maxHeight); - childDrawable.setBounds(0, 0, maxWidth, maxHeight); - } - } else if (mDrawable != null) { - mDrawable.setBounds(0, 0, - mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); - } - } - - public void setX(float x) { - mTranslationX = x; - } - - public void setY(float y) { - mTranslationY = y; - } - - public void setScaleX(float x) { - mScaleX = x; - } - - public void setScaleY(float y) { - mScaleY = y; - } - - public void setAlpha(float alpha) { - mAlpha = alpha; - } - - public float getX() { - return mTranslationX; - } - - public float getY() { - return mTranslationY; - } - - public float getScaleX() { - return mScaleX; - } - - public float getScaleY() { - return mScaleY; - } - - public float getAlpha() { - return mAlpha; - } - - public void setPositionX(float x) { - mPositionX = x; - } - - public void setPositionY(float y) { - mPositionY = y; - } - - public float getPositionX() { - return mPositionX; - } - - public float getPositionY() { - return mPositionY; - } - - public int getWidth() { - return mDrawable != null ? mDrawable.getIntrinsicWidth() : 0; - } - - public int getHeight() { - return mDrawable != null ? mDrawable.getIntrinsicHeight() : 0; - } - - public void draw(Canvas canvas) { - if (mDrawable == null || !mEnabled) { - return; - } - canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.scale(mScaleX, mScaleY, mPositionX, mPositionY); - canvas.translate(mTranslationX + mPositionX, mTranslationY + mPositionY); - canvas.translate(-0.5f * getWidth(), -0.5f * getHeight()); - mDrawable.setAlpha((int) Math.round(mAlpha * 255f)); - mDrawable.draw(canvas); - canvas.restore(); - } - - public void setEnabled(boolean enabled) { - mEnabled = enabled; - } - - public int getResourceId() { - return mResourceId; - } -} diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java deleted file mode 100644 index d559d9d..0000000 --- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget.multiwaveview; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; - -import android.animation.Animator.AnimatorListener; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.util.Log; - -class Tweener { - private static final String TAG = "Tweener"; - private static final boolean DEBUG = false; - - ObjectAnimator animator; - private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>(); - - public Tweener(ObjectAnimator anim) { - animator = anim; - } - - private static void remove(Animator animator) { - Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator(); - while (iter.hasNext()) { - Entry<Object, Tweener> entry = iter.next(); - if (entry.getValue().animator == animator) { - if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey()) - + " sTweens.size() = " + sTweens.size()); - iter.remove(); - break; // an animator can only be attached to one object - } - } - } - - public static Tweener to(Object object, long duration, Object... vars) { - long delay = 0; - AnimatorUpdateListener updateListener = null; - AnimatorListener listener = null; - TimeInterpolator interpolator = null; - - // Iterate through arguments and discover properties to animate - ArrayList<PropertyValuesHolder> props = new ArrayList<PropertyValuesHolder>(vars.length/2); - for (int i = 0; i < vars.length; i+=2) { - if (!(vars[i] instanceof String)) { - throw new IllegalArgumentException("Key must be a string: " + vars[i]); - } - String key = (String) vars[i]; - Object value = vars[i+1]; - if ("simultaneousTween".equals(key)) { - // TODO - } else if ("ease".equals(key)) { - interpolator = (TimeInterpolator) value; // TODO: multiple interpolators? - } else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) { - updateListener = (AnimatorUpdateListener) value; - } else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) { - listener = (AnimatorListener) value; - } else if ("delay".equals(key)) { - delay = ((Number) value).longValue(); - } else if ("syncWith".equals(key)) { - // TODO - } else if (value instanceof float[]) { - props.add(PropertyValuesHolder.ofFloat(key, - ((float[])value)[0], ((float[])value)[1])); - } else if (value instanceof int[]) { - props.add(PropertyValuesHolder.ofInt(key, - ((int[])value)[0], ((int[])value)[1])); - } else if (value instanceof Number) { - float floatValue = ((Number)value).floatValue(); - props.add(PropertyValuesHolder.ofFloat(key, floatValue)); - } else { - throw new IllegalArgumentException( - "Bad argument for key \"" + key + "\" with value " + value.getClass()); - } - } - - // Re-use existing tween, if present - Tweener tween = sTweens.get(object); - ObjectAnimator anim = null; - if (tween == null) { - anim = ObjectAnimator.ofPropertyValuesHolder(object, - props.toArray(new PropertyValuesHolder[props.size()])); - tween = new Tweener(anim); - sTweens.put(object, tween); - if (DEBUG) Log.v(TAG, "Added new Tweener " + tween); - } else { - anim = sTweens.get(object).animator; - replace(props, object); // Cancel all animators for given object - } - - if (interpolator != null) { - anim.setInterpolator(interpolator); - } - - // Update animation with properties discovered in loop above - anim.setStartDelay(delay); - anim.setDuration(duration); - if (updateListener != null) { - anim.removeAllUpdateListeners(); // There should be only one - anim.addUpdateListener(updateListener); - } - if (listener != null) { - anim.removeAllListeners(); // There should be only one. - anim.addListener(listener); - } - anim.addListener(mCleanupListener); - - return tween; - } - - Tweener from(Object object, long duration, Object... vars) { - // TODO: for v of vars - // toVars[v] = object[v] - // object[v] = vars[v] - return Tweener.to(object, duration, vars); - } - - // Listener to watch for completed animations and remove them. - private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() { - - @Override - public void onAnimationEnd(Animator animation) { - remove(animation); - } - - @Override - public void onAnimationCancel(Animator animation) { - remove(animation); - } - }; - - public static void reset() { - if (DEBUG) { - Log.v(TAG, "Reset()"); - if (sTweens.size() > 0) { - Log.v(TAG, "Cleaning up " + sTweens.size() + " animations"); - } - } - sTweens.clear(); - } - - private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) { - for (final Object killobject : args) { - Tweener tween = sTweens.get(killobject); - if (tween != null) { - tween.animator.cancel(); - if (props != null) { - tween.animator.setValues( - props.toArray(new PropertyValuesHolder[props.size()])); - } else { - sTweens.remove(tween); - } - } - } - } -} diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 3fdcaf7..ea22b15 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -16,12 +16,9 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" > <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml index 935424a..2a3ee90 100644 --- a/core/res/res/layout/notification_template_material_big_base.xml +++ b/core/res/res/layout/notification_template_material_big_base.xml @@ -16,12 +16,9 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" > <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml index 302e651..f1a9549 100644 --- a/core/res/res/layout/notification_template_material_big_picture.xml +++ b/core/res/res/layout/notification_template_material_big_picture.xml @@ -16,12 +16,9 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="match_parent" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" > <ImageView android:id="@+id/big_picture" diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index d0c10b2..f657f04 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -16,12 +16,9 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" > <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml index ac448ee..d292d4e 100644 --- a/core/res/res/layout/notification_template_material_inbox.xml +++ b/core/res/res/layout/notification_template_material_inbox.xml @@ -16,12 +16,9 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" > <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml index 69020a4..0292d28 100644 --- a/core/res/res/layout/notification_template_material_media.xml +++ b/core/res/res/layout/notification_template_material_media.xml @@ -16,14 +16,11 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="64dp" android:orientation="horizontal" android:background="#00000000" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" > <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml new file mode 100644 index 0000000..3435474 --- /dev/null +++ b/core/res/res/values-television/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** 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. +** 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 TV products. Do not translate. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Flags enabling default window features. See Window.java --> + <bool name="config_defaultWindowFeatureOptionsPanel">false</bool> +</resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 551c044..39c42ee 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7034,75 +7034,16 @@ <!-- =============================== --> <eat-comment /> <declare-styleable name="GlowPadView"> - <!-- Reference to an array resource that be shown as targets around a circle. --> - <attr name="targetDrawables" format="reference" /> - - <!-- Reference to an array resource that be used as description for the targets around the circle. --> + <!-- Reference to an array resource that be used as description for the targets around the circle. + {@deprecated Removed.} --> <attr name="targetDescriptions" format="reference" /> - <!-- Reference to an array resource that be used to announce the directions with targets around the circle. --> + <!-- Reference to an array resource that be used to announce the directions with targets around the circle. + {@deprecated Removed.}--> <attr name="directionDescriptions" format="reference" /> - - <!-- Sets a drawable as the center. --> - <attr name="handleDrawable" format="reference" /> - - <!-- Drawable to use for wave ripple animation. --> - <attr name="outerRingDrawable" format="reference"/> - - <!-- Drawble used for drawing points --> - <attr name="pointDrawable" format="reference" /> - - <!-- Inner radius of glow area. --> - <attr name="innerRadius"/> - - <!-- Outer radius of glow area. Target icons will be drawn on this circle. --> - <attr name="outerRadius" format="dimension" /> - - <!-- Radius of glow under finger. --> - <attr name="glowRadius" format="dimension" /> - - <!-- Tactile feedback duration for actions. Set to '0' for no vibration. --> - <attr name="vibrationDuration" format="integer" /> - - <!-- How close we need to be before snapping to a target. --> - <attr name="snapMargin" format="dimension" /> - - <!-- Number of waves/chevrons to show in animation. --> - <attr name="feedbackCount" format="integer" /> - - <!-- Used when the handle shouldn't wait to be hit before following the finger --> - <attr name="alwaysTrackFinger" format="boolean" /> - - <!-- Location along the circle of the first item, in degrees.--> - <attr name="firstItemOffset" format="float" /> - - <!-- Causes targets to snap to the finger location on activation. --> - <attr name="magneticTargets" format="boolean" /> - - <attr name="gravity" /> - - <!-- Determine whether the glow pad is allowed to scale to fit the bounds indicated - by its parent. If this is set to false, no scaling will occur. If this is set to true - scaling will occur to fit for any axis in which gravity is set to center. --> - <attr name="allowScaling" format="boolean" /> </declare-styleable> <!-- =============================== --> - <!-- SizeAdaptiveLayout class attributes --> - <!-- =============================== --> - <eat-comment /> - <declare-styleable name="SizeAdaptiveLayout_Layout"> - <!-- The maximum valid height for this item. --> - <attr name="layout_maxHeight" format="dimension"> - <!-- Indicates that the view may be resized arbitrarily large. --> - <enum name="unbounded" value="-1" /> - </attr> - <!-- The minimum valid height for this item. --> - <attr name="layout_minHeight" format="dimension" /> - </declare-styleable> - <declare-styleable name="SizeAdaptiveLayout" /> - - <!-- =============================== --> <!-- Location package class attributes --> <!-- =============================== --> <eat-comment /> @@ -7509,11 +7450,6 @@ <enum name="pageDeleteDropTarget" value="7" /> </attr> - <declare-styleable name="SlidingChallengeLayout_Layout"> - <attr name="layout_childType" /> - <attr name="layout_maxHeight" /> - </declare-styleable> - <!-- Attributes that can be used with <code><FragmentBreadCrumbs></code> tags. --> <declare-styleable name="FragmentBreadCrumbs"> @@ -7522,27 +7458,6 @@ <attr name="itemColor" format="color|reference" /> </declare-styleable> - <declare-styleable name="MultiPaneChallengeLayout"> - <!-- Influences how layout_centerWithinArea behaves --> - <attr name="orientation" /> - </declare-styleable> - - <declare-styleable name="MultiPaneChallengeLayout_Layout"> - <!-- Percentage of the screen this child should consume or center within. - If 0/default, the view will be measured by standard rules - as if this were a FrameLayout. --> - <attr name="layout_centerWithinArea" format="float" /> - <attr name="layout_childType" /> - <attr name="layout_gravity" /> - <attr name="layout_maxWidth" format="dimension" /> - <attr name="layout_maxHeight" /> - </declare-styleable> - - <declare-styleable name="KeyguardSecurityViewFlipper_Layout"> - <attr name="layout_maxWidth" /> - <attr name="layout_maxHeight" /> - </declare-styleable> - <declare-styleable name="Toolbar"> <attr name="titleTextAppearance" format="reference" /> <attr name="subtitleTextAppearance" format="reference" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 9f49b08..9945c63 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1759,7 +1759,9 @@ <public type="attr" name="actionModeSplitBackground" id="0x0101039d" /> <public type="attr" name="textAppearanceListItem" id="0x0101039e" /> <public type="attr" name="textAppearanceListItemSmall" id="0x0101039f" /> + <!-- @deprecated Removed. --> <public type="attr" name="targetDescriptions" id="0x010103a0" /> + <!-- @deprecated Removed. --> <public type="attr" name="directionDescriptions" id="0x010103a1" /> <public type="attr" name="overridesImplicitlyEnabledSubtype" id="0x010103a2" /> <public type="attr" name="listPreferredItemPaddingLeft" id="0x010103a3" /> diff --git a/core/tests/coretests/res/layout/size_adaptive.xml b/core/tests/coretests/res/layout/size_adaptive.xml deleted file mode 100644 index 03d0574..0000000 --- a/core/tests/coretests/res/layout/size_adaptive.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/multi1" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_color.xml b/core/tests/coretests/res/layout/size_adaptive_color.xml deleted file mode 100644 index cdb7a59..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_color.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:background="#ffffff" - android:id="@+id/multi1" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml b/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml deleted file mode 100644 index d24df5b..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:background="@drawable/size_adaptive_statelist" - android:id="@+id/multi1" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u.xml b/core/tests/coretests/res/layout/size_adaptive_four_u.xml deleted file mode 100644 index 232b921..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_four_u.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/gridLayout4" - android:layout_width="match_parent" - android:layout_height="256dp" - android:background="#000000" - android:columnCount="2" - android:padding="1dp" > - - <ImageView - android:id="@+id/actor" - android:layout_width="62dp" - android:layout_height="62dp" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="2" - android:contentDescription="@string/actor" - android:src="@drawable/abe" /> - - <TextView - android:layout_width="0dp" - android:id="@+id/name" - android:layout_row="0" - android:layout_column="1" - android:layout_gravity="fill_horizontal" - android:padding="3dp" - android:text="@string/actor" - android:textColor="#ffffff" - android:textStyle="bold" /> - - <ImageView - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_row="1" - android:layout_column="1" - android:layout_gravity="fill_horizontal" - android:padding="5dp" - android:adjustViewBounds="true" - android:background="#555555" - android:scaleType="centerCrop" - android:src="@drawable/gettysburg" - android:contentDescription="@string/caption" /> - - <TextView - android:layout_width="0dp" - android:id="@+id/note" - android:layout_row="2" - android:layout_column="1" - android:layout_gravity="fill_horizontal" - android:padding="3dp" - android:singleLine="true" - android:text="@string/first" - android:textColor="#ffffff" /> -</GridLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml deleted file mode 100644 index 93a10de..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/gridLayout4" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="#000000" - android:columnCount="2" - android:padding="1dp" - android:orientation="horizontal" > - - <ImageView - android:id="@+id/actor" - android:layout_width="62dp" - android:layout_height="62dp" - android:layout_row="0" - android:layout_column="0" - android:layout_rowSpan="2" - android:contentDescription="@string/actor" - android:src="@drawable/abe" /> - - <TextView - android:layout_width="0dp" - android:id="@+id/name" - android:layout_row="0" - android:layout_column="1" - android:layout_gravity="fill_horizontal" - android:padding="3dp" - android:text="@string/actor" - android:textColor="#ffffff" - android:textStyle="bold" /> - - <TextView - android:id="@+id/note" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_column="1" - android:layout_gravity="fill_horizontal" - android:layout_marginTop="5dp" - android:layout_row="1" - android:padding="3dp" - android:singleLine="false" - android:text="@string/first" - android:textColor="#ffffff" /> - - </GridLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_gappy.xml b/core/tests/coretests/res/layout/size_adaptive_gappy.xml deleted file mode 100644 index d5e3b41..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_gappy.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/multi_with_gap" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="256dp" - internal:layout_minHeight="128dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_large_only.xml b/core/tests/coretests/res/layout/size_adaptive_large_only.xml deleted file mode 100644 index cf58265..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_large_only.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/large_only_multi" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="256dp" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_lies.xml b/core/tests/coretests/res/layout/size_adaptive_lies.xml deleted file mode 100644 index 7de892e..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_lies.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/multi1" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u.xml b/core/tests/coretests/res/layout/size_adaptive_one_u.xml deleted file mode 100644 index b6fe4a0..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_one_u.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/gridLayout1" - android:layout_width="match_parent" - android:layout_height="64dp" - android:background="#000000" - android:columnCount="3" - android:padding="1dp" - android:rowCount="2" > - - <ImageView - android:id="@+id/actor" - android:layout_width="62dp" - android:layout_height="62dp" - android:layout_column="0" - android:layout_row="0" - android:layout_rowSpan="2" - android:contentDescription="@string/actor" - android:src="@drawable/abe" /> - - <TextView - android:id="@+id/name" - android:layout_gravity="fill" - android:padding="3dp" - android:text="@string/actor" - android:textColor="#ffffff" - android:textStyle="bold" /> - - <ImageView - android:layout_width="62dp" - android:layout_height="62dp" - android:layout_gravity="fill_vertical" - android:layout_rowSpan="2" - android:adjustViewBounds="true" - android:background="#555555" - android:padding="2dp" - android:scaleType="fitXY" - android:src="@drawable/gettysburg" - android:contentDescription="@string/caption" /> - - <TextView - android:id="@+id/note" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_gravity="fill" - android:layout_marginTop="5dp" - android:padding="3dp" - android:singleLine="true" - android:text="@string/first" - android:textColor="#ffffff" /> - -</GridLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml deleted file mode 100644 index df54eb6..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/gridLayout1" - android:layout_width="match_parent" - android:layout_height="64dp" - android:background="#000000" - android:columnCount="2" - android:padding="1dp" - android:rowCount="2" > - - <ImageView - android:id="@+id/actor" - android:layout_width="62dp" - android:layout_height="62dp" - android:layout_column="0" - android:layout_row="0" - android:layout_rowSpan="2" - android:contentDescription="@string/actor" - android:src="@drawable/abe" /> - - <TextView - android:id="@+id/name" - android:layout_gravity="fill" - android:padding="3dp" - android:text="@string/actor" - android:textColor="#ffffff" - android:textStyle="bold" /> - - <TextView - android:id="@+id/note" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_gravity="fill" - android:layout_marginTop="5dp" - android:padding="3dp" - android:singleLine="true" - android:text="@string/first" - android:textColor="#ffffff" /> - -</GridLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_overlapping.xml b/core/tests/coretests/res/layout/size_adaptive_overlapping.xml deleted file mode 100644 index 4abe8b0..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_overlapping.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/multi_with_overlap" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="256dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="256dp"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_singleton.xml b/core/tests/coretests/res/layout/size_adaptive_singleton.xml deleted file mode 100644 index eba387f..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_singleton.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u_text" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_text.xml b/core/tests/coretests/res/layout/size_adaptive_text.xml deleted file mode 100644 index a9f0ba9..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_text.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/multi1" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u_text" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u_text" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/res/layout/size_adaptive_three_way.xml b/core/tests/coretests/res/layout/size_adaptive_three_way.xml deleted file mode 100644 index 1eb5396..0000000 --- a/core/tests/coretests/res/layout/size_adaptive_three_way.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> -<com.android.internal.widget.SizeAdaptiveLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/three_way_multi" - android:layout_width="match_parent" - android:layout_height="64dp" > - - <include - android:id="@+id/one_u" - layout="@layout/size_adaptive_one_u" - android:layout_width="fill_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - /> - - <include - android:id="@+id/two_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="128dp" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="128dp"/> - - <include - android:id="@+id/four_u" - layout="@layout/size_adaptive_four_u" - android:layout_width="fill_parent" - android:layout_height="256dp" - internal:layout_minHeight="129dp" - internal:layout_maxHeight="unbounded"/> - -</com.android.internal.widget.SizeAdaptiveLayout> diff --git a/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java deleted file mode 100644 index 18411b0..0000000 --- a/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget; - -import com.android.frameworks.coretests.R; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.view.LayoutInflater; -import android.view.View; - -import com.android.internal.widget.SizeAdaptiveLayout; - - -public class SizeAdaptiveLayoutTest extends AndroidTestCase { - - private LayoutInflater mInflater; - private int mOneU; - private int mFourU; - private SizeAdaptiveLayout mSizeAdaptiveLayout; - private View mSmallView; - private View mMediumView; - private View mLargeView; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - // inflate the layout - final Context context = getContext(); - mInflater = LayoutInflater.from(context); - mOneU = 64; - mFourU = 4 * mOneU; - } - - private void inflate(int resource){ - mSizeAdaptiveLayout = (SizeAdaptiveLayout) mInflater.inflate(resource, null); - mSizeAdaptiveLayout.onAttachedToWindow(); - - mSmallView = mSizeAdaptiveLayout.findViewById(R.id.one_u); - mMediumView = mSizeAdaptiveLayout.findViewById(R.id.two_u); - mLargeView = mSizeAdaptiveLayout.findViewById(R.id.four_u); - } - - /** - * The name 'test preconditions' is a convention to signal that if this - * test doesn't pass, the test case was not set up properly and it might - * explain any and all failures in other tests. This is not guaranteed - * to run before other tests, as junit uses reflection to find the tests. - */ - @SmallTest - public void testPreconditions() { - assertNotNull(mInflater); - - inflate(R.layout.size_adaptive); - assertNotNull(mSizeAdaptiveLayout); - assertNotNull(mSmallView); - assertNotNull(mLargeView); - } - - @SmallTest - public void testOpenLarge() { - inflate(R.layout.size_adaptive); - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int height = (int) lp.minHeight + 10; - - measureAndLayout(height); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - assertEquals("1U should be gone", - View.GONE, - mSmallView.getVisibility()); - } - - @SmallTest - public void testOpenSmall() { - inflate(R.layout.size_adaptive); - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("1U should be visible", - View.VISIBLE, - mSmallView.getVisibility()); - assertEquals("4U should be gone", - View.GONE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testOpenTooSmall() { - inflate(R.layout.size_adaptive); - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - int height = (int) lp.minHeight - 10; - - measureAndLayout(height); - - assertEquals("1U should be visible", - View.VISIBLE, - mSmallView.getVisibility()); - assertEquals("4U should be gone", - View.GONE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testOpenTooBig() { - inflate(R.layout.size_adaptive); - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - lp.maxHeight = 500; - mLargeView.setLayoutParams(lp); - int height = (int) (lp.minHeight + 10); - - measureAndLayout(height); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - assertEquals("1U should be gone", - View.GONE, - mSmallView.getVisibility()); - } - - @SmallTest - public void testOpenWrapContent() { - inflate(R.layout.size_adaptive_text); - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int height = (int) lp.minHeight + 10; - - // manually measure it, and lay it out - int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST); - mSizeAdaptiveLayout.measure(500, measureSpec); - assertTrue("should not be forced to 4U", - mSizeAdaptiveLayout.getMeasuredHeight() < mFourU); - } - - @SmallTest - public void testOpenOneUOnlySmall() { - inflate(R.layout.size_adaptive_singleton); - assertNull("largeView should be NULL in the singleton layout", mLargeView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - int height = (int) lp.minHeight - 10; - - measureAndLayout(height); - - assertEquals("1U should be visible", - View.VISIBLE, - mSmallView.getVisibility()); - } - - @SmallTest - public void testOpenOneUOnlyLarge() { - inflate(R.layout.size_adaptive_singleton); - assertNull("largeView should be NULL in the singleton layout", mLargeView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - int height = (int) lp.maxHeight + 10; - - measureAndLayout(height); - - assertEquals("1U should be visible", - View.VISIBLE, - mSmallView.getVisibility()); - } - - @SmallTest - public void testOpenOneUOnlyJustRight() { - inflate(R.layout.size_adaptive_singleton); - assertNull("largeView should be NULL in the singleton layout", mLargeView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("1U should be visible", - View.VISIBLE, - mSmallView.getVisibility()); - } - - @SmallTest - public void testOpenFourUOnlySmall() { - inflate(R.layout.size_adaptive_large_only); - assertNull("smallView should be NULL in the singleton layout", mSmallView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int height = (int) lp.minHeight - 10; - - measureAndLayout(height); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testOpenFourUOnlyLarge() { - inflate(R.layout.size_adaptive_large_only); - assertNull("smallView should be NULL in the singleton layout", mSmallView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int height = (int) lp.maxHeight + 10; - - measureAndLayout(height); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testOpenFourUOnlyJustRight() { - inflate(R.layout.size_adaptive_large_only); - assertNull("smallView should be NULL in the singleton layout", mSmallView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testOpenIntoAGap() { - inflate(R.layout.size_adaptive_gappy); - - SizeAdaptiveLayout.LayoutParams smallParams = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - SizeAdaptiveLayout.LayoutParams largeParams = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - assertTrue("gappy layout should have a gap", - smallParams.maxHeight + 10 < largeParams.minHeight); - int height = (int) smallParams.maxHeight + 10; - - measureAndLayout(height); - - assertTrue("one and only one view should be visible", - mLargeView.getVisibility() != mSmallView.getVisibility()); - // behavior is undefined in this case. - } - - @SmallTest - public void testOpenIntoAnOverlap() { - inflate(R.layout.size_adaptive_overlapping); - - SizeAdaptiveLayout.LayoutParams smallParams = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - SizeAdaptiveLayout.LayoutParams largeParams = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - assertEquals("overlapping layout should overlap", - smallParams.minHeight, - largeParams.minHeight); - int height = (int) smallParams.maxHeight; - - measureAndLayout(height); - - assertTrue("one and only one view should be visible", - mLargeView.getVisibility() != mSmallView.getVisibility()); - assertEquals("1U should get priority in an overlap because it is first", - View.VISIBLE, - mSmallView.getVisibility()); - } - - @SmallTest - public void testOpenThreeWayViewSmall() { - inflate(R.layout.size_adaptive_three_way); - assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("1U should be visible", - View.VISIBLE, - mSmallView.getVisibility()); - assertEquals("2U should be gone", - View.GONE, - mMediumView.getVisibility()); - assertEquals("4U should be gone", - View.GONE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testOpenThreeWayViewMedium() { - inflate(R.layout.size_adaptive_three_way); - assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mMediumView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("1U should be gone", - View.GONE, - mSmallView.getVisibility()); - assertEquals("2U should be visible", - View.VISIBLE, - mMediumView.getVisibility()); - assertEquals("4U should be gone", - View.GONE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testOpenThreeWayViewLarge() { - inflate(R.layout.size_adaptive_three_way); - assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView); - - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("1U should be gone", - View.GONE, - mSmallView.getVisibility()); - assertEquals("2U should be gone", - View.GONE, - mMediumView.getVisibility()); - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - } - - @SmallTest - public void testResizeWithoutAnimation() { - inflate(R.layout.size_adaptive); - - SizeAdaptiveLayout.LayoutParams largeParams = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int startHeight = (int) largeParams.minHeight + 10; - int endHeight = (int) largeParams.minHeight + 10; - - measureAndLayout(startHeight); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - assertFalse("There should be no animation on initial rendering.", - mSizeAdaptiveLayout.getTransitionAnimation().isRunning()); - - measureAndLayout(endHeight); - - assertEquals("4U should still be visible", - View.VISIBLE, - mLargeView.getVisibility()); - assertFalse("There should be no animation on scale within a view.", - mSizeAdaptiveLayout.getTransitionAnimation().isRunning()); - } - - @SmallTest - public void testResizeWithAnimation() { - inflate(R.layout.size_adaptive); - - SizeAdaptiveLayout.LayoutParams smallParams = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - SizeAdaptiveLayout.LayoutParams largeParams = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int startHeight = (int) largeParams.minHeight + 10; - int endHeight = (int) smallParams.maxHeight; - - measureAndLayout(startHeight); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - assertFalse("There should be no animation on initial rendering.", - mSizeAdaptiveLayout.getTransitionAnimation().isRunning()); - - measureAndLayout(endHeight); - - assertEquals("1U should now be visible", - View.VISIBLE, - mSmallView.getVisibility()); - assertTrue("There should be an animation on scale between views.", - mSizeAdaptiveLayout.getTransitionAnimation().isRunning()); - } - - @SmallTest - public void testModestyPanelChangesColorWhite() { - inflate(R.layout.size_adaptive_color); - View panel = mSizeAdaptiveLayout.getModestyPanel(); - assertTrue("ModestyPanel should have a ColorDrawable background", - panel.getBackground() instanceof ColorDrawable); - ColorDrawable panelColor = (ColorDrawable) panel.getBackground(); - ColorDrawable salColor = (ColorDrawable) mSizeAdaptiveLayout.getBackground(); - assertEquals("ModestyPanel color should match the SizeAdaptiveLayout", - panelColor.getColor(), salColor.getColor()); - } - - @SmallTest - public void testModestyPanelTracksStateListColor() { - inflate(R.layout.size_adaptive_color_statelist); - View panel = mSizeAdaptiveLayout.getModestyPanel(); - assertEquals("ModestyPanel should have a ColorDrawable background" , - panel.getBackground().getClass(), ColorDrawable.class); - ColorDrawable panelColor = (ColorDrawable) panel.getBackground(); - assertEquals("ModestyPanel color should match the SizeAdaptiveLayout", - panelColor.getColor(), Color.RED); - } - @SmallTest - public void testOpenSmallEvenWhenLargeIsActuallySmall() { - inflate(R.layout.size_adaptive_lies); - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("1U should be visible", - View.VISIBLE, - mSmallView.getVisibility()); - assertTrue("1U should also have been measured", - mSmallView.getMeasuredHeight() > 0); - } - - @SmallTest - public void testOpenLargeEvenWhenLargeIsActuallySmall() { - inflate(R.layout.size_adaptive_lies); - SizeAdaptiveLayout.LayoutParams lp = - (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams(); - int height = (int) lp.minHeight; - - measureAndLayout(height); - - assertEquals("4U should be visible", - View.VISIBLE, - mLargeView.getVisibility()); - assertTrue("4U should also have been measured", - mLargeView.getMeasuredHeight() > 0); - } - - private void measureAndLayout(int height) { - // manually measure it, and lay it out - int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST); - mSizeAdaptiveLayout.measure(500, measureSpec); - mSizeAdaptiveLayout.layout(0, 0, 500, mSizeAdaptiveLayout.getMeasuredHeight()); - } -} diff --git a/docs/html/training/location/display-address.jd b/docs/html/training/location/display-address.jd index 621b082..516f14f 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/wearables/data-layer/data-items.jd b/docs/html/training/wearables/data-layer/data-items.jd index 12babbf..49a8d32 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 6a3949a..c797f68 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/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 13cdc69..5d9355a 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 781988d..c80adfa 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 803ab05..713f28c 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -934,6 +934,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/SystemUI/res/layout/notification_public_default.xml b/packages/SystemUI/res/layout/notification_public_default.xml index efabc06..044ba09 100644 --- a/packages/SystemUI/res/layout/notification_public_default.xml +++ b/packages/SystemUI/res/layout/notification_public_default.xml @@ -16,12 +16,9 @@ --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" android:id="@+id/status_bar_latest_event_content" android:layout_width="match_parent" android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" > <ImageView android:id="@+id/icon" android:layout_width="40dp" diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index ed43d8e..153b67b 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); @@ -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/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index fddb54e..d6abce9 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 9f8d665..059dde1 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 2b65c18..b5d2720 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2363,7 +2363,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; @@ -2372,7 +2372,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); } @@ -2396,7 +2396,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (stack != null) { ActivityRecord r = stack.topRunningActivityLocked(null); if (r != null) { - setFocusedActivityLocked(r); + setFocusedActivityLocked(r, "setFocusedStack"); } } } @@ -2440,7 +2440,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; @@ -3114,7 +3114,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 @@ -3136,7 +3136,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); } } @@ -5697,7 +5697,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); @@ -5837,7 +5837,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { - stack.activityDestroyedLocked(token); + stack.activityDestroyedLocked(token, "activityDestroyed"); } } } @@ -5948,7 +5948,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("File descriptors passed in options"); } } - + synchronized(this) { int callingUid = Binder.getCallingUid(); int origUserId = userId; @@ -5978,7 +5978,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); } @@ -8019,7 +8019,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); } @@ -8048,7 +8048,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); @@ -8154,7 +8154,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); } @@ -8262,7 +8262,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 { @@ -8347,7 +8348,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); @@ -10813,7 +10814,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Start up initial activity. mBooting = true; - startHomeActivityLocked(mCurrentUserId); + startHomeActivityLocked(mCurrentUserId, "systemReady"); try { if (AppGlobals.getPackageManager().hasSystemUidErrors()) { @@ -18368,7 +18369,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) { @@ -18625,7 +18626,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 8569ea4..a3676f9 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -522,7 +522,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 7c1d54c..dbd787b 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -294,7 +294,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: { @@ -484,10 +484,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); @@ -1505,7 +1505,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; @@ -1541,7 +1541,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"); } } @@ -1827,11 +1827,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); @@ -2206,7 +2203,7 @@ final class ActivityStack { } final int targetTaskId = targetTask.taskId; - mWindowManager.setAppGroupId(target.appToken, targetTaskId); + mWindowManager.setAppTask(target.appToken, targetTaskId); boolean noOptions = canMoveOptions; final int start = replyChainEnd < 0 ? i : replyChainEnd; @@ -2231,7 +2228,7 @@ final class ActivityStack { p.setTask(targetTask, null); targetTask.addActivityAtBottom(p); - mWindowManager.setAppGroupId(p.appToken, targetTaskId); + mWindowManager.setAppTask(p.appToken, targetTaskId); } mWindowManager.moveTaskToBottom(targetTaskId); @@ -2371,7 +2368,7 @@ final class ActivityStack { new RuntimeException("here").fillInStackTrace()); if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " from " + srcPos + " in to resetting task " + task); - mWindowManager.setAppGroupId(p.appToken, taskId); + mWindowManager.setAppTask(p.appToken, taskId); } mWindowManager.moveTaskToTop(taskId); if (VALIDATE_TOKENS) { @@ -2480,18 +2477,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"); } } } @@ -2515,7 +2513,7 @@ final class ActivityStack { } if (r.app != null && r.app.thread != null) { - adjustFocusedActivityLocked(r); + adjustFocusedActivityLocked(r, "stopActivity"); r.resumeKeyDispatchingLocked(); try { r.stopped = false; @@ -2719,7 +2717,7 @@ final class ActivityStack { r.pauseKeyDispatchingLocked(); - adjustFocusedActivityLocked(r); + adjustFocusedActivityLocked(r, "finishActivity"); finishActivityResultsLocked(r, resultCode, resultData); @@ -3022,7 +3020,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(); @@ -3047,9 +3045,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(); @@ -3214,7 +3212,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; } @@ -3244,7 +3242,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)"); @@ -3263,7 +3261,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); @@ -3275,7 +3273,7 @@ final class ActivityStack { if (isInStackLocked(r) != null) { if (r.state == ActivityState.DESTROYING) { cleanUpActivityLocked(r, true, false); - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, reason); } } mStackSupervisor.resumeTopActivitiesLocked(); @@ -3411,7 +3409,7 @@ final class ActivityStack { mService.updateUsageStats(r, false); } } - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, "appDied"); } else { // We have the current state for this activity, so @@ -3480,15 +3478,15 @@ final class ActivityStack { } } - final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) { + final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, 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 (noAnimation) { ActivityOptions.abort(options); } else { updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options); @@ -3499,11 +3497,10 @@ 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 (noAnimation) { mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); ActivityRecord r = topRunningActivityLocked(null); if (r != null) { @@ -3533,7 +3530,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); @@ -3588,16 +3585,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) { @@ -3612,7 +3600,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(); @@ -4065,7 +4053,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; @@ -4103,7 +4091,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 b1bb02a..4674cc2 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -400,7 +400,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) { @@ -418,7 +418,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(); @@ -428,16 +428,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; @@ -447,7 +447,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); } @@ -455,10 +455,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) { @@ -830,8 +830,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,11 +1592,11 @@ public final class ActivityStackSupervisor implements DisplayListener { final ActivityRecord parent = task.stack.mActivityContainer.mParentActivity; isHomeActivity = parent != null && parent.isHomeActivity(); } - moveHomeStack(isHomeActivity); + moveHomeStack(isHomeActivity, reason); } } - final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, + final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, Bundle options, TaskRecord inTask) { final Intent intent = r.intent; @@ -1807,6 +1807,7 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityStack targetStack; intent.setFlags(launchFlags); + final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0; // We may want to try to place the new activity in to an existing task. We always // do this if the target activity is singleTask or singleInstance; we will also do @@ -1840,7 +1841,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 +1870,8 @@ public final class ActivityStackSupervisor implements DisplayListener { intentActivity.setTaskToAffiliateWith(sourceRecord.task); } movedHome = true; - targetStack.moveTaskToFrontLocked(intentActivity.task, r, options); + targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation, + options, "bringingFoundTaskToFront"); if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) { @@ -2067,7 +2069,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 +2098,11 @@ 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, noAnimation, options, + "sourceTaskToFront"); } if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { // In this case, we are adding the activity to an existing @@ -2153,7 +2156,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, noAnimation, 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 +2192,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 +2215,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 +2512,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 +2521,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, false /* noAnimation */, options, reason); if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack=" + task.stack); } @@ -2664,7 +2667,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // display. stack = 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."); } @@ -2691,7 +2694,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; @@ -2701,7 +2704,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(); @@ -3067,14 +3070,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; } @@ -3469,7 +3472,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) { @@ -3486,7 +3489,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 41499be..c376744 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/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 77a1fa9..667d02a 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -117,7 +117,6 @@ public class MediaSessionService extends SystemService implements Monitor { public void onStart() { publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); Watchdog.getInstance().addMonitor(this); - updateUser(); mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); mAudioService = getAudioService(); @@ -125,6 +124,8 @@ public class MediaSessionService extends SystemService implements Monitor { mContentResolver = getContext().getContentResolver(); mSettingsObserver = new SettingsObserver(); mSettingsObserver.observe(); + + updateUser(); } private IAudioService getAudioService() { @@ -501,6 +502,12 @@ public class MediaSessionService extends SystemService implements Monitor { UserRecord user = mUserRecords.get(record.getUserId()); if (receiver != null && user != null) { user.mLastMediaButtonReceiver = receiver; + ComponentName component = receiver.getIntent().getComponent(); + if (component != null && record.getPackageName().equals(component.getPackageName())) { + Settings.Secure.putStringForUser(mContentResolver, + Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(), + record.getUserId()); + } } } @@ -511,14 +518,17 @@ public class MediaSessionService extends SystemService implements Monitor { final class UserRecord { private final int mUserId; private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); + private final Context mContext; private PendingIntent mLastMediaButtonReceiver; + private ComponentName mRestoredMediaButtonReceiver; public UserRecord(Context context, int userId) { + mContext = context; mUserId = userId; + restoreMediaButtonReceiver(); } public void startLocked() { - // nothing for now } public void stopLocked() { @@ -548,6 +558,7 @@ public class MediaSessionService extends SystemService implements Monitor { pw.println(prefix + "Record for user " + mUserId); String indent = prefix + " "; pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver); + pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver); int size = mSessions.size(); pw.println(indent + size + " Sessions:"); for (int i = 0; i < size; i++) { @@ -556,6 +567,19 @@ public class MediaSessionService extends SystemService implements Monitor { pw.println(indent + mSessions.get(i).toString()); } } + + private void restoreMediaButtonReceiver() { + String receiverName = Settings.Secure.getStringForUser(mContentResolver, + Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT); + if (!TextUtils.isEmpty(receiverName)) { + ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); + if (eventReceiver == null) { + // an invalid name was persisted + return; + } + mRestoredMediaButtonReceiver = eventReceiver; + } + } } final class SessionsListenerRecord implements IBinder.DeathRecipient { @@ -723,8 +747,9 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { // If we don't have a media button receiver to fall back on // include non-playing sessions for dispatching - boolean useNotPlayingSessions = mUserRecords.get( - ActivityManager.getCurrentUser()).mLastMediaButtonReceiver == null; + UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser()); + boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null + && ur.mRestoredMediaButtonReceiver == null; MediaSessionRecord session = mPriorityStack .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions); if (isVoiceKey(keyEvent.getKeyCode())) { @@ -929,9 +954,12 @@ public class MediaSessionService extends SystemService implements Monitor { // Launch the last PendingIntent we had with priority int userId = ActivityManager.getCurrentUser(); UserRecord user = mUserRecords.get(userId); - if (user.mLastMediaButtonReceiver != null) { + if (user.mLastMediaButtonReceiver != null + || user.mRestoredMediaButtonReceiver != null) { if (DEBUG) { - Log.d(TAG, "Sending media key to last known PendingIntent"); + Log.d(TAG, "Sending media key to last known PendingIntent " + + user.mLastMediaButtonReceiver + " or restored Intent " + + user.mRestoredMediaButtonReceiver); } if (needWakeLock) { mKeyEventReceiver.aquireWakeLockLocked(); @@ -939,9 +967,15 @@ public class MediaSessionService extends SystemService implements Monitor { Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); try { - user.mLastMediaButtonReceiver.send(getContext(), - needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, - mediaButtonIntent, mKeyEventReceiver, null); + if (user.mLastMediaButtonReceiver != null) { + user.mLastMediaButtonReceiver.send(getContext(), + needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, + mediaButtonIntent, mKeyEventReceiver, null); + } else { + mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver); + getContext().sendBroadcastAsUser(mediaButtonIntent, + new UserHandle(userId)); + } } catch (CanceledException e) { Log.i(TAG, "Error sending key event to media button receiver " + user.mLastMediaButtonReceiver, e); diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 2af56fe..50b2262 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/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 3b27cd2..a04f6cb 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -52,7 +52,7 @@ class AppWindowToken extends WindowToken { final boolean voiceInteraction; - int groupId = -1; + Task mTask; boolean appFullscreen; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean layoutConfigChanges; @@ -256,7 +256,8 @@ class AppWindowToken extends WindowToken { mIsExiting = false; removeAllWindows(); - final Task task = service.mTaskIdToTask.get(groupId); + // Use local variable because removeAppToken will null out mTask. + final Task task = mTask; if (task != null) { if (!task.removeAppToken(this)) { Slog.e(WindowManagerService.TAG, "removeAppFromTaskLocked: token=" + this @@ -295,8 +296,8 @@ class AppWindowToken extends WindowToken { if (allAppWindows.size() > 0) { pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows); } - pw.print(prefix); pw.print("groupId="); pw.print(groupId); - pw.print(" appFullscreen="); pw.print(appFullscreen); + pw.print(prefix); pw.print("task="); pw.println(mTask); + pw.print(prefix); pw.print(" appFullscreen="); pw.print(appFullscreen); pw.print(" requestedOrientation="); pw.println(requestedOrientation); pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested); pw.print(" clientHidden="); pw.print(clientHidden); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e3b53b0..8bbc5a9 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -379,7 +379,7 @@ class DisplayContent { ArrayList<Task> tasks = stack.getTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task task = tasks.get(taskNdx); - pw.print(" mTaskId="); pw.println(task.taskId); + pw.print(" mTaskId="); pw.println(task.mTaskId); AppTokenList tokens = task.mAppTokens; for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) { final AppWindowToken wtoken = tokens.get(tokenNdx); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 80d727d..a9b26e2 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -26,14 +26,13 @@ import com.android.server.EventLogTags; class Task { TaskStack mStack; final AppTokenList mAppTokens = new AppTokenList(); - final int taskId; + final int mTaskId; final int mUserId; boolean mDeferRemoval = false; final WindowManagerService mService; - Task(AppWindowToken wtoken, TaskStack stack, int userId, WindowManagerService service) { - taskId = wtoken.groupId; - mAppTokens.add(wtoken); + Task(int taskId, TaskStack stack, int userId, WindowManagerService service) { + mTaskId = taskId; mStack = stack; mUserId = userId; mService = service; @@ -45,38 +44,47 @@ class Task { void addAppToken(int addPos, AppWindowToken wtoken) { final int lastPos = mAppTokens.size(); - for (int pos = 0; pos < lastPos && pos < addPos; ++pos) { - if (mAppTokens.get(pos).removed) { - // addPos assumes removed tokens are actually gone. - ++addPos; + if (addPos >= lastPos) { + addPos = lastPos; + } else { + for (int pos = 0; pos < lastPos && pos < addPos; ++pos) { + if (mAppTokens.get(pos).removed) { + // addPos assumes removed tokens are actually gone. + ++addPos; + } } } mAppTokens.add(addPos, wtoken); + wtoken.mTask = this; mDeferRemoval = false; } void removeLocked() { if (!mAppTokens.isEmpty() && mStack.isAnimating()) { - if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + taskId); + if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId); mDeferRemoval = true; return; } - if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + taskId); - EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, taskId, "removeTask"); + if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId); + EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask"); mDeferRemoval = false; mStack.removeTask(this); - mService.mTaskIdToTask.delete(taskId); + mService.mTaskIdToTask.delete(mTaskId); } boolean removeAppToken(AppWindowToken wtoken) { boolean removed = mAppTokens.remove(wtoken); if (mAppTokens.size() == 0) { - EventLog.writeEvent(com.android.server.EventLogTags.WM_TASK_REMOVED, taskId, + EventLog.writeEvent(com.android.server.EventLogTags.WM_TASK_REMOVED, mTaskId, "removeAppToken: last token"); if (mDeferRemoval) { removeLocked(); } } + wtoken.mTask = null; + /* Leave mTaskId for now, it might be useful for debug + wtoken.mTaskId = -1; + */ return removed; } @@ -88,6 +96,6 @@ class Task { @Override public String toString() { - return "{taskId=" + taskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}"; + return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}"; } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index d6927e1..5d73149 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -222,7 +222,7 @@ public class TaskStack { if (toTop) { mDisplayContent.moveStack(this, true); } - EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx); + EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx); } void moveTaskToTop(Task task) { @@ -252,10 +252,9 @@ public class TaskStack { } mDisplayContent.layoutNeeded = true; } - final int taskId = task.taskId; for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); - if (wtoken.groupId == taskId) { + if (wtoken.mTask == task) { wtoken.mIsExiting = false; mExitingAppTokens.remove(appNdx); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 46db132..a30dc91 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3612,15 +3612,15 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } - private Task createTask(int taskId, int stackId, int userId, AppWindowToken atoken) { - if (DEBUG_STACK) Slog.i(TAG, "createTask: taskId=" + taskId + " stackId=" + stackId + private Task createTaskLocked(int taskId, int stackId, int userId, AppWindowToken atoken) { + if (DEBUG_STACK) Slog.i(TAG, "createTaskLocked: taskId=" + taskId + " stackId=" + stackId + " atoken=" + atoken); final TaskStack stack = mStackIdToStack.get(stackId); if (stack == null) { throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId); } EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId); - Task task = new Task(atoken, stack, userId, this); + Task task = new Task(taskId, stack, userId, this); mTaskIdToTask.put(taskId, task); stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */); return task; @@ -3657,7 +3657,6 @@ public class WindowManagerService extends IWindowManager.Stub } atoken = new AppWindowToken(this, token, voiceInteraction); atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; - atoken.groupId = taskId; atoken.appFullscreen = fullscreen; atoken.showWhenLocked = showWhenLocked; atoken.requestedOrientation = requestedOrientation; @@ -3669,10 +3668,9 @@ public class WindowManagerService extends IWindowManager.Stub Task task = mTaskIdToTask.get(taskId); if (task == null) { - createTask(taskId, stackId, userId, atoken); - } else { - task.addAppToken(addPos, atoken); + task = createTaskLocked(taskId, stackId, userId, atoken); } + task.addAppToken(addPos, atoken); mTokenMap.put(token.asBinder(), atoken); @@ -3685,28 +3683,27 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setAppGroupId(IBinder token, int groupId) { + public void setAppTask(IBinder token, int taskId) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, - "setAppGroupId()")) { + "setAppTask()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { final AppWindowToken atoken = findAppWindowToken(token); if (atoken == null) { - Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token); + Slog.w(TAG, "Attempted to set task id of non-existing app token: " + token); return; } - final Task oldTask = mTaskIdToTask.get(atoken.groupId); + final Task oldTask = atoken.mTask; oldTask.removeAppToken(atoken); - atoken.groupId = groupId; - Task newTask = mTaskIdToTask.get(groupId); + Task newTask = mTaskIdToTask.get(taskId); if (newTask == null) { - newTask = createTask(groupId, oldTask.mStack.mStackId, oldTask.mUserId, atoken); - } else { - newTask.mAppTokens.add(atoken); + newTask = + createTaskLocked(taskId, oldTask.mStack.mStackId, oldTask.mUserId, atoken); } + newTask.addAppToken(Integer.MAX_VALUE /* at top */, atoken); } } @@ -3990,7 +3987,7 @@ public class WindowManagerService extends IWindowManager.Stub void setFocusedStackFrame() { final TaskStack stack; if (mFocusedApp != null) { - Task task = mTaskIdToTask.get(mFocusedApp.groupId); + final Task task = mFocusedApp.mTask; stack = task.mStack; final DisplayContent displayContent = task.getDisplayContent(); if (displayContent != null) { @@ -4812,7 +4809,7 @@ public class WindowManagerService extends IWindowManager.Stub + " animating=" + wtoken.mAppAnimator.animating); if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken: " + wtoken + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); - final TaskStack stack = mTaskIdToTask.get(wtoken.groupId).mStack; + final TaskStack stack = wtoken.mTask.mStack; if (delayed && !wtoken.allAppWindows.isEmpty()) { // set the token aside because it has an active animation to be finished if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, @@ -4877,7 +4874,7 @@ public class WindowManagerService extends IWindowManager.Stub final int numTasks = tasks.size(); for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { final Task task = tasks.get(taskNdx); - Slog.v(TAG, " Task #" + task.taskId + " activities from bottom to top:"); + Slog.v(TAG, " Task #" + task.mTaskId + " activities from bottom to top:"); AppTokenList tokens = task.mAppTokens; final int numTokens = tokens.size(); for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a22ba33..98f00de 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -793,14 +793,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { TaskStack getStack() { AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken; if (wtoken != null) { - Task task = mService.mTaskIdToTask.get(wtoken.groupId); + Task task = wtoken.mTask; if (task != null) { if (task.mStack != null) { return task.mStack; } Slog.e(TAG, "getStack: mStack null for task=" + task); } else { - Slog.e(TAG, "getStack: " + this + " couldn't find taskId=" + wtoken.groupId + Slog.e(TAG, "getStack: " + this + " couldn't find task for " + wtoken + " Callers=" + Debug.getCallers(4)); } } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 6621726..1a6b292 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 f8d7539..d2030f2 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(); diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 62c92a1..e44969d 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -103,7 +103,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.setAppGroupId(null, 0); + mWm.setAppTask(null, 0); fail("IWindowManager.setAppGroupId did not throw SecurityException as" + " expected"); } catch (SecurityException e) { diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 5176419..d90271b 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -293,7 +293,7 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException { + public void setAppTask(IBinder arg0, int arg1) throws RemoteException { // TODO Auto-generated method stub } |