diff options
Diffstat (limited to 'core/java')
70 files changed, 3097 insertions, 1684 deletions
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index f13d940..d3e10f3 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -166,6 +166,7 @@ public class LayoutTransition { * we cache all of the current animations in this map for possible cancellation on * another layout event. */ + private final HashMap<View, Animator> pendingAnimations = new HashMap<View, Animator>(); private final HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>(); private final HashMap<View, Animator> currentVisibilityAnimations = new HashMap<View, Animator>(); @@ -542,6 +543,8 @@ public class LayoutTransition { // reset the inter-animation delay, in case we use it later staggerDelay = 0; + final long duration = (changeReason == APPEARING) ? + mChangingAppearingDuration : mChangingDisappearingDuration; final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup if (!observer.isAlive()) { @@ -556,12 +559,6 @@ public class LayoutTransition { // only animate the views not being added or removed if (child != newView) { - // If there's an animation running on this view already, cancel it - Animator currentAnimation = currentChangingAnimations.get(child); - if (currentAnimation != null) { - currentAnimation.cancel(); - currentChangingAnimations.remove(child); - } // Make a copy of the appropriate animation final Animator anim = baseAnimator.clone(); @@ -573,6 +570,30 @@ public class LayoutTransition { // its target object anim.setupStartValues(); + // If there's an animation running on this view already, cancel it + Animator currentAnimation = pendingAnimations.get(child); + if (currentAnimation != null) { + currentAnimation.cancel(); + pendingAnimations.remove(child); + } + // Cache the animation in case we need to cancel it later + pendingAnimations.put(child, anim); + + // For the animations which don't get started, we have to have a means of + // removing them from the cache, lest we leak them and their target objects. + // We run an animator for the default duration+100 (an arbitrary time, but one + // which should far surpass the delay between setting them up here and + // handling layout events which start them. + ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f). + setDuration(duration+100); + pendingAnimRemover.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + pendingAnimations.remove(child); + } + }); + pendingAnimRemover.start(); + // Add a listener to track layout changes on this view. If we don't get a callback, // then there's nothing to animate. final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() { @@ -583,19 +604,25 @@ public class LayoutTransition { anim.setupEndValues(); long startDelay; - long duration; if (changeReason == APPEARING) { startDelay = mChangingAppearingDelay + staggerDelay; staggerDelay += mChangingAppearingStagger; - duration = mChangingAppearingDuration; } else { startDelay = mChangingDisappearingDelay + staggerDelay; staggerDelay += mChangingDisappearingStagger; - duration = mChangingDisappearingDuration; } anim.setStartDelay(startDelay); anim.setDuration(duration); + Animator prevAnimation = currentChangingAnimations.get(child); + if (prevAnimation != null) { + prevAnimation.cancel(); + currentChangingAnimations.remove(child); + } + Animator pendingAnimation = pendingAnimations.get(child); + if (pendingAnimation != null) { + pendingAnimations.remove(child); + } // Cache the animation in case we need to cancel it later currentChangingAnimations.put(child, anim); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 055984f..8b6b819 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -655,7 +655,7 @@ public class Activity extends ContextThemeWrapper boolean mCalled; boolean mCheckedForLoaderManager; boolean mLoadersStarted; - private boolean mResumed; + /*package*/ boolean mResumed; private boolean mStopped; boolean mFinished; boolean mStartedActivity; @@ -4268,7 +4268,7 @@ public class Activity extends ContextThemeWrapper mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); - mWindow.getLayoutInflater().setFactory2(this); + mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } @@ -4363,9 +4363,8 @@ public class Activity extends ContextThemeWrapper mLastNonConfigurationInstances = null; - // First call onResume() -before- setting mResumed, so we don't - // send out any status bar / menu notifications the client makes. mCalled = false; + // mResumed is set by the instrumentation mInstrumentation.callActivityOnResume(this); if (!mCalled) { throw new SuperNotCalledException( @@ -4374,7 +4373,6 @@ public class Activity extends ContextThemeWrapper } // Now really resume, and install the current status bar and menu. - mResumed = true; mCalled = false; mFragments.dispatchResume(); @@ -4399,6 +4397,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPause()"); } + mResumed = false; } final void performUserLeaving() { @@ -4461,7 +4460,10 @@ public class Activity extends ContextThemeWrapper } } - final boolean isResumed() { + /** + * @hide + */ + public final boolean isResumed() { return mResumed; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 960b943..8f9a76b 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -193,6 +193,9 @@ public final class ActivityThread { final HashMap<IBinder, ProviderClientRecord> mLocalProviders = new HashMap<IBinder, ProviderClientRecord>(); + final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners + = new HashMap<Activity, ArrayList<OnActivityPausedListener>>(); + final GcIdler mGcIdler = new GcIdler(); boolean mGcIdlerScheduled = false; @@ -1514,6 +1517,28 @@ public final class ActivityThread { } } + public void registerOnActivityPausedListener(Activity activity, + OnActivityPausedListener listener) { + synchronized (mOnPauseListeners) { + ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity); + if (list == null) { + list = new ArrayList<OnActivityPausedListener>(); + mOnPauseListeners.put(activity, list); + } + list.add(listener); + } + } + + public void unregisterOnActivityPausedListener(Activity activity, + OnActivityPausedListener listener) { + synchronized (mOnPauseListeners) { + ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity); + if (list != null) { + list.remove(listener); + } + } + } + public final ActivityInfo resolveActivityInfo(Intent intent) { ActivityInfo aInfo = intent.resolveActivityInfo( mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES); @@ -2454,6 +2479,17 @@ public final class ActivityThread { } } r.paused = true; + + // Notify any outstanding on paused listeners + ArrayList<OnActivityPausedListener> listeners; + synchronized (mOnPauseListeners) { + listeners = mOnPauseListeners.remove(r.activity); + } + int size = (listeners != null ? listeners.size() : 0); + for (int i = 0; i < size; i++) { + listeners.get(i).onPaused(r.activity); + } + return state; } diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index 428f4e3..e83d104 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -58,29 +58,69 @@ import android.widget.ListView; public class AlertDialog extends Dialog implements DialogInterface { private AlertController mAlert; + /** + * Special theme constant for {@link #AlertDialog(Context, int)}: use + * the traditional (pre-Holo) alert dialog theme. + */ + public static final int THEME_TRADITIONAL = 1; + + /** + * Special theme constant for {@link #AlertDialog(Context, int)}: use + * the holographic alert theme with a dark background. + */ + public static final int THEME_HOLO_DARK = 2; + + /** + * Special theme constant for {@link #AlertDialog(Context, int)}: use + * the holographic alert theme with a light background. + */ + public static final int THEME_HOLO_LIGHT = 3; + protected AlertDialog(Context context) { - this(context, getDefaultDialogTheme(context)); + this(context, resolveDialogTheme(context, 0), true); } + /** + * Construct an AlertDialog that uses an explicit theme. The actual style + * that an AlertDialog uses is a private implementation, however you can + * here supply either the name of an attribute in the theme from which + * to get the dialog's style (such as {@link android.R.attr#alertDialogTheme} + * or one of the constants {@link #THEME_TRADITIONAL}, + * {@link #THEME_HOLO_DARK}, or {@link #THEME_HOLO_LIGHT}. + */ protected AlertDialog(Context context, int theme) { - super(context, theme == 0 ? getDefaultDialogTheme(context) : theme); + this(context, theme, true); + } + + AlertDialog(Context context, int theme, boolean createContextWrapper) { + super(context, resolveDialogTheme(context, theme), createContextWrapper); mWindow.alwaysReadCloseOnTouchAttr(); - mAlert = new AlertController(context, this, getWindow()); + mAlert = new AlertController(getContext(), this, getWindow()); } protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { - super(context, getDefaultDialogTheme(context)); + super(context, resolveDialogTheme(context, 0)); mWindow.alwaysReadCloseOnTouchAttr(); setCancelable(cancelable); setOnCancelListener(cancelListener); mAlert = new AlertController(context, this, getWindow()); } - private static int getDefaultDialogTheme(Context context) { - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme, - outValue, true); - return outValue.resourceId; + static int resolveDialogTheme(Context context, int resid) { + if (resid == THEME_TRADITIONAL) { + return com.android.internal.R.style.Theme_Dialog_Alert; + } else if (resid == THEME_HOLO_DARK) { + return com.android.internal.R.style.Theme_Holo_Dialog_Alert; + } else if (resid == THEME_HOLO_LIGHT) { + return com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert; + } else if (resid >= 0x01000000) { // start of real resource IDs. + return resid; + } else { + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme, + outValue, true); + return outValue.resourceId; + } } /** @@ -294,15 +334,23 @@ public class AlertDialog extends Dialog implements DialogInterface { * Constructor using a context for this builder and the {@link AlertDialog} it creates. */ public Builder(Context context) { - this(context, getDefaultDialogTheme(context)); + this(context, resolveDialogTheme(context, 0)); } /** * Constructor using a context and theme for this builder and - * the {@link AlertDialog} it creates. + * the {@link AlertDialog} it creates. The actual theme + * that an AlertDialog uses is a private implementation, however you can + * here supply either the name of an attribute in the theme from which + * to get the dialog's style (such as {@link android.R.attr#alertDialogTheme} + * or one of the constants + * {@link AlertDialog#THEME_TRADITIONAL AlertDialog.THEME_TRADITIONAL}, + * {@link AlertDialog#THEME_HOLO_DARK AlertDialog.THEME_HOLO_DARK}, or + * {@link AlertDialog#THEME_HOLO_LIGHT AlertDialog.THEME_HOLO_LIGHT}. */ public Builder(Context context, int theme) { - P = new AlertController.AlertParams(new ContextThemeWrapper(context, theme)); + P = new AlertController.AlertParams(new ContextThemeWrapper( + context, resolveDialogTheme(context, theme))); mTheme = theme; } @@ -840,7 +888,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * to do and want this to be created and displayed. */ public AlertDialog create() { - final AlertDialog dialog = new AlertDialog(P.mContext, mTheme); + final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); dialog.setOnCancelListener(P.mOnCancelListener); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index f4fa567..23d4065 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -119,7 +119,7 @@ public class Dialog implements DialogInterface, Window.Callback, * present its UI. */ public Dialog(Context context) { - this(context, 0); + this(context, 0, true); } /** @@ -135,6 +135,10 @@ public class Dialog implements DialogInterface, Window.Callback, * <var>context</var>. If 0, the default dialog theme will be used. */ public Dialog(Context context, int theme) { + this(context, theme, true); + } + + Dialog(Context context, int theme, boolean createContextWrapper) { if (theme == 0) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, @@ -142,7 +146,7 @@ public class Dialog implements DialogInterface, Window.Callback, theme = outValue.resourceId; } - mContext = new ContextThemeWrapper(context, theme); + mContext = createContextWrapper ? new ContextThemeWrapper(context, theme) : context; mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); Window w = PolicyManager.makeNewWindow(mContext); mWindow = w; @@ -152,7 +156,7 @@ public class Dialog implements DialogInterface, Window.Callback, mUiThread = Thread.currentThread(); mListenersHandler = new ListenersHandler(this); } - + /** * @deprecated * @hide diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index ad811d8..cd278be 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1149,6 +1149,7 @@ public class Instrumentation { * @param activity The activity being resumed. */ public void callActivityOnResume(Activity activity) { + activity.mResumed = true; activity.onResume(); if (mActivityMonitors != null) { diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 431be05..1ee386d 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -587,6 +587,7 @@ class LoaderManagerImpl extends LoaderManager { if (DEBUG) Log.v(TAG, " Removing last inactive loader: " + info); inactive.mDeliveredData = false; inactive.destroy(); + info.mLoader.abandon(); mInactiveLoaders.put(id, info); } else { // We already have an inactive loader for this ID that we are @@ -617,6 +618,7 @@ class LoaderManagerImpl extends LoaderManager { // Keep track of the previous instance of this loader so we can destroy // it when the new one completes. if (DEBUG) Log.v(TAG, " Making last loader inactive: " + info); + info.mLoader.abandon(); mInactiveLoaders.put(id, info); } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index de84c56..5049e19 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -927,9 +927,8 @@ public class Notification implements Parcelable if (mContentInfo != null) { contentView.setTextViewText(R.id.info, mContentInfo); } else if (mNumber > 0) { - if (mNumber > 100) { - contentView.setTextViewText(R.id.info, mContext.getString( - R.string.status_bar_notification_info_overflow)); + if (mNumber > 999) { + contentView.setTextViewText(R.id.info, "999+"); } else { NumberFormat f = NumberFormat.getIntegerInstance(); contentView.setTextViewText(R.id.info, f.format(mNumber)); diff --git a/core/java/android/app/OnActivityPausedListener.java b/core/java/android/app/OnActivityPausedListener.java new file mode 100644 index 0000000..379f133 --- /dev/null +++ b/core/java/android/app/OnActivityPausedListener.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.app; + +/** + * A listener that is called when an Activity is paused. Since this is tracked client side + * it should not be trusted to represent the exact current state, but can be used as a hint + * for cleanup. + * + * @hide + */ +public interface OnActivityPausedListener { + /** + * Called when the given activity is paused. + */ + public void onPaused(Activity activity); +} diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 97e6931..1af0983 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -22,6 +22,7 @@ import android.os.Binder; import android.os.RemoteException; import android.os.IBinder; import android.os.ServiceManager; +import android.view.View; import com.android.internal.statusbar.IStatusBarService; @@ -31,52 +32,24 @@ import com.android.internal.statusbar.IStatusBarService; * @hide */ public class StatusBarManager { - /** - * Flag for {@link #disable} to make the status bar not expandable. Unless you also - * set {@link #DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show. - */ - public static final int DISABLE_EXPAND = 0x00000001; - - /** - * Flag for {@link #disable} to hide notification icons and scrolling ticker text. - */ - public static final int DISABLE_NOTIFICATION_ICONS = 0x00000002; - - /** - * Flag for {@link #disable} to disable incoming notification alerts. This will not block - * icons, but it will block sound, vibrating and other visual or aural notifications. - */ - public static final int DISABLE_NOTIFICATION_ALERTS = 0x00000004; - /** - * Flag for {@link #disable} to hide only the scrolling ticker. Note that - * {@link #DISABLE_NOTIFICATION_ICONS} implies {@link #DISABLE_NOTIFICATION_TICKER}. - */ - public static final int DISABLE_NOTIFICATION_TICKER = 0x00000008; + public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND; + public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS; + public static final int DISABLE_NOTIFICATION_ALERTS + = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS; + public static final int DISABLE_NOTIFICATION_TICKER + = View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER; + public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO; + public static final int DISABLE_NAVIGATION = View.STATUS_BAR_DISABLE_NAVIGATION; + public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK; + public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK; - /** - * Flag for {@link #disable} to hide the center system info area. - */ - public static final int DISABLE_SYSTEM_INFO = 0x00000010; - - /** - * Flag for {@link #disable} to hide only the navigation buttons. Don't use this - * unless you're a special part of the system UI (i.e., setup wizard, keyguard). - */ - public static final int DISABLE_NAVIGATION = 0x00000020; - - /** - * Flag for {@link #disable} to hide only the clock. You might use this if your activity has - * its own clock making the status bar's clock redundant. - */ - public static final int DISABLE_CLOCK = 0x00000040; - - - /** - * Re-enable all of the status bar features that you've disabled. - */ public static final int DISABLE_NONE = 0x00000000; + public static final int DISABLE_MASK = DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS + | DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER + | DISABLE_SYSTEM_INFO| DISABLE_NAVIGATION | DISABLE_BACK | DISABLE_CLOCK; + private Context mContext; private IStatusBarService mService; private IBinder mToken = new Binder(); diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java index a7dd5fb..383cb6b 100644 --- a/core/java/android/content/AsyncTaskLoader.java +++ b/core/java/android/content/AsyncTaskLoader.java @@ -169,11 +169,6 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { * to properly dispose of the result. */ public void onCanceled(D data) { - onCancelled(data); - } - - @Deprecated - public void onCancelled(D data) { } void executePendingTask() { @@ -214,10 +209,15 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "Load complete of old task, trying to cancel"); dispatchOnCancelled(task, data); } else { - mLastLoadCompleteTime = SystemClock.uptimeMillis(); - mTask = null; - if (DEBUG) Slog.v(TAG, "Delivering result"); - deliverResult(data); + if (isAbandoned()) { + // This cursor has been abandoned; just cancel the new data. + onCanceled(data); + } else { + mLastLoadCompleteTime = SystemClock.uptimeMillis(); + mTask = null; + if (DEBUG) Slog.v(TAG, "Delivering result"); + deliverResult(data); + } } } diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index d63fe69..a9d6117 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -45,6 +45,7 @@ public class Loader<D> { OnLoadCompleteListener<D> mListener; Context mContext; boolean mStarted = false; + boolean mAbandoned = false; boolean mReset = true; boolean mContentChanged = false; @@ -151,6 +152,15 @@ public class Loader<D> { } /** + * Return whether this loader has been abandoned. In this state, the + * loader <em>must not</em> report any new data, and <em>must</em> keep + * its last reported data valid until it is finally reset. + */ + public boolean isAbandoned() { + return mAbandoned; + } + + /** * Return whether this load has been reset. That is, either the loader * has not yet been started for the first time, or its {@link #reset()} * has been called. @@ -177,6 +187,7 @@ public class Loader<D> { public final void startLoading() { mStarted = true; mReset = false; + mAbandoned = false; onStartLoading(); } @@ -236,6 +247,28 @@ public class Loader<D> { } /** + * Tell the Loader that it is being abandoned. This is called prior + * to {@link #reset} to have it retain its current data but not report + * any new data. + */ + public void abandon() { + mAbandoned = true; + onAbandon(); + } + + /** + * Subclasses implement this to take care of being abandoned. This is + * an optional intermediate state prior to {@link #onReset()} -- it means that + * the client is no longer interested in any new data from the loader, + * so the loader must not report any further updates. However, the + * loader <em>must</em> keep its last reported data valid until the final + * {@link #onReset()} happens. You can retrieve the current abandoned + * state with {@link #isAbandoned}. + */ + protected void onAbandon() { + } + + /** * Resets the state of the Loader. The Loader should at this point free * all of its resources, since it may never be called again; however, its * {@link #startLoading()} may later be called at which point it must be @@ -251,6 +284,7 @@ public class Loader<D> { onReset(); mReset = true; mStarted = false; + mAbandoned = false; mContentChanged = false; } @@ -327,6 +361,7 @@ public class Loader<D> { writer.print(" mListener="); writer.println(mListener); writer.print(prefix); writer.print("mStarted="); writer.print(mStarted); writer.print(" mContentChanged="); writer.print(mContentChanged); + writer.print(" mAbandoned="); writer.print(mAbandoned); writer.print(" mReset="); writer.println(mReset); } }
\ No newline at end of file diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 4a75514..f079e42 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -25,6 +25,7 @@ import android.os.ServiceManager; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.view.IRotationWatcher; import android.view.IWindowManager; import android.view.Surface; @@ -489,6 +490,8 @@ public class SensorManager private final Handler mHandler; private SensorEvent mValuesPool; public SparseBooleanArray mSensors = new SparseBooleanArray(); + public SparseBooleanArray mFirstEvent = new SparseBooleanArray(); + public SparseIntArray mSensorAccuracies = new SparseIntArray(); ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) { mSensorEventListener = listener; @@ -499,10 +502,30 @@ public class SensorManager mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { - SensorEvent t = (SensorEvent)msg.obj; - if (t.accuracy >= 0) { - mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy); + final SensorEvent t = (SensorEvent)msg.obj; + final int handle = t.sensor.getHandle(); + + switch (t.sensor.getType()) { + // Only report accuracy for sensors that support it. + case Sensor.TYPE_MAGNETIC_FIELD: + case Sensor.TYPE_ORIENTATION: + // call onAccuracyChanged() only if the value changes + final int accuracy = mSensorAccuracies.get(handle); + if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { + mSensorAccuracies.put(handle, t.accuracy); + mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy); + } + break; + default: + // For other sensors, just report the accuracy once + if (mFirstEvent.get(handle) == false) { + mFirstEvent.put(handle, true); + mSensorEventListener.onAccuracyChanged( + t.sensor, SENSOR_STATUS_ACCURACY_HIGH); + } + break; } + mSensorEventListener.onSensorChanged(t); returnToPool(t); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 255eb6c..feb246e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -219,7 +219,34 @@ import java.io.PrintWriter; public class InputMethodService extends AbstractInputMethodService { static final String TAG = "InputMethodService"; static final boolean DEBUG = false; - + + /** + * The back button will close the input window. + */ + public static final int BACK_DISPOSITION_DEFAULT = 0; // based on window + + /** + * This input method will not consume the back key. + */ + public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back + + /** + * This input method will consume the back key. + */ + public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down + + /** + * @hide + * The IME is active. It may or may not be visible. + */ + public static final int IME_ACTIVE = 0x1; + + /** + * @hide + * The IME is visible. + */ + public static final int IME_VISIBLE = 0x2; + InputMethodManager mImm; int mTheme = 0; @@ -271,6 +298,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean mIsInputViewShown; int mStatusIcon; + int mBackDisposition; final Insets mTmpInsets = new Insets(); final int[] mTmpLocation = new int[2]; @@ -394,9 +422,9 @@ public class InputMethodService extends AbstractInputMethodService { showWindow(true); } // If user uses hard keyboard, IME button should always be shown. - if (!onEvaluateInputViewShown()) { - mImm.setIMEButtonVisible(mToken, true); - } + boolean showing = onEvaluateInputViewShown(); + mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0), + mBackDisposition); if (resultReceiver != null) { resultReceiver.send(wasVis != isInputViewShown() ? InputMethodManager.RESULT_SHOWN @@ -704,9 +732,9 @@ public class InputMethodService extends AbstractInputMethodService { hideWindow(); } // If user uses hard keyboard, IME button should always be shown. - if (!onEvaluateInputViewShown()) { - mImm.setIMEButtonVisible(mToken, true); - } + boolean showing = onEvaluateInputViewShown(); + mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0), + mBackDisposition); } } @@ -736,6 +764,14 @@ public class InputMethodService extends AbstractInputMethodService { return mWindow; } + public void setBackDisposition(int disposition) { + mBackDisposition = disposition; + } + + public int getBackDisposition() { + return mBackDisposition; + } + /** * Return the maximum width, in pixels, available the input method. * Input methods are positioned at the bottom of the screen and, unless @@ -1029,7 +1065,7 @@ public class InputMethodService extends AbstractInputMethodService { public boolean onEvaluateInputViewShown() { Configuration config = getResources().getConfiguration(); return config.keyboard == Configuration.KEYBOARD_NOKEYS - || config.hardKeyboardHidden == Configuration.KEYBOARDHIDDEN_YES; + || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES; } /** @@ -1378,7 +1414,7 @@ public class InputMethodService extends AbstractInputMethodService { if (!wasVisible) { if (DEBUG) Log.v(TAG, "showWindow: showing!"); - mImm.setIMEButtonVisible(mToken, true); + mImm.setImeWindowStatus(mToken, IME_ACTIVE, mBackDisposition); onWindowShown(); mWindow.show(); } @@ -1394,7 +1430,7 @@ public class InputMethodService extends AbstractInputMethodService { } mInputViewStarted = false; mCandidatesViewStarted = false; - mImm.setIMEButtonVisible(mToken, false); + mImm.setImeWindowStatus(mToken, 0, mBackDisposition); if (mWindowVisible) { mWindow.hide(); mWindowVisible = false; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 7e809f5..cab8ed2 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -221,6 +221,8 @@ public class ConnectivityManager /** {@hide} */ public static final int TYPE_DUMMY = 8; + /** {@hide} */ + public static final int TYPE_ETHERNET = 9; /** {@hide} TODO: Need to adjust this for WiMAX. */ public static final int MAX_RADIO_TYPE = TYPE_DUMMY; /** {@hide} TODO: Need to adjust this for WiMAX. */ diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index a663fb8..d439a48 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -16,8 +16,12 @@ package android.nfc; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.IntentFilter; import android.nfc.NdefMessage; import android.nfc.Tag; +import android.nfc.TechListParcel; import android.nfc.ILlcpSocket; import android.nfc.ILlcpServiceSocket; import android.nfc.ILlcpConnectionlessSocket; @@ -44,6 +48,11 @@ interface INfcAdapter NdefMessage localGet(); void localSet(in NdefMessage message); void openTagConnection(in Tag tag); + void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent, + in IntentFilter[] filters, in TechListParcel techLists); + void disableForegroundDispatch(in ComponentName activity); + void enableForegroundNdefPush(in ComponentName activity, in NdefMessage msg); + void disableForegroundNdefPush(in ComponentName activity); // Non-public methods // TODO: check and complete diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl index 5d222d9..57dc38c 100644 --- a/core/java/android/nfc/INfcTag.aidl +++ b/core/java/android/nfc/INfcTag.aidl @@ -17,6 +17,7 @@ package android.nfc; import android.nfc.NdefMessage; +import android.nfc.TransceiveResult; /** * @hide @@ -30,7 +31,7 @@ interface INfcTag byte[] getUid(int nativeHandle); boolean isNdef(int nativeHandle); boolean isPresent(int nativeHandle); - byte[] transceive(int nativeHandle, in byte[] data, boolean raw); + TransceiveResult transceive(int nativeHandle, in byte[] data, boolean raw); int getLastError(int nativeHandle); @@ -39,4 +40,7 @@ interface INfcTag int ndefMakeReadOnly(int nativeHandle); boolean ndefIsWritable(int nativeHandle); int formatNdef(int nativeHandle, in byte[] key); + + void setIsoDepTimeout(int timeout); + void resetIsoDepTimeout(); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 758c8a0..4808032 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -18,11 +18,16 @@ package android.nfc; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; import android.app.ActivityThread; +import android.app.OnActivityPausedListener; +import android.app.PendingIntent; import android.content.Context; +import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.IBinder; +import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; @@ -43,7 +48,6 @@ public final class NfcAdapter { * * If any activities respond to this intent neither * {@link #ACTION_TECHNOLOGY_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started. - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; @@ -51,13 +55,12 @@ public final class NfcAdapter { /** * Intent to started when a tag is discovered. The data URI is formated as * {@code vnd.android.nfc://tag/} with the path having a directory entry for each technology - * in the {@link Tag#getTechnologyList()} is ascending order. + * in the {@link Tag#getTechList()} is sorted ascending order. * * This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before * {@link #ACTION_TAG_DISCOVERED} * * If any activities respond to this intent {@link #ACTION_TAG_DISCOVERED} will not be started. - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_TECHNOLOGY_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; @@ -76,7 +79,6 @@ public final class NfcAdapter { /** * Mandatory Tag extra for the ACTION_TAG intents. - * @hide */ public static final String EXTRA_TAG = "android.nfc.extra.TAG"; @@ -102,6 +104,20 @@ public final class NfcAdapter { "android.nfc.action.TRANSACTION_DETECTED"; /** + * Broadcast Action: an RF field ON has been detected. + * @hide + */ + public static final String ACTION_RF_FIELD_ON_DETECTED = + "android.nfc.action.RF_FIELD_ON_DETECTED"; + + /** + * Broadcast Action: an RF Field OFF has been detected. + * @hide + */ + public static final String ACTION_RF_FIELD_OFF_DETECTED = + "android.nfc.action.RF_FIELD_OFF_DETECTED"; + + /** * Broadcast Action: an adapter's state changed between enabled and disabled. * * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains @@ -197,8 +213,7 @@ public final class NfcAdapter { // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort // recovery private static INfcAdapter sService; - - private final Context mContext; + private static INfcTag sTagService; /** * Helper to check if this device has FEATURE_NFC, but without using @@ -235,6 +250,12 @@ public final class NfcAdapter { Log.e(TAG, "could not retrieve NFC service"); return null; } + try { + sTagService = sService.getNfcTagInterface(); + } catch (RemoteException e) { + Log.e(TAG, "could not retrieve NFC Tag service"); + return null; + } } return sService; } @@ -289,7 +310,6 @@ public final class NfcAdapter { if (setupService() == null) { throw new UnsupportedOperationException(); } - mContext = context; } /** @@ -297,10 +317,20 @@ public final class NfcAdapter { * @hide */ public INfcAdapter getService() { + isEnabled(); // NOP call to recover sService if it is stale return sService; } /** + * Returns the binder interface to the tag service. + * @hide + */ + public INfcTag getTagService() { + isEnabled(); // NOP call to recover sTagService if it is stale + return sTagService; + } + + /** * NFC service dead - attempt best effort recovery * @hide */ @@ -309,11 +339,21 @@ public final class NfcAdapter { INfcAdapter service = getServiceInterface(); if (service == null) { Log.e(TAG, "could not retrieve NFC service during service recovery"); + // nothing more can be done now, sService is still stale, we'll hit + // this recovery path again later return; } - /* assigning to sService is not thread-safe, but this is best-effort code - * and on a well-behaved system should never happen */ + // assigning to sService is not thread-safe, but this is best-effort code + // and on a well-behaved system should never happen sService = service; + try { + sTagService = service.getNfcTagInterface(); + } catch (RemoteException ee) { + Log.e(TAG, "could not retrieve NFC tag service during service recovery"); + // nothing more can be done now, sService is still stale, we'll hit + // this recovery path again later + } + return; } @@ -374,6 +414,136 @@ public final class NfcAdapter { } /** + * Enables foreground dispatching to the given Activity. This will force all NFC Intents that + * match the given filters to be delivered to the activity bypassing the standard dispatch + * mechanism. If no IntentFilters are given all the PendingIntent will be invoked for every + * dispatch Intent. + * + * This method must be called from the main thread. + * + * @param activity the Activity to dispatch to + * @param intent the PendingIntent to start for the dispatch + * @param filters the IntentFilters to override dispatching for, or null to always dispatch + * @throws IllegalStateException + */ + public void enableForegroundDispatch(Activity activity, PendingIntent intent, + IntentFilter[] filters, String[][] techLists) { + if (activity == null || intent == null) { + throw new NullPointerException(); + } + if (!activity.isResumed()) { + throw new IllegalStateException("Foregorund dispatching can only be enabled " + + "when your activity is resumed"); + } + try { + TechListParcel parcel = null; + if (techLists != null && techLists.length > 0) { + parcel = new TechListParcel(techLists); + } + ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, + mForegroundDispatchListener); + sService.enableForegroundDispatch(activity.getComponentName(), intent, filters, + parcel); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** + * Disables foreground activity dispatching setup with + * {@link #enableForegroundDispatch}. + * + * <p>This must be called before the Activity returns from + * it's <code>onPause()</code> or this method will throw an IllegalStateException. + * + * <p>This method must be called from the main thread. + */ + public void disableForegroundDispatch(Activity activity) { + ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, + mForegroundDispatchListener); + disableForegroundDispatchInternal(activity, false); + } + + OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() { + @Override + public void onPaused(Activity activity) { + disableForegroundDispatchInternal(activity, true); + } + }; + + void disableForegroundDispatchInternal(Activity activity, boolean force) { + try { + sService.disableForegroundDispatch(activity.getComponentName()); + if (!force && !activity.isResumed()) { + throw new IllegalStateException("You must disable forgeground dispatching " + + "while your activity is still resumed"); + } + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** + * Enable NDEF message push over P2P while this Activity is in the foreground. For this to + * function properly the other NFC device being scanned must support the "com.android.npp" + * NDEF push protocol. + * + * <p><em>NOTE</em> While foreground NDEF push is active standard tag dispatch is disabled. + * Only the foreground activity may receive tag discovered dispatches via + * {@link #enableForegroundDispatch}. + */ + public void enableForegroundNdefPush(Activity activity, NdefMessage msg) { + if (activity == null || msg == null) { + throw new NullPointerException(); + } + if (!activity.isResumed()) { + throw new IllegalStateException("Foregorund NDEF push can only be enabled " + + "when your activity is resumed"); + } + try { + ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, + mForegroundNdefPushListener); + sService.enableForegroundNdefPush(activity.getComponentName(), msg); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** + * Disables foreground NDEF push setup with + * {@link #enableForegroundNdefPush}. + * + * <p>This must be called before the Activity returns from + * it's <code>onPause()</code> or this method will throw an IllegalStateException. + * + * <p>This method must be called from the main thread. + */ + public void disableForegroundNdefPush(Activity activity) { + ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, + mForegroundNdefPushListener); + disableForegroundNdefPushInternal(activity, false); + } + + OnActivityPausedListener mForegroundNdefPushListener = new OnActivityPausedListener() { + @Override + public void onPaused(Activity activity) { + disableForegroundNdefPushInternal(activity, true); + } + }; + + void disableForegroundNdefPushInternal(Activity activity, boolean force) { + try { + sService.disableForegroundNdefPush(activity.getComponentName()); + if (!force && !activity.isResumed()) { + throw new IllegalStateException("You must disable forgeground NDEF push " + + "while your activity is still resumed"); + } + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** * Set the NDEF Message that this NFC adapter should appear as to Tag * readers. * <p> diff --git a/core/java/android/nfc/NfcSecureElement.java b/core/java/android/nfc/NfcSecureElement.java index 5f4c066..ea2846e 100755 --- a/core/java/android/nfc/NfcSecureElement.java +++ b/core/java/android/nfc/NfcSecureElement.java @@ -16,7 +16,7 @@ package android.nfc; -import android.nfc.technology.TagTechnology; +import android.nfc.tech.TagTechnology; import android.os.RemoteException; import android.util.Log; diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java index 7404950..aae75c9 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -16,36 +16,35 @@ package android.nfc; -import android.nfc.technology.IsoDep; -import android.nfc.technology.MifareClassic; -import android.nfc.technology.MifareUltralight; -import android.nfc.technology.NfcV; -import android.nfc.technology.Ndef; -import android.nfc.technology.NdefFormatable; -import android.nfc.technology.NfcA; -import android.nfc.technology.NfcB; -import android.nfc.technology.NfcF; -import android.nfc.technology.TagTechnology; +import android.nfc.tech.IsoDep; +import android.nfc.tech.MifareClassic; +import android.nfc.tech.MifareUltralight; +import android.nfc.tech.Ndef; +import android.nfc.tech.NdefFormatable; +import android.nfc.tech.NfcA; +import android.nfc.tech.NfcB; +import android.nfc.tech.NfcF; +import android.nfc.tech.NfcV; +import android.nfc.tech.TagTechnology; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import java.util.Arrays; /** * Represents a (generic) discovered tag. * <p> - * A tag is a passive NFC element, such as NFC Forum Tag's, Mifare class Tags, - * Sony Felica Tags. + * A tag is a passive NFC element, such as NFC Forum Tag's, MIFARE class Tags, + * Sony FeliCa Tags, etc. * <p> * Tag's have a type and usually have a UID. * <p> * {@link Tag} objects are passed to applications via the {@link NfcAdapter#EXTRA_TAG} extra * in {@link NfcAdapter#ACTION_TAG_DISCOVERED} intents. A {@link Tag} object is immutable * and represents the state of the tag at the time of discovery. It can be - * directly queried for its UID and Type, or used to create a {@link TagTechnology} - * (with {@link Tag#getTechnology(int)}). + * directly queried for its UID and Type, or used to create a {@link TagTechnology} using the + * static <code>get()</code> methods on the varios tech classes. * <p> * A {@link Tag} can be used to create a {@link TagTechnology} only while the tag is in * range. If it is removed and then returned to range, then the most recent @@ -55,13 +54,14 @@ import java.util.Arrays; * time and calls on this class will retrieve those read-only properties, and * not cause any further RF activity or block. Note however that arrays passed to and * returned by this class are *not* cloned, so be careful not to modify them. - * @hide */ public class Tag implements Parcelable { /*package*/ final byte[] mId; /*package*/ final int[] mTechList; + /*package*/ final String[] mTechStringList; /*package*/ final Bundle[] mTechExtras; /*package*/ final int mServiceHandle; // for use by NFC service, 0 indicates a mock + /*package*/ final INfcTag mTagService; /*package*/ int mConnectedTechnology; @@ -69,24 +69,26 @@ public class Tag implements Parcelable { * Hidden constructor to be used by NFC service and internal classes. * @hide */ - public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle) { + public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle, + INfcTag tagService) { if (techList == null) { throw new IllegalArgumentException("rawTargets cannot be null"); } mId = id; mTechList = Arrays.copyOf(techList, techList.length); + mTechStringList = generateTechStringList(techList); // Ensure mTechExtras is as long as mTechList mTechExtras = Arrays.copyOf(techListExtras, techList.length); mServiceHandle = serviceHandle; + mTagService = tagService; mConnectedTechnology = -1; } /** * Construct a mock Tag. - * <p>This is an application constructed tag, so NfcAdapter methods on this - * Tag such as {@link #getTechnology} may fail with - * {@link IllegalArgumentException} since it does not represent a physical Tag. + * <p>This is an application constructed tag, so NfcAdapter methods on this Tag may fail + * with {@link IllegalArgumentException} since it does not represent a physical Tag. * <p>This constructor might be useful for mock testing. * @param id The tag identifier, can be null * @param techList must not be null @@ -94,7 +96,46 @@ public class Tag implements Parcelable { */ public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras) { // set serviceHandle to 0 to indicate mock tag - return new Tag(id, techList, techListExtras, 0); + return new Tag(id, techList, techListExtras, 0, null); + } + + private String[] generateTechStringList(int[] techList) { + final int size = techList.length; + String[] strings = new String[size]; + for (int i = 0; i < size; i++) { + switch (techList[i]) { + case TagTechnology.ISO_DEP: + strings[i] = IsoDep.class.getName(); + break; + case TagTechnology.MIFARE_CLASSIC: + strings[i] = MifareClassic.class.getName(); + break; + case TagTechnology.MIFARE_ULTRALIGHT: + strings[i] = MifareUltralight.class.getName(); + break; + case TagTechnology.NDEF: + strings[i] = Ndef.class.getName(); + break; + case TagTechnology.NDEF_FORMATABLE: + strings[i] = NdefFormatable.class.getName(); + break; + case TagTechnology.NFC_A: + strings[i] = NfcA.class.getName(); + break; + case TagTechnology.NFC_B: + strings[i] = NfcB.class.getName(); + break; + case TagTechnology.NFC_F: + strings[i] = NfcF.class.getName(); + break; + case TagTechnology.NFC_V: + strings[i] = NfcV.class.getName(); + break; + default: + throw new IllegalArgumentException("Unknown tech type " + techList[i]); + } + } + return strings; } /** @@ -119,19 +160,24 @@ public class Tag implements Parcelable { * Returns technologies present in the tag that this implementation understands, * or a zero length array if there are no supported technologies on this tag. * - * The elements of the list are guaranteed be one of the constants defined in - * {@link TagTechnology}. + * The elements of the list are the names of the classes implementing the technology. * * The ordering of the returned array is undefined and should not be relied upon. */ - public int[] getTechnologyList() { - return Arrays.copyOf(mTechList, mTechList.length); + public String[] getTechList() { + return mTechStringList; } - /** - * Returns the technology, or null if not present - */ - public TagTechnology getTechnology(NfcAdapter adapter, int tech) { + /** @hide */ + public boolean hasTech(int techType) { + for (int tech : mTechList) { + if (tech == techType) return true; + } + return false; + } + + /** @hide */ + public Bundle getTechExtras(int tech) { int pos = -1; for (int idx = 0; idx < mTechList.length; idx++) { if (mTechList[idx] == tech) { @@ -143,44 +189,12 @@ public class Tag implements Parcelable { return null; } - Bundle extras = mTechExtras[pos]; - try { - switch (tech) { - case TagTechnology.NFC_A: { - return new NfcA(adapter, this, extras); - } - case TagTechnology.NFC_B: { - return new NfcB(adapter, this, extras); - } - case TagTechnology.ISO_DEP: { - return new IsoDep(adapter, this, extras); - } - case TagTechnology.NFC_V: { - return new NfcV(adapter, this, extras); - } - case TagTechnology.NDEF: { - return new Ndef(adapter, this, tech, extras); - } - case TagTechnology.NDEF_FORMATABLE: { - return new NdefFormatable(adapter, this, tech, extras); - } - case TagTechnology.NFC_F: { - return new NfcF(adapter, this, extras); - } - case TagTechnology.MIFARE_CLASSIC: { - return new MifareClassic(adapter, this, extras); - } - case TagTechnology.MIFARE_ULTRALIGHT: { - return new MifareUltralight(adapter, this, extras); - } + return mTechExtras[pos]; + } - default: { - throw new UnsupportedOperationException("Tech " + tech + " not supported"); - } - } - } catch (RemoteException e) { - return null; - } + /** @hide */ + public INfcTag getTagService() { + return mTagService; } @Override @@ -222,25 +236,41 @@ public class Tag implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + // Null mTagService means this is a mock tag + int isMock = (mTagService == null)?1:0; + writeBytesWithNull(dest, mId); dest.writeInt(mTechList.length); dest.writeIntArray(mTechList); dest.writeTypedArray(mTechExtras, 0); dest.writeInt(mServiceHandle); + dest.writeInt(isMock); + if (isMock == 0) { + dest.writeStrongBinder(mTagService.asBinder()); + } } public static final Parcelable.Creator<Tag> CREATOR = new Parcelable.Creator<Tag>() { @Override public Tag createFromParcel(Parcel in) { + INfcTag tagService; + // Tag fields byte[] id = Tag.readBytesWithNull(in); int[] techList = new int[in.readInt()]; in.readIntArray(techList); Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR); int serviceHandle = in.readInt(); + int isMock = in.readInt(); + if (isMock == 0) { + tagService = INfcTag.Stub.asInterface(in.readStrongBinder()); + } + else { + tagService = null; + } - return new Tag(id, techList, techExtras, serviceHandle); + return new Tag(id, techList, techExtras, serviceHandle, tagService); } @Override @@ -249,7 +279,9 @@ public class Tag implements Parcelable { } }; - /* + /** + * For internal use only. + * * @hide */ public synchronized void setConnectedTechnology(int technology) { @@ -260,14 +292,18 @@ public class Tag implements Parcelable { } } - /* + /** + * For internal use only. + * * @hide */ public int getConnectedTechnology() { return mConnectedTechnology; } - /* + /** + * For internal use only. + * * @hide */ public void setTechnologyDisconnected() { diff --git a/core/java/android/nfc/TagLostException.java b/core/java/android/nfc/TagLostException.java new file mode 100644 index 0000000..1981d7c --- /dev/null +++ b/core/java/android/nfc/TagLostException.java @@ -0,0 +1,29 @@ +/* + * 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 android.nfc; + +import java.io.IOException; + +public class TagLostException extends IOException { + public TagLostException() { + super(); + } + + public TagLostException(String message) { + super(message); + } +} diff --git a/core/java/android/nfc/TechListParcel.aidl b/core/java/android/nfc/TechListParcel.aidl new file mode 100644 index 0000000..92e646f --- /dev/null +++ b/core/java/android/nfc/TechListParcel.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.nfc; + +parcelable TechListParcel;
\ No newline at end of file diff --git a/core/java/android/nfc/TechListParcel.java b/core/java/android/nfc/TechListParcel.java new file mode 100644 index 0000000..396f0f1 --- /dev/null +++ b/core/java/android/nfc/TechListParcel.java @@ -0,0 +1,66 @@ +/* + * 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 android.nfc; + +import android.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class TechListParcel implements Parcelable { + + private String[][] mTechLists; + + public TechListParcel(String[]... strings) { + mTechLists = strings; + } + + public String[][] getTechLists() { + return mTechLists; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + int count = mTechLists.length; + dest.writeInt(count); + for (int i = 0; i < count; i++) { + String[] techList = mTechLists[i]; + dest.writeStringArray(techList); + } + } + + public static final Creator<TechListParcel> CREATOR = new Creator<TechListParcel>() { + @Override + public TechListParcel createFromParcel(Parcel source) { + int count = source.readInt(); + String[][] techLists = new String[count][]; + for (int i = 0; i < count; i++) { + techLists[i] = source.readStringArray(); + } + return new TechListParcel(techLists); + } + + @Override + public TechListParcel[] newArray(int size) { + return new TechListParcel[size]; + } + }; +} diff --git a/core/java/android/nfc/TransceiveResult.aidl b/core/java/android/nfc/TransceiveResult.aidl new file mode 100644 index 0000000..98f92ee --- /dev/null +++ b/core/java/android/nfc/TransceiveResult.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.nfc; + +parcelable TransceiveResult; diff --git a/core/java/android/nfc/TransceiveResult.java b/core/java/android/nfc/TransceiveResult.java new file mode 100644 index 0000000..16244b8 --- /dev/null +++ b/core/java/android/nfc/TransceiveResult.java @@ -0,0 +1,90 @@ +/* + * 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 android.nfc; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Class used to pipe transceive result from the NFC service. + * + * @hide + */ +public final class TransceiveResult implements Parcelable { + private final boolean mTagLost; + private final boolean mSuccess; + private final byte[] mResponseData; + + public TransceiveResult(final boolean success, final boolean tagIsLost, + final byte[] data) { + mSuccess = success; + mTagLost = tagIsLost; + mResponseData = data; + } + + public boolean isSuccessful() { + return mSuccess; + } + + public boolean isTagLost() { + return mTagLost; + } + + public byte[] getResponseData() { + return mResponseData; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSuccess ? 1 : 0); + dest.writeInt(mTagLost ? 1 : 0); + if (mSuccess) { + dest.writeInt(mResponseData.length); + dest.writeByteArray(mResponseData); + } + } + + public static final Parcelable.Creator<TransceiveResult> CREATOR = + new Parcelable.Creator<TransceiveResult>() { + @Override + public TransceiveResult createFromParcel(Parcel in) { + boolean success = (in.readInt() == 1) ? true : false; + boolean tagLost = (in.readInt() == 1) ? true : false; + byte[] responseData; + + if (success) { + int responseLength = in.readInt(); + responseData = new byte[responseLength]; + in.readByteArray(responseData); + } else { + responseData = null; + } + return new TransceiveResult(success, tagLost, responseData); + } + + @Override + public TransceiveResult[] newArray(int size) { + return new TransceiveResult[size]; + } + }; + +} diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java new file mode 100644 index 0000000..32a850d --- /dev/null +++ b/core/java/android/nfc/tech/BasicTagTechnology.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.tech; + +import android.nfc.ErrorCodes; +import android.nfc.Tag; +import android.nfc.TagLostException; +import android.nfc.TransceiveResult; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; + +/** + * A base class for tag technologies that are built on top of transceive(). + */ +/* package */ abstract class BasicTagTechnology implements TagTechnology { + private static final String TAG = "NFC"; + + /*package*/ final Tag mTag; + /*package*/ boolean mIsConnected; + /*package*/ int mSelectedTechnology; + + BasicTagTechnology(Tag tag, int tech) throws RemoteException { + mTag = tag; + mSelectedTechnology = tech; + } + + @Override + public Tag getTag() { + return mTag; + } + + /** Internal helper to throw IllegalStateException if the technology isn't connected */ + void checkConnected() { + if ((mTag.getConnectedTechnology() != mSelectedTechnology) || + (mTag.getConnectedTechnology() == -1)) { + throw new IllegalStateException("Call connect() first!"); + } + } + + /** + * Helper to indicate if {@link #connect} has succeeded. + * <p> + * Does not cause RF activity, and does not block. + * @return true if {@link #connect} has completed successfully and the {@link Tag} is believed + * to be within range. Applications must still handle {@link java.io.IOException} + * while using methods that require a connection in case the connection is lost after this + * method returns. + */ + public boolean isConnected() { + if (!mIsConnected) { + return false; + } + + try { + return mTag.getTagService().isPresent(mTag.getServiceHandle()); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + return false; + } + } + + @Override + public void connect() throws IOException { + try { + int errorCode = mTag.getTagService().connect(mTag.getServiceHandle(), + mSelectedTechnology); + + if (errorCode == ErrorCodes.SUCCESS) { + // Store this in the tag object + mTag.setConnectedTechnology(mSelectedTechnology); + mIsConnected = true; + } else { + throw new IOException(); + } + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + throw new IOException("NFC service died"); + } + } + + @Override + public void reconnect() throws IOException { + if (!mIsConnected) { + throw new IllegalStateException("Technology not connected yet"); + } + + try { + int errorCode = mTag.getTagService().reconnect(mTag.getServiceHandle()); + + if (errorCode != ErrorCodes.SUCCESS) { + mIsConnected = false; + mTag.setTechnologyDisconnected(); + throw new IOException(); + } + } catch (RemoteException e) { + mIsConnected = false; + mTag.setTechnologyDisconnected(); + Log.e(TAG, "NFC service dead", e); + throw new IOException("NFC service died"); + } + } + + @Override + public void close() throws IOException { + try { + /* Note that we don't want to physically disconnect the tag, + * but just reconnect to it to reset its state + */ + mTag.getTagService().reconnect(mTag.getServiceHandle()); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } finally { + mIsConnected = false; + mTag.setTechnologyDisconnected(); + } + } + + /** Internal transceive */ + /*package*/ byte[] transceive(byte[] data, boolean raw) throws IOException { + checkConnected(); + + try { + TransceiveResult result = mTag.getTagService().transceive(mTag.getServiceHandle(), + data, raw); + if (result == null) { + throw new IOException("transceive failed"); + } else { + if (result.isSuccessful()) { + return result.getResponseData(); + } else { + if (result.isTagLost()) { + throw new TagLostException("Tag was lost."); + } + else { + throw new IOException("transceive failed"); + } + } + } + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + throw new IOException("NFC service died"); + } + } +} diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java new file mode 100644 index 0000000..774982e --- /dev/null +++ b/core/java/android/nfc/tech/IsoDep.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.tech; + +import android.nfc.Tag; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; + +/** + * A low-level connection to a {@link Tag} using the ISO-DEP technology, also known as + * ISO1443-4. + * + * <p>You can acquire this kind of connection with {@link #get}. + * Use this class to send and receive data with {@link #transceive transceive()}. + * + * <p>Applications must implement their own protocol stack on top of + * {@link #transceive transceive()}. + * + * <p class="note"><strong>Note:</strong> + * Use of this class requires the {@link android.Manifest.permission#NFC} + * permission. + */ +public final class IsoDep extends BasicTagTechnology { + private static final String TAG = "NFC"; + + /** @hide */ + public static final String EXTRA_HI_LAYER_RESP = "hiresp"; + /** @hide */ + public static final String EXTRA_HIST_BYTES = "histbytes"; + + private byte[] mHiLayerResponse = null; + private byte[] mHistBytes = null; + + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static IsoDep get(Tag tag) { + if (!tag.hasTech(TagTechnology.ISO_DEP)) return null; + try { + return new IsoDep(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public IsoDep(Tag tag) + throws RemoteException { + super(tag, TagTechnology.ISO_DEP); + Bundle extras = tag.getTechExtras(TagTechnology.ISO_DEP); + if (extras != null) { + mHiLayerResponse = extras.getByteArray(EXTRA_HI_LAYER_RESP); + mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES); + } + } + + /** + * Sets the timeout of an IsoDep transceive transaction in milliseconds. + * If the transaction has not completed before the timeout, + * any ongoing {@link #transceive} operation will be + * aborted and the connection to the tag is lost. This setting is applied + * only to the {@link Tag} object linked to this technology and will be + * reset when {@link IsoDep#close} is called. + * The default transaction timeout is 300 milliseconds. + */ + public void setTimeout(int timeout) { + try { + mTag.getTagService().setIsoDepTimeout(timeout); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } + } + + @Override + public void close() throws IOException { + try { + mTag.getTagService().resetIsoDepTimeout(); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } + super.close(); + } + + /** + * Return the historical bytes if the tag is using {@link NfcA}, null otherwise. + */ + public byte[] getHistoricalBytes() { + return mHistBytes; + } + + /** + * Return the hi layer response bytes if the tag is using {@link NfcB}, null otherwise. + */ + public byte[] getHiLayerResponse() { + return mHiLayerResponse; + } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * + * @param data bytes to send + * @return bytes received in response + * @throws IOException if the target is lost or connection closed + */ + public byte[] transceive(byte[] data) throws IOException { + return transceive(data, true); + } +} diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java new file mode 100644 index 0000000..d337ead --- /dev/null +++ b/core/java/android/nfc/tech/MifareClassic.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.tech; + +import android.nfc.Tag; +import android.nfc.TagLostException; +import android.os.RemoteException; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Technology class representing MIFARE Classic tags (also known as MIFARE Standard). + * + * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology + * MIFARE Classic tags will still be scanned, but will only show the NfcA technology. + * + * <p>MIFARE Classic tags have sectors that each contain blocks. The block size is constant at + * 16 bytes, but the number of sectors and the sector size varies by product. MIFARE has encryption + * built in and each sector has two keys associated with it, as well as ACLs to determine what + * level acess each key grants. Before operating on a sector you must call either + * {@link #authenticateSectorWithKeyA(int, byte[])} or + * {@link #authenticateSectorWithKeyB(int, byte[])} to gain authorization for your request. + */ +public final class MifareClassic extends BasicTagTechnology { + /** + * The well-known default MIFARE read key. All keys are set to this at the factory. + * Using this key will effectively make the payload in the sector public. + */ + public static final byte[] KEY_DEFAULT = + {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; + /** + * The well-known, default MIFARE Application Directory read key. + */ + public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = + {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; + /** + * The well-known, default read key for NDEF data on a MIFARE Classic + */ + public static final byte[] KEY_NFC_FORUM = + {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; + + /** A Mifare Classic compatible card of unknown type */ + public static final int TYPE_UNKNOWN = -1; + /** A MIFARE Classic tag */ + public static final int TYPE_CLASSIC = 0; + /** A MIFARE Plus tag */ + public static final int TYPE_PLUS = 1; + /** A MIFARE Pro tag */ + public static final int TYPE_PRO = 2; + + /** The tag contains 16 sectors, each holding 4 blocks. */ + public static final int SIZE_1K = 1024; + /** The tag contains 32 sectors, each holding 4 blocks. */ + public static final int SIZE_2K = 2048; + /** + * The tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors + * contain 16 blocks. + */ + public static final int SIZE_4K = 4096; + /** The tag contains 5 sectors, each holding 4 blocks. */ + public static final int SIZE_MINI = 320; + + /** Size of a Mifare Classic block (in bytes) */ + public static final int BLOCK_SIZE = 16; + + private static final int MAX_BLOCK_COUNT = 256; + private static final int MAX_SECTOR_COUNT = 40; + + private boolean mIsEmulated; + private int mType; + private int mSize; + + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static MifareClassic get(Tag tag) { + if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null; + try { + return new MifareClassic(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public MifareClassic(Tag tag) throws RemoteException { + super(tag, TagTechnology.MIFARE_CLASSIC); + + NfcA a = NfcA.get(tag); // Mifare Classic is always based on NFC a + + mIsEmulated = false; + + switch (a.getSak()) { + case 0x08: + mType = TYPE_CLASSIC; + mSize = SIZE_1K; + break; + case 0x09: + mType = TYPE_CLASSIC; + mSize = SIZE_MINI; + break; + case 0x10: + mType = TYPE_PLUS; + mSize = SIZE_2K; + // SecLevel = SL2 + break; + case 0x11: + mType = TYPE_PLUS; + mSize = SIZE_4K; + // Seclevel = SL2 + break; + case 0x18: + mType = TYPE_CLASSIC; + mSize = SIZE_4K; + break; + case 0x28: + mType = TYPE_CLASSIC; + mSize = SIZE_1K; + mIsEmulated = true; + break; + case 0x38: + mType = TYPE_CLASSIC; + mSize = SIZE_4K; + mIsEmulated = true; + break; + case 0x88: + mType = TYPE_CLASSIC; + mSize = SIZE_1K; + // NXP-tag: false + break; + case 0x98: + case 0xB8: + mType = TYPE_PRO; + mSize = SIZE_4K; + break; + default: + // Stack incorrectly reported a MifareClassic. We cannot handle this + // gracefully - we have no idea of the memory layout. Bail. + throw new RuntimeException( + "Tag incorrectly enumerated as Mifare Classic, SAK = " + a.getSak()); + } + } + + /** Returns the type of the tag, determined at discovery time */ + public int getType() { + return mType; + } + + /** Returns the size of the tag in bytes, determined at discovery time */ + public int getSize() { + return mSize; + } + + /** Returns true if the tag is emulated, determined at discovery time. + * These are actually smart-cards that emulate a Mifare Classic interface. + * They can be treated identically to a Mifare Classic tag. + * @hide + */ + public boolean isEmulated() { + return mIsEmulated; + } + + /** Returns the number of sectors on this tag, determined at discovery time */ + public int getSectorCount() { + switch (mSize) { + case SIZE_1K: + return 16; + case SIZE_2K: + return 32; + case SIZE_4K: + return 40; + case SIZE_MINI: + return 5; + default: + return 0; + } + } + + /** Returns the total block count, determined at discovery time */ + public int getBlockCount() { + return mSize / BLOCK_SIZE; + } + + /** Returns the block count for the given sector, determined at discovery time */ + public int getBlockCountInSector(int sectorIndex) { + validateSector(sectorIndex); + + if (sectorIndex < 32) { + return 4; + } else { + return 16; + } + } + + /** Return the sector index of a given block */ + public int blockToSector(int blockIndex) { + validateBlock(blockIndex); + + if (blockIndex < 32 * 4) { + return blockIndex / 4; + } else { + return 32 + (blockIndex - 32 * 4) / 16; + } + } + + /** Return the first block of a given sector */ + public int sectorToBlock(int sectorIndex) { + if (sectorIndex < 32) { + return sectorIndex * 4; + } else { + return 32 * 4 + (sectorIndex - 32) * 16; + } + } + + // Methods that require connect() + /** + * Authenticate a sector. + * <p>Every sector has an A and B key with different access privileges, + * this method attempts to authenticate against the A key. + * <p>This requires a that the tag be connected. + */ + public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException { + return authenticate(sectorIndex, key, true); + } + + /** + * Authenticate a sector. + * <p>Every sector has an A and B key with different access privileges, + * this method attempts to authenticate against the B key. + * <p>This requires a that the tag be connected. + */ + public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException { + return authenticate(sectorIndex, key, false); + } + + private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException { + validateSector(sector); + checkConnected(); + + byte[] cmd = new byte[12]; + + // First byte is the command + if (keyA) { + cmd[0] = 0x60; // phHal_eMifareAuthentA + } else { + cmd[0] = 0x61; // phHal_eMifareAuthentB + } + + // Second byte is block address + // Authenticate command takes a block address. Authenticating a block + // of a sector will authenticate the entire sector. + cmd[1] = (byte) sectorToBlock(sector); + + // Next 4 bytes are last 4 bytes of UID + byte[] uid = getTag().getId(); + System.arraycopy(uid, uid.length - 4, cmd, 2, 4); + + // Next 6 bytes are key + System.arraycopy(key, 0, cmd, 6, 6); + + try { + if (transceive(cmd, false) != null) { + return true; + } + } catch (TagLostException e) { + throw e; + } catch (IOException e) { + // No need to deal with, will return false anyway + } + return false; + } + + /** + * Read 16-byte block. + * <p>This requires a that the tag be connected. + * @throws IOException + */ + public byte[] readBlock(int blockIndex) throws IOException { + validateBlock(blockIndex); + checkConnected(); + + byte[] cmd = { 0x30, (byte) blockIndex }; + return transceive(cmd, false); + } + + /** + * Write 16-byte block. + * <p>This requires a that the tag be connected. + * @throws IOException + */ + public void writeBlock(int blockIndex, byte[] data) throws IOException { + validateBlock(blockIndex); + checkConnected(); + if (data.length != 16) { + throw new IllegalArgumentException("must write 16-bytes"); + } + + byte[] cmd = new byte[data.length + 2]; + cmd[0] = (byte) 0xA0; // MF write command + cmd[1] = (byte) blockIndex; + System.arraycopy(data, 0, cmd, 2, data.length); + + transceive(cmd, false); + } + + /** + * Increment a value block, and store the result in temporary memory. + * @param blockIndex + * @throws IOException + */ + public void increment(int blockIndex, int value) throws IOException { + validateBlock(blockIndex); + validateValueOperand(value); + checkConnected(); + + ByteBuffer cmd = ByteBuffer.allocate(6); + cmd.order(ByteOrder.LITTLE_ENDIAN); + cmd.put( (byte) 0xC1 ); + cmd.put( (byte) blockIndex ); + cmd.putInt(value); + + transceive(cmd.array(), false); + } + + /** + * Decrement a value block, and store the result in temporary memory. + * @param blockIndex + * @throws IOException + */ + public void decrement(int blockIndex, int value) throws IOException { + validateBlock(blockIndex); + validateValueOperand(value); + checkConnected(); + + ByteBuffer cmd = ByteBuffer.allocate(6); + cmd.order(ByteOrder.LITTLE_ENDIAN); + cmd.put( (byte) 0xC0 ); + cmd.put( (byte) blockIndex ); + cmd.putInt(value); + + transceive(cmd.array(), false); + } + + /** + * Copy from temporary memory to value block. + * @param blockIndex + * @throws IOException + */ + public void transfer(int blockIndex) throws IOException { + validateBlock(blockIndex); + checkConnected(); + + byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; + + transceive(cmd, false); + } + + /** + * Copy from value block to temporary memory. + * @param blockIndex + * @throws IOException + */ + public void restore(int blockIndex) throws IOException { + validateBlock(blockIndex); + checkConnected(); + + byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; + + transceive(cmd, false); + } + + /** + * Send raw NfcA data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * <p>This requires a that the tag be connected. + * + * @param data bytes to send + * @return bytes received in response + * @throws IOException if the target is lost or connection closed + */ + public byte[] transceive(byte[] data) throws IOException { + return transceive(data, true); + } + + private static void validateSector(int sector) { + // Do not be too strict on upper bounds checking, since some cards + // have more addressable memory than they report. For example, + // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in + // Mifare Classic compatibility mode. + // Note that issuing a command to an out-of-bounds block is safe - the + // tag should report error causing IOException. This validation is a + // helper to guard against obvious programming mistakes. + if (sector < 0 || sector >= MAX_SECTOR_COUNT) { + throw new IndexOutOfBoundsException("sector out of bounds: " + sector); + } + } + + private static void validateBlock(int block) { + // Just looking for obvious out of bounds... + if (block < 0 || block >= MAX_BLOCK_COUNT) { + throw new IndexOutOfBoundsException("block out of bounds: " + block); + } + } + + private static void validateValueOperand(int value) { + if (value < 0) { + throw new IllegalArgumentException("value operand negative"); + } + } +} diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java new file mode 100644 index 0000000..b514f1c --- /dev/null +++ b/core/java/android/nfc/tech/MifareUltralight.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.tech; + +import android.nfc.Tag; +import android.os.RemoteException; + +import java.io.IOException; + +//TOOD: Ultralight C 3-DES authentication, one-way counter + +/** + * Technology class representing MIFARE Ultralight and MIFARE Ultralight C tags. + * + * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology + * MIFARE Ultralight class tags will still be scanned, but will only show the NfcA technology. + * + * <p>MIFARE Ultralight compatible tags have 4 byte pages. The read command + * returns 4 pages (16 bytes) at a time, for speed. The write command operates + * on a single page (4 bytes) to minimize EEPROM write cycles. + * + * <p>The original MIFARE Ultralight consists of a 64 byte EEPROM. The first + * 4 pages are for the OTP area, manufacturer data, and locking bits. They are + * readable and some bits are writable. The final 12 pages are the user + * read/write area. For more information see the NXP data sheet MF0ICU1. + * + * <p>The MIFARE Ultralight C consists of a 192 byte EEPROM. The first 4 pages + * are for OTP, manufacturer data, and locking bits. The next 36 pages are the + * user read/write area. The next 4 pages are additional locking bits, counters + * and authentication configuration and are readable. The final 4 pages are for + * the authentication key and are not readable. For more information see the + * NXP data sheet MF0ICU2. + */ +public final class MifareUltralight extends BasicTagTechnology { + /** A MIFARE Ultralight compatible tag of unknown type */ + public static final int TYPE_UNKNOWN = -1; + /** A MIFARE Ultralight tag */ + public static final int TYPE_ULTRALIGHT = 1; + /** A MIFARE Ultralight C tag */ + public static final int TYPE_ULTRALIGHT_C = 2; + + /** Size of a MIFARE Ultralight page in bytes */ + public static final int PAGE_SIZE = 4; + + private static final int NXP_MANUFACTURER_ID = 0x04; + private static final int MAX_PAGE_COUNT = 256; + + private int mType; + + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static MifareUltralight get(Tag tag) { + if (!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) return null; + try { + return new MifareUltralight(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public MifareUltralight(Tag tag) throws RemoteException { + super(tag, TagTechnology.MIFARE_ULTRALIGHT); + + // Check if this could actually be a Mifare + NfcA a = NfcA.get(tag); + + mType = TYPE_UNKNOWN; + + if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) { + // could be UL or UL-C + //TODO: stack should use NXP AN1303 procedure to make a best guess + // attempt at classifying Ultralight vs Ultralight C. + mType = TYPE_ULTRALIGHT; + } + } + + /** Returns the type of the tag. + * <p>It is very hard to always accurately classify a MIFARE Ultralight + * compatible tag as Ultralight original or Ultralight C. So consider + * {@link #getType} a hint. */ + public int getType() { + return mType; + } + + // Methods that require connect() + /** + * Read 4 pages (16 bytes). + * <p>The MIFARE Ultralight protocol always reads 4 pages at a time. + * <p>If the read spans past the last readable block, then the tag will + * return pages that have been wrapped back to the first blocks. MIFARE + * Ultralight tags have readable blocks 0x00 through 0x0F. So a read to + * block offset 0x0E would return blocks 0x0E, 0x0F, 0x00, 0x01. MIFARE + * Ultralight C tags have readable blocks 0x00 through 0x2B. So a read to + * block 0x2A would return blocks 0x2A, 0x2B, 0x00, 0x01. + * <p>This requires that the tag be connected. + * + * @return 4 pages (16 bytes) + * @throws IOException + */ + public byte[] readPages(int pageOffset) throws IOException { + validatePageOffset(pageOffset); + checkConnected(); + + byte[] cmd = { 0x30, (byte) pageOffset}; + return transceive(cmd, false); + } + + /** + * Write 1 page (4 bytes). + * <p>The MIFARE Ultralight protocol always writes 1 page at a time. + * <p>This requires that the tag be connected. + * + * @param pageOffset The offset of the page to write + * @param data The data to write + * @throws IOException + */ + public void writePage(int pageOffset, byte[] data) throws IOException { + validatePageOffset(pageOffset); + checkConnected(); + + byte[] cmd = new byte[data.length + 2]; + cmd[0] = (byte) 0xA2; + cmd[1] = (byte) pageOffset; + System.arraycopy(data, 0, cmd, 2, data.length); + + transceive(cmd, false); + } + + /** + * Send raw NfcA data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * <p>This requires a that the tag be connected. + * + * @param data bytes to send + * @return bytes received in response + * @throws IOException if the target is lost or connection closed + */ + public byte[] transceive(byte[] data) throws IOException { + return transceive(data, true); + } + + private static void validatePageOffset(int pageOffset) { + // Do not be too strict on upper bounds checking, since some cards + // may have more addressable memory than they report. + // Note that issuing a command to an out-of-bounds block is safe - the + // tag will wrap the read to an addressable area. This validation is a + // helper to guard against obvious programming mistakes. + if (pageOffset < 0 || pageOffset >= MAX_PAGE_COUNT) { + throw new IndexOutOfBoundsException("page out of bounds: " + pageOffset); + } + } +} diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/tech/Ndef.java index 05872fe..c6804f9 100644 --- a/core/java/android/nfc/technology/Ndef.java +++ b/core/java/android/nfc/tech/Ndef.java @@ -14,30 +14,34 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; import android.nfc.ErrorCodes; import android.nfc.FormatException; +import android.nfc.INfcTag; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; /** * A high-level connection to a {@link Tag} using one of the NFC type 1, 2, 3, or 4 technologies * to interact with NDEF data. MiFare Classic cards that present NDEF data may also be used - * via this class. To determine the exact technology being used call {@link #getTechnologyId()} + * via this class. To determine the exact technology being used call {@link #getType()} * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * * <p class="note"><strong>Note:</strong> * Use of this class requires the {@link android.Manifest.permission#NFC} * permission. */ public final class Ndef extends BasicTagTechnology { + private static final String TAG = "NFC"; + /** @hide */ public static final int NDEF_MODE_READ_ONLY = 1; /** @hide */ @@ -57,14 +61,12 @@ public final class Ndef extends BasicTagTechnology { /** @hide */ public static final String EXTRA_NDEF_TYPE = "ndeftype"; - //TODO: consider removing OTHER entirely - and not allowing Ndef to - // enumerate for tag types outside of (NFC Forum 1-4, MifareClassic) public static final int OTHER = -1; public static final int NFC_FORUM_TYPE_1 = 1; public static final int NFC_FORUM_TYPE_2 = 2; public static final int NFC_FORUM_TYPE_3 = 3; public static final int NFC_FORUM_TYPE_4 = 4; - public static final int MIFARE_CLASSIC = 105; + public static final int MIFARE_CLASSIC = 101; private final int mMaxNdefSize; private final int mCardState; @@ -72,11 +74,27 @@ public final class Ndef extends BasicTagTechnology { private final int mNdefType; /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static Ndef get(Tag tag) { + if (!tag.hasTech(TagTechnology.NDEF)) return null; + try { + return new Ndef(tag); + } catch (RemoteException e) { + return null; + } + } + + /** * Internal constructor, to be used by NfcAdapter * @hide */ - public Ndef(NfcAdapter adapter, Tag tag, int tech, Bundle extras) throws RemoteException { - super(adapter, tag, tech); + public Ndef(Tag tag) throws RemoteException { + super(tag, TagTechnology.NDEF); + Bundle extras = tag.getTechExtras(TagTechnology.NDEF); if (extras != null) { mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH); mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE); @@ -97,15 +115,6 @@ public final class Ndef extends BasicTagTechnology { } /** - * Get optional extra NDEF messages. - * Some tags may contain extra NDEF messages, but not all - * implementations will be able to read them. - */ - public NdefMessage[] getExtraNdefMessage() throws IOException, FormatException { - throw new UnsupportedOperationException(); - } - - /** * Get NDEF tag type. * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2}, * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4}, @@ -148,11 +157,12 @@ public final class Ndef extends BasicTagTechnology { checkConnected(); try { + INfcTag tagService = mTag.getTagService(); int serviceHandle = mTag.getServiceHandle(); - if (mTagService.isNdef(serviceHandle)) { - NdefMessage msg = mTagService.ndefRead(serviceHandle); + if (tagService.isNdef(serviceHandle)) { + NdefMessage msg = tagService.ndefRead(serviceHandle); if (msg == null) { - int errorCode = mTagService.getLastError(serviceHandle); + int errorCode = tagService.getLastError(serviceHandle); switch (errorCode) { case ErrorCodes.ERROR_IO: throw new IOException(); @@ -168,7 +178,7 @@ public final class Ndef extends BasicTagTechnology { return null; } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); return null; } } @@ -181,9 +191,10 @@ public final class Ndef extends BasicTagTechnology { checkConnected(); try { + INfcTag tagService = mTag.getTagService(); int serviceHandle = mTag.getServiceHandle(); - if (mTagService.isNdef(serviceHandle)) { - int errorCode = mTagService.ndefWrite(serviceHandle, msg); + if (tagService.isNdef(serviceHandle)) { + int errorCode = tagService.ndefWrite(serviceHandle, msg); switch (errorCode) { case ErrorCodes.SUCCESS: break; @@ -200,67 +211,54 @@ public final class Ndef extends BasicTagTechnology { throw new IOException("Tag is not ndef"); } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); } } /** - * Attempt to write extra NDEF messages. - * Implementations may be able to write extra NDEF - * message after the first primary message, but it is not - * guaranteed. Even if it can be written, other implementations - * may not be able to read NDEF messages after the primary message. - * It is recommended to use additional NDEF records instead. - * - * @throws IOException + * Indicates whether a tag can be made read-only with + * {@link #makeReadonly()} */ - public void writeExtraNdefMessage(int i, NdefMessage msg) throws IOException, FormatException { - checkConnected(); - - throw new UnsupportedOperationException(); + public boolean canMakeReadonly() { + if (mNdefType == NFC_FORUM_TYPE_1 || mNdefType == NFC_FORUM_TYPE_2) { + return true; + } else { + return false; + } } /** - * Set the CC field to indicate this tag is read-only + * Sets the CC field to indicate this tag is read-only + * and permanently sets the lock bits to prevent any further NDEF + * modifications. + * This is a one-way process and can not be reverted! * @throws IOException */ public boolean makeReadonly() throws IOException { checkConnected(); try { - int errorCode = mTagService.ndefMakeReadOnly(mTag.getServiceHandle()); - switch (errorCode) { - case ErrorCodes.SUCCESS: - return true; - case ErrorCodes.ERROR_IO: - throw new IOException(); - case ErrorCodes.ERROR_INVALID_PARAM: - return false; - default: - // Should not happen - throw new IOException(); - } + INfcTag tagService = mTag.getTagService(); + if (tagService.isNdef(mTag.getServiceHandle())) { + int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle()); + switch (errorCode) { + case ErrorCodes.SUCCESS: + return true; + case ErrorCodes.ERROR_IO: + throw new IOException(); + case ErrorCodes.ERROR_INVALID_PARAM: + return false; + default: + // Should not happen + throw new IOException(); + } + } + else { + throw new IOException("Tag is not ndef"); + } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); return false; } } - - /** - * Attempt to use tag specific technology to really make - * the tag read-only - * For NFC Forum Type 1 and 2 only. - */ - public void makeLowLevelReadonly() { - checkConnected(); - - throw new UnsupportedOperationException(); - } - - @Override - public byte[] transceive(byte[] data) { - checkConnected(); - - throw new UnsupportedOperationException(); - } } diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/tech/NdefFormatable.java index 222c558..2919c43 100644 --- a/core/java/android/nfc/technology/NdefFormatable.java +++ b/core/java/android/nfc/tech/NdefFormatable.java @@ -14,34 +14,52 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; import android.nfc.ErrorCodes; import android.nfc.FormatException; +import android.nfc.INfcTag; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; import android.nfc.Tag; -import android.os.Bundle; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; /** * An interface to a {@link Tag} allowing to format the tag as NDEF. * - * <p>You can acquire this kind of interface with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * * <p class="note"><strong>Note:</strong> * Use of this class requires the {@link android.Manifest.permission#NFC} * permission. */ public final class NdefFormatable extends BasicTagTechnology { + private static final String TAG = "NFC"; + + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NdefFormatable get(Tag tag) { + if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null; + try { + return new NdefFormatable(tag); + } catch (RemoteException e) { + return null; + } + } + /** * Internal constructor, to be used by NfcAdapter * @hide */ - public NdefFormatable(NfcAdapter adapter, Tag tag, int tech, Bundle extras) throws RemoteException { - super(adapter, tag, tech); + public NdefFormatable(Tag tag) throws RemoteException { + super(tag, TagTechnology.NDEF_FORMATABLE); } /** @@ -52,10 +70,9 @@ public final class NdefFormatable extends BasicTagTechnology { checkConnected(); try { - byte[] DEFAULT_KEY = {(byte)0xFF,(byte)0xFF,(byte)0xFF, - (byte)0xFF,(byte)0xFF,(byte)0xFF}; int serviceHandle = mTag.getServiceHandle(); - int errorCode = mTagService.formatNdef(serviceHandle, DEFAULT_KEY); + INfcTag tagService = mTag.getTagService(); + int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT); switch (errorCode) { case ErrorCodes.SUCCESS: break; @@ -68,8 +85,8 @@ public final class NdefFormatable extends BasicTagTechnology { throw new IOException(); } // Now check and see if the format worked - if (mTagService.isNdef(serviceHandle)) { - errorCode = mTagService.ndefWrite(serviceHandle, firstMessage); + if (tagService.isNdef(serviceHandle)) { + errorCode = tagService.ndefWrite(serviceHandle, firstMessage); switch (errorCode) { case ErrorCodes.SUCCESS: break; @@ -85,14 +102,7 @@ public final class NdefFormatable extends BasicTagTechnology { throw new IOException(); } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); } } - - @Override - public byte[] transceive(byte[] data) { - checkConnected(); - - throw new UnsupportedOperationException(); - } } diff --git a/core/java/android/nfc/technology/NfcA.java b/core/java/android/nfc/tech/NfcA.java index ef46762..24badc4 100644 --- a/core/java/android/nfc/technology/NfcA.java +++ b/core/java/android/nfc/tech/NfcA.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** * A low-level connection to a {@link Tag} using the NFC-A technology, also known as * ISO1443-3A. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -44,8 +45,25 @@ public final class NfcA extends BasicTagTechnology { private short mSak; private byte[] mAtqa; - public NfcA(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException { - super(adapter, tag, TagTechnology.NFC_A); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcA get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_A)) return null; + try { + return new NfcA(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcA(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_A); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_A); mSak = extras.getShort(EXTRA_SAK); mAtqa = extras.getByteArray(EXTRA_ATQA); } @@ -63,4 +81,19 @@ public final class NfcA extends BasicTagTechnology { public short getSak() { return mSak; } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * + * @param data bytes to send + * @return bytes received in response + * @throws IOException if the target is lost or connection closed + */ + public byte[] transceive(byte[] data) throws IOException { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/technology/NfcB.java b/core/java/android/nfc/tech/NfcB.java index 267c94d..abeef32 100644 --- a/core/java/android/nfc/technology/NfcB.java +++ b/core/java/android/nfc/tech/NfcB.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** * A low-level connection to a {@link Tag} using the NFC-B technology, also known as * ISO1443-3B. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -44,9 +45,25 @@ public final class NfcB extends BasicTagTechnology { private byte[] mAppData; private byte[] mProtInfo; - public NfcB(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.NFC_B); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcB get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_B)) return null; + try { + return new NfcB(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcB(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_B); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_B); mAppData = extras.getByteArray(EXTRA_APPDATA); mProtInfo = extras.getByteArray(EXTRA_PROTINFO); } @@ -67,4 +84,18 @@ public final class NfcB extends BasicTagTechnology { return mProtInfo; } + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * + * @param data bytes to send + * @return bytes received in response + * @throws IOException if the target is lost or connection closed + */ + public byte[] transceive(byte[] data) throws IOException { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/technology/NfcF.java b/core/java/android/nfc/tech/NfcF.java index 6741ac8..f617739 100644 --- a/core/java/android/nfc/technology/NfcF.java +++ b/core/java/android/nfc/tech/NfcF.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** * A low-level connection to a {@link Tag} using the NFC-F technology, also known as * JIS6319-4. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -44,9 +45,25 @@ public final class NfcF extends BasicTagTechnology { private byte[] mSystemCode = null; private byte[] mManufacturer = null; - public NfcF(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.NFC_F); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcF get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_F)) return null; + try { + return new NfcF(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcF(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_F); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_F); if (extras != null) { mSystemCode = extras.getByteArray(EXTRA_SC); mManufacturer = extras.getByteArray(EXTRA_PMM); @@ -60,4 +77,19 @@ public final class NfcF extends BasicTagTechnology { public byte[] getManufacturer() { return mManufacturer; } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * + * @param data bytes to send + * @return bytes received in response + * @throws IOException if the target is lost or connection closed + */ + public byte[] transceive(byte[] data) throws IOException { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/technology/NfcV.java b/core/java/android/nfc/tech/NfcV.java index 460de6a..8e1f066 100644 --- a/core/java/android/nfc/technology/NfcV.java +++ b/core/java/android/nfc/tech/NfcV.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** - * A low-level connection to a {@link Tag} using the NFC-V technology, also known as + * A low-level connection to a {@link Tag} using NFC vicinity technology, also known as * ISO15693. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -45,9 +46,25 @@ public final class NfcV extends BasicTagTechnology { private byte mRespFlags; private byte mDsfId; - public NfcV(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.NFC_V); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcV get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_V)) return null; + try { + return new NfcV(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcV(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_V); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_V); mRespFlags = extras.getByte(EXTRA_RESP_FLAGS); mDsfId = extras.getByte(EXTRA_DSFID); } @@ -59,4 +76,19 @@ public final class NfcV extends BasicTagTechnology { public byte getDsfId() { return mDsfId; } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * + * @param data bytes to send + * @return bytes received in response + * @throws IOException if the target is lost or connection closed + */ + public byte[] transceive(byte[] data) throws IOException { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/tech/TagTechnology.java b/core/java/android/nfc/tech/TagTechnology.java new file mode 100644 index 0000000..c8ccdcf --- /dev/null +++ b/core/java/android/nfc/tech/TagTechnology.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc.tech; + +import android.nfc.Tag; + +import java.io.Closeable; +import java.io.IOException; + +public interface TagTechnology extends Closeable { + /** + * This technology is an instance of {@link NfcA}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_A = 1; + + /** + * This technology is an instance of {@link NfcB}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_B = 2; + + /** + * This technology is an instance of {@link IsoDep}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int ISO_DEP = 3; + + /** + * This technology is an instance of {@link NfcF}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_F = 4; + + /** + * This technology is an instance of {@link NfcV}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_V = 5; + + /** + * This technology is an instance of {@link Ndef}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NDEF = 6; + + /** + * This technology is an instance of {@link NdefFormatable}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NDEF_FORMATABLE = 7; + + /** + * This technology is an instance of {@link MifareClassic}. + * <p>Support for this technology type is optional. If a stack doesn't support this technology + * type tags using it must still be discovered and present the lower level radio interface + * technologies in use. + * @hide + */ + public static final int MIFARE_CLASSIC = 8; + + /** + * This technology is an instance of {@link MifareUltralight}. + * <p>Support for this technology type is optional. If a stack doesn't support this technology + * type tags using it must still be discovered and present the lower level radio interface + * technologies in use. + * @hide + */ + public static final int MIFARE_ULTRALIGHT = 9; + + /** + * Get the {@link Tag} object this technology came from. + */ + public Tag getTag(); + + /** + * Opens a connection to the {@link Tag} enabling interactive commands. The command set + * varies by the technology type. + * + * <p>This method blocks until the connection has been established. + * + * <p>A call to {@link #close} from another thread will cancel a blocked call and cause an + * IOException to be thrown on the thread that is blocked. + * + * @see #reconnect() + * @see #close() + * @throws IOException if the target is lost, or connect canceled + */ + public void connect() throws IOException; + + /** + * Re-connect to the {@link Tag} associated with this connection. Reconnecting to a tag can be + * used to reset the state of the tag itself. + * + * <p>This method blocks until the connection is re-established. + * + * <p>A call to {@link #close} from another thread will cancel a blocked call and cause an + * IOException to be thrown on the thread that is blocked. + * + * @see #connect() + * @see #close() + * @throws IOException + */ + public void reconnect() throws IOException; + + /** + * Closes the connection to the {@link Tag}. This call is non-blocking and causes all blocking + * operations such as {@link #connect} to be canceled and immediately throw + * {@link java.io.IOException} on the thread that is blocked. + * + * <p> + * Once this method is called, this object cannot be re-used and should be discarded. Further + * calls to {@link #connect} will fail. + * + * @see #connect() + * @see #reconnect() + */ + public void close() throws IOException; +} diff --git a/core/java/android/nfc/technology/BasicTagTechnology.java b/core/java/android/nfc/technology/BasicTagTechnology.java deleted file mode 100644 index 553f6ec..0000000 --- a/core/java/android/nfc/technology/BasicTagTechnology.java +++ /dev/null @@ -1,254 +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 android.nfc.technology; - -import java.io.IOException; - -import android.nfc.INfcAdapter; -import android.nfc.INfcTag; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.ErrorCodes; -import android.os.RemoteException; -import android.util.Log; - -/** - * A base class for tag technologies that are built on top of transceive(). - */ -/* package */ abstract class BasicTagTechnology implements TagTechnology { - - /*package*/ final Tag mTag; - /*package*/ boolean mIsConnected; - /*package*/ int mSelectedTechnology; - private final NfcAdapter mAdapter; - - // Following fields are final after construction, except for - // during attemptDeadServiceRecovery() when NFC crashes. - // Not locked - we accept a best effort attempt when NFC crashes. - /*package*/ INfcAdapter mService; - /*package*/ INfcTag mTagService; - - private static final String TAG = "NFC"; - - /** - * @hide - */ - public BasicTagTechnology(NfcAdapter adapter, Tag tag, int tech) throws RemoteException { - int[] techList = tag.getTechnologyList(); - int i; - - // Check target validity - for (i = 0; i < techList.length; i++) { - if (tech == techList[i]) { - break; - } - } - if (i >= techList.length) { - // Technology not found - throw new IllegalArgumentException("Technology " + tech + " not present on tag " + tag); - } - - mAdapter = adapter; - mService = mAdapter.getService(); - try { - mTagService = mService.getNfcTagInterface(); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - } - mTag = tag; - mSelectedTechnology = tech; - } - - /** - * @hide - */ - public BasicTagTechnology(NfcAdapter adapter, Tag tag) throws RemoteException { - this(adapter, tag, tag.getTechnologyList()[0]); - } - - /** NFC service dead - attempt best effort recovery */ - /*package*/ void attemptDeadServiceRecovery(Exception e) { - mAdapter.attemptDeadServiceRecovery(e); - /* assigning to mService is not thread-safe, but this is best-effort code - * and on a well-behaved system should never happen */ - mService = mAdapter.getService(); - try { - mTagService = mService.getNfcTagInterface(); - } catch (RemoteException e2) { - Log.e(TAG, "second RemoteException trying to recover from dead NFC service", e2); - } - } - - /** - * Get the {@link Tag} this connection is associated with. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - */ - @Override - public Tag getTag() { - return mTag; - } - - public void checkConnected() { - if ((mTag.getConnectedTechnology() != getTechnologyId()) || - (mTag.getConnectedTechnology() == -1)) { - throw new IllegalStateException("Call connect() first!"); - } - } - - /** - * <p>Requires {@link android.Manifest.permission#NFC} permission. - */ - @Override - public int getTechnologyId() { - return mSelectedTechnology; - } - - /** - * Helper to indicate if {@link #transceive transceive()} calls might succeed. - * <p> - * Does not cause RF activity, and does not block. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * @return true if {@link #connect} has completed successfully and the {@link Tag} is believed - * to be within range. Applications must still handle {@link java.io.IOException} - * while using {@link #transceive transceive()}, in case connection is lost after this method - * returns true. - */ - public boolean isConnected() { - if (!mIsConnected) { - return false; - } - - try { - return mTagService.isPresent(mTag.getServiceHandle()); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - return false; - } - } - - /** - * Connect to the {@link Tag} associated with this connection. - * <p> - * This method blocks until the connection is established. - * <p> - * {@link #close} can be called from another thread to cancel this connection - * attempt. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * @throws IOException if the target is lost, or connect canceled - */ - @Override - public void connect() throws IOException { - try { - int errorCode = mTagService.connect(mTag.getServiceHandle(), getTechnologyId()); - - if (errorCode == ErrorCodes.SUCCESS) { - // Store this in the tag object - mTag.setConnectedTechnology(getTechnologyId()); - mIsConnected = true; - } else { - throw new IOException(); - } - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } - - /** - * Re-connect to the {@link Tag} associated with this connection. - * <p> - * Reconnecting to a tag can be used to reset the state of the tag itself. - * This method blocks until the connection is re-established. - * <p> - * {@link #close} can be called from another thread to cancel this connection - * attempt. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * @throws IOException if the target is lost, or connect canceled - */ - @Override - public void reconnect() throws IOException { - if (!mIsConnected) { - throw new IllegalStateException("Technology not connected yet"); - } else { - try { - int errorCode = mTagService.reconnect(mTag.getServiceHandle()); - - if (errorCode != ErrorCodes.SUCCESS) { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - throw new IOException(); - } - } catch (RemoteException e) { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } - } - - /** - * Close this connection. - * <p> - * Causes blocking operations such as {@link #transceive transceive()} or {@link #connect} to - * be canceled and immediately throw {@link java.io.IOException}. - * <p> - * Once this method is called, this object cannot be re-used and should be discarded. Further - * calls to {@link #transceive transceive()} or {@link #connect} will fail. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - */ - @Override - public void close() { - try { - /* Note that we don't want to physically disconnect the tag, - * but just reconnect to it to reset its state - */ - mTagService.reconnect(mTag.getServiceHandle()); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - } finally { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - } - } - - /** - * Send data to a tag and receive the response. - * <p> - * This method will block until the response is received. It can be canceled - * with {@link #close}. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws IOException if the target is lost or connection closed - */ - public byte[] transceive(byte[] data) throws IOException { - checkConnected(); - - try { - byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, true); - if (response == null) { - throw new IOException("transceive failed"); - } - return response; - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } -} diff --git a/core/java/android/nfc/technology/IsoDep.java b/core/java/android/nfc/technology/IsoDep.java deleted file mode 100644 index 32a7542..0000000 --- a/core/java/android/nfc/technology/IsoDep.java +++ /dev/null @@ -1,67 +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 android.nfc.technology; - -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -import java.io.IOException; - -/** - * A low-level connection to a {@link Tag} using the ISO-DEP technology, also known as - * ISO1443-4. - * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. - * Use this class to send and receive data with {@link #transceive transceive()}. - * - * <p>Applications must implement their own protocol stack on top of - * {@link #transceive transceive()}. - * - * <p class="note"><strong>Note:</strong> - * Use of this class requires the {@link android.Manifest.permission#NFC} - * permission. - */ -public final class IsoDep extends BasicTagTechnology { - /** @hide */ - public static final String EXTRA_HI_LAYER_RESP = "hiresp"; - /** @hide */ - public static final String EXTRA_HIST_BYTES = "histbytes"; - - private byte[] mHiLayerResponse = null; - private byte[] mHistBytes = null; - - public IsoDep(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.ISO_DEP); - if (extras != null) { - mHiLayerResponse = extras.getByteArray(EXTRA_HI_LAYER_RESP); - mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES); - } - } - - /** - * 3A only - */ - public byte[] getHistoricalBytes() { return mHistBytes; } - - /** - * 3B only - */ - public byte[] getHiLayerResponse() { return mHiLayerResponse; } -} diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java deleted file mode 100644 index 799f0a7..0000000 --- a/core/java/android/nfc/technology/MifareClassic.java +++ /dev/null @@ -1,404 +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 android.nfc.technology; - -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -import java.io.IOException; - -/** - * Concrete class for TagTechnology.MIFARE_CLASSIC - * - * Mifare classic has n sectors, with varying sizes, although - * they are at least the same pattern for any one mifare classic - * product. Each sector has two keys. Authentication with the correct - * key is needed before access to any sector. - * - * Each sector has k blocks. - * Block size is constant across the whole mifare classic family. - */ -public final class MifareClassic extends BasicTagTechnology { - /** - * The well-known, default MIFARE read key. - * Use this key to effectively make the payload in this sector - * public. - */ - public static final byte[] KEY_DEFAULT = - {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; - /** - * The well-known, default Mifare Application Directory read key. - */ - public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = - {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; - /** - * The well-known, default read key for NDEF data on a Mifare Classic - */ - public static final byte[] KEY_NFC_FORUM = - {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; - - public static final int TYPE_CLASSIC = 0; - public static final int TYPE_PLUS = 1; - public static final int TYPE_PRO = 2; - public static final int TYPE_DESFIRE = 3; - public static final int TYPE_ULTRALIGHT = 4; - public static final int TYPE_UNKNOWN = 5; - - public static final int SIZE_1K = 1024; - public static final int SIZE_2K = 2048; - public static final int SIZE_4K = 4096; - public static final int SIZE_MINI = 320; - public static final int SIZE_UNKNOWN = 0; - - private boolean mIsEmulated; - private int mType; - private int mSize; - - public MifareClassic(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException { - super(adapter, tag, TagTechnology.MIFARE_CLASSIC); - - // Check if this could actually be a Mifare - NfcA a = (NfcA) tag.getTechnology(adapter, TagTechnology.NFC_A); - //short[] ATQA = getATQA(tag); - - mIsEmulated = false; - mType = TYPE_UNKNOWN; - mSize = SIZE_UNKNOWN; - - switch (a.getSak()) { - case 0x00: - // could be UL or UL-C - mType = TYPE_ULTRALIGHT; - break; - case 0x08: - // Type == classic - // Size = 1K - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - break; - case 0x09: - // Type == classic mini - // Size == ? - mType = TYPE_CLASSIC; - mSize = SIZE_MINI; - break; - case 0x10: - // Type == MF+ - // Size == 2K - // SecLevel = SL2 - mType = TYPE_PLUS; - mSize = SIZE_2K; - break; - case 0x11: - // Type == MF+ - // Size == 4K - // Seclevel = SL2 - mType = TYPE_PLUS; - mSize = SIZE_4K; - break; - case 0x18: - // Type == classic - // Size == 4k - mType = TYPE_CLASSIC; - mSize = SIZE_4K; - break; - case 0x20: - // TODO this really should be a short, not byte - if (a.getAtqa()[0] == 0x03) { - // Type == DESFIRE - mType = TYPE_DESFIRE; - } else { - // Type == MF+ - // SL = SL3 - mType = TYPE_PLUS; - mSize = SIZE_UNKNOWN; - } - break; - case 0x28: - // Type == MF Classic - // Size == 1K - // Emulated == true - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - mIsEmulated = true; - break; - case 0x38: - // Type == MF Classic - // Size == 4K - // Emulated == true - mType = TYPE_CLASSIC; - mSize = SIZE_4K; - mIsEmulated = true; - break; - case 0x88: - // Type == MF Classic - // Size == 1K - // NXP-tag: false - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - break; - case 0x98: - case 0xB8: - // Type == MF Pro - // Size == 4K - mType = TYPE_PRO; - mSize = SIZE_4K; - break; - default: - // Unknown mifare - mType = TYPE_UNKNOWN; - mSize = SIZE_UNKNOWN; - break; - } - } - - // Immutable data known at discovery time - public int getSize() { - return mSize; - } - - public int getType() { - return mType; - } - - public boolean isEmulated() { - return mIsEmulated; - } - - public int getSectorCount() { - switch (mSize) { - case SIZE_1K: { - return 16; - } - case SIZE_2K: { - return 32; - } - case SIZE_4K: { - return 40; - } - case SIZE_MINI: { - return 5; - } - default: { - return 0; - } - } - } - - public int getSectorSize(int sector) { - return getBlockCount(sector) * 16; - } - - public int getTotalBlockCount() { - int totalBlocks = 0; - for (int sec = 0; sec < getSectorCount(); sec++) { - totalBlocks += getSectorSize(sec); - } - - return totalBlocks; - } - - public int getBlockCount(int sector) { - if (sector >= getSectorCount()) { - throw new IllegalArgumentException("this card only has " + getSectorCount() + - " sectors"); - } - - if (sector <= 32) { - return 4; - } else { - return 16; - } - } - - private byte firstBlockInSector(int sector) { - if (sector < 32) { - return (byte) ((sector * 4) & 0xff); - } else { - return (byte) ((32 * 4 + ((sector - 32) * 16)) & 0xff); - } - } - - // Methods that require connect() - /** - * Authenticate for a given block. - * Note that this will authenticate the entire sector the block belongs to. - */ - public boolean authenticateBlock(int block, byte[] key, boolean keyA) { - checkConnected(); - - byte[] cmd = new byte[12]; - - // First byte is the command - if (keyA) { - cmd[0] = 0x60; // phHal_eMifareAuthentA - } else { - cmd[0] = 0x61; // phHal_eMifareAuthentB - } - - // Second byte is block address - cmd[1] = (byte) block; - - // Next 4 bytes are last 4 bytes of UID - byte[] uid = getTag().getId(); - System.arraycopy(uid, uid.length - 4, cmd, 2, 4); - - // Next 6 bytes are key - System.arraycopy(key, 0, cmd, 6, 6); - - try { - if ((transceive(cmd) != null)) { - return true; - } - } catch (IOException e) { - // No need to deal with, will return false anyway - } - return false; - } - - /** - * Authenticate for a given sector. - */ - public boolean authenticateSector(int sector, byte[] key, boolean keyA) { - checkConnected(); - - byte addr = (byte) ((firstBlockInSector(sector)) & 0xff); - - // Note that authenticating a block of a sector, will authenticate - // the entire sector. - return authenticateBlock(addr, key, keyA); - } - - /** - * Sector indexing starts at 0. - * Block indexing starts at 0, and resets in each sector. - * @throws IOException - */ - public byte[] readBlock(int sector, int block) throws IOException { - checkConnected(); - - byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); - return readBlock(addr); - - } - - /** - * Reads absolute block index. - * @throws IOException - */ - public byte[] readBlock(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] blockread_cmd = { 0x30, addr }; - - return transceive(blockread_cmd); - } - - /** - * Writes absolute block index. - * @throws IOException - */ - public void writeBlock(int block, byte[] data) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] blockwrite_cmd = new byte[data.length + 2]; - blockwrite_cmd[0] = (byte) 0xA0; // MF write command - blockwrite_cmd[1] = addr; - System.arraycopy(data, 0, blockwrite_cmd, 2, data.length); - - transceive(blockwrite_cmd); - } - - /** - * Writes relative block in sector. - * @throws IOException - */ - public void writeBlock(int sector, int block, byte[] data) throws IOException { - checkConnected(); - - byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); - - writeBlock(addr, data); - } - - public void increment(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] incr_cmd = { (byte) 0xC1, (byte) block }; - - transceive(incr_cmd); - } - - public void decrement(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] decr_cmd = { (byte) 0xC0, (byte) block }; - - transceive(decr_cmd); - } - - public void transfer(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] trans_cmd = { (byte) 0xB0, (byte) block }; - - transceive(trans_cmd); - } - - public void restore(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] rest_cmd = { (byte) 0xC2, (byte) block }; - - transceive(rest_cmd); - } - - /** - * Send data to a tag and receive the response. - * <p> - * This method will block until the response is received. It can be canceled - * with {@link #close}. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws IOException if the target is lost or connection closed - */ - @Override - public byte[] transceive(byte[] data) throws IOException { - checkConnected(); - - try { - byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false); - if (response == null) { - throw new IOException("transceive failed"); - } - return response; - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } -} diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/technology/MifareUltralight.java deleted file mode 100644 index 7103b4d..0000000 --- a/core/java/android/nfc/technology/MifareUltralight.java +++ /dev/null @@ -1,133 +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 android.nfc.technology; - -import java.io.IOException; - -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -/** - * Concrete class for TagTechnology.MIFARE_ULTRALIGHT - * - * Mifare classic has n sectors, with varying sizes, although - * they are at least the same pattern for any one mifare classic - * product. Each sector has two keys. Authentication with the correct - * key is needed before access to any sector. - * - * Each sector has k blocks. - * Block size is constant across the whole mifare classic family. - */ -public final class MifareUltralight extends BasicTagTechnology { - public static final int TYPE_ULTRALIGHT = 1; - public static final int TYPE_ULTRALIGHT_C = 2; - public static final int TYPE_UNKNOWN = 10; - - private static final int NXP_MANUFACTURER_ID = 0x04; - - private int mType; - - public MifareUltralight(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException { - super(adapter, tag, TagTechnology.MIFARE_ULTRALIGHT); - - // Check if this could actually be a Mifare - NfcA a = (NfcA) tag.getTechnology(adapter, TagTechnology.NFC_A); - - mType = TYPE_UNKNOWN; - - if( a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID ) { - // could be UL or UL-C - mType = TYPE_ULTRALIGHT; - } - } - - public int getType() { - return mType; - } - - // Methods that require connect() - /** - * @throws IOException - */ - public byte[] readBlock(int block) throws IOException { - checkConnected(); - - byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead - return transceive(blockread_cmd); - } - - /** - * @throws IOException - */ - public byte[] readOTP() throws IOException { - checkConnected(); - - return readBlock(3); // OTP is at page 3 - } - - public void writePage(int block, byte[] data) throws IOException { - checkConnected(); - - byte[] pagewrite_cmd = new byte[data.length + 2]; - pagewrite_cmd[0] = (byte) 0xA2; - pagewrite_cmd[1] = (byte) block; - System.arraycopy(data, 0, pagewrite_cmd, 2, data.length); - - transceive(pagewrite_cmd); - } - - public void writeBlock(int block, byte[] data) throws IOException { - checkConnected(); - - byte[] blockwrite_cmd = new byte[data.length + 2]; - blockwrite_cmd[0] = (byte) 0xA0; - blockwrite_cmd[1] = (byte) block; - System.arraycopy(data, 0, blockwrite_cmd, 2, data.length); - - transceive(blockwrite_cmd); - } - - /** - * Send data to a tag and receive the response. - * <p> - * This method will block until the response is received. It can be canceled - * with {@link #close}. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws IOException if the target is lost or connection closed - */ - @Override - public byte[] transceive(byte[] data) throws IOException { - checkConnected(); - - try { - byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false); - if (response == null) { - throw new IOException("transceive failed"); - } - return response; - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } - -} diff --git a/core/java/android/nfc/technology/TagTechnology.java b/core/java/android/nfc/technology/TagTechnology.java deleted file mode 100644 index 62216c1..0000000 --- a/core/java/android/nfc/technology/TagTechnology.java +++ /dev/null @@ -1,94 +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 android.nfc.technology; - -import android.nfc.Tag; - -import java.io.IOException; - -public interface TagTechnology { - /** - * This object is an instance of {@link NfcA} - */ - public static final int NFC_A = 1; - - /** - * This object is an instance of {@link NfcB} - */ - public static final int NFC_B = 2; - - /** - * This object is an instance of {@link IsoDep} - */ - public static final int ISO_DEP = 3; - - /** - * This object is an instance of {@link NfcF} - */ - public static final int NFC_F = 4; - - /** - * This object is an instance of {@link NfcV} - */ - public static final int NFC_V = 5; - - /** - * This object is an instance of {@link Ndef} - */ - public static final int NDEF = 6; - - /** - * This object is an instance of {@link NdefFormatable} - */ - public static final int NDEF_FORMATABLE = 7; - - /** - * This object is an instance of {@link MifareClassic} - */ - public static final int MIFARE_CLASSIC = 8; - - /** - * This object is an instance of {@link MifareUltralight} - */ - public static final int MIFARE_ULTRALIGHT = 9; - - /** - * Returns the technology type for this tag connection. - */ - public int getTechnologyId(); - - /** - * Get the backing tag object. - */ - public Tag getTag(); - - /** - * @throws IOException - */ - public void connect() throws IOException; - - /** - * @throws IOException - */ - public void reconnect() throws IOException; - - /** - * Non-blocking. Immediately causes all blocking calls - * to throw IOException. - */ - public void close(); -} diff --git a/core/java/android/nfc/technology/package.html b/core/java/android/nfc/technology/package.html deleted file mode 100644 index 26b8a32..0000000 --- a/core/java/android/nfc/technology/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<HTML> -<BODY> -{@hide} -</BODY> -</HTML>
\ No newline at end of file diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index b9d4711..4c83515 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -26,7 +26,7 @@ import android.os.RemoteException; * WARNING! Update IMountService.h and IMountService.cpp if you change this * file. In particular, the ordering of the methods below must match the * _TRANSACTION enum in IMountService.cpp - * + * * @hide - Applications should use android.os.storage.StorageManager to access * storage functions. */ @@ -620,6 +620,23 @@ public interface IMountService extends IInterface { } return _result; } + + public int changeEncryptionPassword(String password) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(password); + mRemote.transact(Stub.TRANSACTION_changeEncryptionPassword, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } } private static final String DESCRIPTOR = "IMountService"; @@ -680,6 +697,8 @@ public interface IMountService extends IInterface { static final int TRANSACTION_encryptStorage = IBinder.FIRST_CALL_TRANSACTION + 27; + static final int TRANSACTION_changeEncryptionPassword = IBinder.FIRST_CALL_TRANSACTION + 28; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -977,6 +996,14 @@ public interface IMountService extends IInterface { reply.writeInt(result); return true; } + case TRANSACTION_changeEncryptionPassword: { + data.enforceInterface(DESCRIPTOR); + String password = data.readString(); + int result = changeEncryptionPassword(password); + reply.writeNoException(); + reply.writeInt(result); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -1146,4 +1173,10 @@ public interface IMountService extends IInterface { * Encrypts storage. */ public int encryptStorage(String password) throws RemoteException; + + /** + * Changes the encryption password. + */ + public int changeEncryptionPassword(String password) throws RemoteException; + } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index c46d2c5..6d7b7ce 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -512,10 +512,17 @@ public class TextToSpeech { Intent intent = new Intent("android.intent.action.START_TTS_SERVICE"); intent.addCategory("android.intent.category.TTS"); - mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE); - // TODO handle case where the binding works (should always work) but - // the plugin fails + boolean bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); + if (!bound) { + Log.e("TextToSpeech.java", "initTts() failed to bind to service"); + if (mInitListener != null) { + mInitListener.onInit(ERROR); + } + } else { + // initialization listener will be called inside ServiceConnection + Log.i("TextToSpeech.java", "initTts() successfully bound to service"); + } + // TODO handle plugin failures } @@ -765,8 +772,9 @@ public class TextToSpeech { { synchronized (mStartLock) { int result = ERROR; - Log.i("TTS", "speak() queueMode=" + queueMode); + Log.i("TextToSpeech.java - speak", "speak text of length " + text.length()); if (!mStarted) { + Log.e("TextToSpeech.java - speak", "service isn't started"); return result; } try { @@ -1264,10 +1272,13 @@ public class TextToSpeech { */ public int synthesizeToFile(String text, HashMap<String,String> params, String filename) { - Log.i("TTS", "synthesizeToFile()"); + Log.i("TextToSpeech.java", "synthesizeToFile()"); synchronized (mStartLock) { int result = ERROR; + Log.i("TextToSpeech.java - synthesizeToFile", "synthesizeToFile text of length " + + text.length()); if (!mStarted) { + Log.e("TextToSpeech.java - synthesizeToFile", "service isn't started"); return result; } try { diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 7748265..d5010c6 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -1142,7 +1142,7 @@ public class TextUtils { // XXX this is probably ok, but need to look at it more tempMt.setPara(format, 0, format.length(), request); - float moreWid = mt.addStyleRun(p, mt.mLen, null); + float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null); if (w + moreWid <= avail) { ok = i + 1; diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java index 4d83891..8f491ef 100644 --- a/core/java/android/view/DragEvent.java +++ b/core/java/android/view/DragEvent.java @@ -21,7 +21,100 @@ import android.content.ClipDescription; import android.os.Parcel; import android.os.Parcelable; -/** !!! TODO: real docs */ +//TODO: Improve Javadoc +/** + * Represents an event that is sent out by the system at various times during a drag and drop + * operation. It is a complex data structure that contains several important pieces of data about + * the operation and the underlying data. + * <p> + * View objects that receive a DragEvent call {@link #getAction()}, which returns + * an action type that indicates the state of the drag and drop operation. This allows a View + * object to react to a change in state by changing its appearance or performing other actions. + * For example, a View can react to the {@link #ACTION_DRAG_ENTERED} action type by + * by changing one or more colors in its displayed image. + * </p> + * <p> + * During a drag and drop operation, the system displays an image that the user drags. This image + * is called a drag shadow. Several action types reflect the position of the drag shadow relative + * to the View receiving the event. + * </p> + * <p> + * Most methods return valid data only for certain event actions. This is summarized in the + * following table. Each possible {@link #getAction()} value is listed in the first column. The + * other columns indicate which method or methods return valid data for that getAction() value: + * </p> + * <table> + * <tr> + * <th scope="col">getAction() Value</th> + * <th scope="col">getClipDescription()</th> + * <th scope="col">getLocalState()</th> + * <th scope="col">getX()</th> + * <th scope="col">getY()</th> + * <th scope="col">getClipData()</th> + * <th scope="col">getResult()</th> + * </tr> + * <tr> + * <td>ACTION_DRAG_STARTED</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * </tr> + * <tr> + * <td>ACTION_DRAG_ENTERED</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * </tr> + * <tr> + * <td>ACTION_DRAG_LOCATION</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * </tr> + * <tr> + * <td>ACTION_DRAG_EXITED</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * </tr> + * <tr> + * <td>ACTION_DROP</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;"> </td> + * </tr> + * <tr> + * <td>ACTION_DRAG_ENDED</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;">X</td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;"> </td> + * <td style="text-align: center;">X</td> + * </tr> + * </table> + * <p> + * The {@link android.view.DragEvent#getAction()}, + * {@link android.view.DragEvent#describeContents()}, + * {@link android.view.DragEvent#writeToParcel(Parcel,int)}, and + * {@link android.view.DragEvent#toString()} methods always return valid data. + * </p> + */ public class DragEvent implements Parcelable { private static final boolean TRACK_RECYCLED_LOCATION = false; @@ -42,89 +135,113 @@ public class DragEvent implements Parcelable { private static DragEvent gRecyclerTop = null; /** - * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose - * action is ACTION_DRAG_STARTED means that a drag operation has been initiated. The - * view receiving this DragEvent should inspect the metadata of the dragged content, - * available via {@link #getClipDescription()}, and return {@code true} from - * {@link View#onDragEvent(DragEvent)} if the view is prepared to accept a drop of - * that clip data. If the view chooses to present a visual indication that it is - * a valid target of the ongoing drag, then it should draw that indication in response - * to this event. + * Action constant returned by {@link #getAction()}: Signals the start of a + * drag and drop operation. The View should return {@code true} from its + * {@link View#onDragEvent(DragEvent) onDragEvent()} handler method or + * {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener + * if it can accept a drop. The onDragEvent() or onDrag() methods usually inspect the metadata + * from {@link #getClipDescription()} to determine if they can accept the data contained in + * this drag. For an operation that doesn't represent data transfer, these methods may + * perform other actions to determine whether or not the View should accept the drag. + * If the View wants to indicate that it is a valid drop target, it can also react by + * changing its appearance. * <p> - * A view will only receive ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED, - * and ACTION_DRAG_LOCATION events if it returns {@code true} in response to the - * ACTION_DRAG_STARTED event. + * A View only receives further drag events if it returns {@code true} in response to + * ACTION_DRAG_STARTED. + * </p> + * @see #ACTION_DRAG_ENDED */ public static final int ACTION_DRAG_STARTED = 1; /** - * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose - * action is ACTION_DRAG_LOCATION means that the drag operation is currently hovering - * over the view. The {@link #getX()} and {@link #getY()} methods supply the location - * of the drag point within the view's coordinate system. + * Action constant returned by {@link #getAction()}: Sent to a View after + * {@link #ACTION_DRAG_ENTERED} if the drag shadow is still within the View object's bounding + * box. The {@link #getX()} and {@link #getY()} methods supply + * the X and Y position of of the drag point within the View object's bounding box. + * <p> + * A View receives an {@link #ACTION_DRAG_ENTERED} event before receiving any + * ACTION_DRAG_LOCATION events. + * </p> * <p> - * A view will receive an ACTION_DRAG_ENTERED event before receiving any - * ACTION_DRAG_LOCATION events. If the drag point leaves the view, then an - * ACTION_DRAG_EXITED event is delivered to the view, after which no more - * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view, - * of course). + * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the + * drag shadow out of the View object's bounding box. If the user moves the drag shadow back + * into the View object's bounding box, the View receives an ACTION_DRAG_ENTERED again before + * receiving any more ACTION_DRAG_LOCATION events. + * </p> + * @see #ACTION_DRAG_ENTERED + * @see #getX() + * @see #getY() */ public static final int ACTION_DRAG_LOCATION = 2; /** - * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose - * action is ACTION_DROP means that the dragged content has been dropped on this view. - * The view should retrieve the content via {@link #getClipData()} and act on it - * appropriately. The {@link #getX()} and {@link #getY()} methods supply the location - * of the drop point within the view's coordinate system. + * Action constant returned by {@link #getAction()}: Signals to a View that the user + * has released the drag shadow, and the drag point is within the bounding box of the View. + * The View should retrieve the data from the DragEvent by calling {@link #getClipData()}. + * The methods {@link #getX()} and {@link #getY()} return the X and Y position of the drop point + * within the View object's bounding box. + * <p> + * The View should return {@code true} from its {@link View#onDragEvent(DragEvent)} + * handler or {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} + * listener if it accepted the drop, and {@code false} if it ignored the drop. + * </p> * <p> - * The view should return {@code true} from its {@link View#onDragEvent(DragEvent)} - * method in response to this event if it accepted the content, and {@code false} - * if it ignored the drop. + * The View can also react to this action by changing its appearance. + * </p> + * @see #getClipData() + * @see #getX() + * @see #getY() */ public static final int ACTION_DROP = 3; /** - * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose - * action is ACTION_DRAG_ENDED means that the drag operation has concluded. A view - * that is drawing a visual indication of drag acceptance should return to its usual - * drawing state in response to this event. + * Action constant returned by {@link #getAction()}: Signals to a View that the drag and drop + * operation has concluded. A View that changed its appearance during the operation should + * return to its usual drawing state in response to this event. * <p> * All views that received an ACTION_DRAG_STARTED event will receive the - * ACTION_DRAG_ENDED event even if they are not currently visible when the drag - * ends. + * ACTION_DRAG_ENDED event even if they are not currently visible when the drag ends. + * </p> + * <p> + * The View object can call {@link #getResult()} to see the result of the operation. + * If a View returned {@code true} in response to {@link #ACTION_DROP}, then + * getResult() returns {@code true}, otherwise it returns {@code false}. + * </p> + * @see #ACTION_DRAG_STARTED + * @see #getResult() */ public static final int ACTION_DRAG_ENDED = 4; /** - * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose - * action is ACTION_DRAG_ENTERED means that the drag point has entered the view's - * bounds. If the view changed its visual state in response to the ACTION_DRAG_ENTERED - * event, it should return to its normal drag-in-progress visual state in response to - * this event. + * Action constant returned by {@link #getAction()}: Signals to a View that the drag point has + * entered the bounding box of the View. * <p> - * A view will receive an ACTION_DRAG_ENTERED event before receiving any - * ACTION_DRAG_LOCATION events. If the drag point leaves the view, then an - * ACTION_DRAG_EXITED event is delivered to the view, after which no more - * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view, - * of course). + * If the View can accept a drop, it can react to ACTION_DRAG_ENTERED + * by changing its appearance in a way that tells the user that the View is the current + * drop target. + * </p> + * The system stops sending ACTION_DRAG_LOCATION events to a View once the user moves the + * drag shadow out of the View object's bounding box. If the user moves the drag shadow back + * into the View object's bounding box, the View receives an ACTION_DRAG_ENTERED again before + * receiving any more ACTION_DRAG_LOCATION events. + * </p> + * @see #ACTION_DRAG_ENTERED + * @see #ACTION_DRAG_LOCATION */ public static final int ACTION_DRAG_ENTERED = 5; /** - * Action constant returned by {@link #getAction()}. Delivery of a DragEvent whose - * action is ACTION_DRAG_ENTERED means that the drag point has entered the view's - * bounds. If the view chooses to present a visual indication that it will receive - * the drop if it occurs now, then it should draw that indication in response to - * this event. + * Action constant returned by {@link #getAction()}: Signals that the user has moved the + * drag shadow outside the bounding box of the View. + * The View can react by changing its appearance in a way that tells the user that + * View is no longer the immediate drop target. * <p> - * A view will receive an ACTION_DRAG_ENTERED event before receiving any - * ACTION_DRAG_LOCATION events. If the drag point leaves the view, then an - * ACTION_DRAG_EXITED event is delivered to the view, after which no more - * ACTION_DRAG_LOCATION events will be sent (unless the drag re-enters the view, - * of course). + * After the system sends an ACTION_DRAG_EXITED event to the View, the View receives no more + * ACTION_DRAG_LOCATION events until the user drags the drag shadow back over the View. + * </p> + * */ -public static final int ACTION_DRAG_EXITED = 6; + public static final int ACTION_DRAG_EXITED = 6; private DragEvent() { } @@ -175,64 +292,101 @@ public static final int ACTION_DRAG_EXITED = 6; /** * Inspect the action value of this event. - * @return One of {@link #ACTION_DRAG_STARTED}, {@link #ACTION_DRAG_ENDED}, - * {@link #ACTION_DROP}, {@link #ACTION_DRAG_ENTERED}, {@link #ACTION_DRAG_EXITED}, - * or {@link #ACTION_DRAG_LOCATION}. + * @return One of the following action constants, in the order in which they usually occur + * during a drag and drop operation: + * <ul> + * <li>{@link #ACTION_DRAG_STARTED}</li> + * <li>{@link #ACTION_DRAG_ENTERED}</li> + * <li>{@link #ACTION_DRAG_LOCATION}</li> + * <li>{@link #ACTION_DROP}</li> + * <li>{@link #ACTION_DRAG_EXITED}</li> + * <li>{@link #ACTION_DRAG_ENDED}</li> + * </ul> */ public int getAction() { return mAction; } /** - * For ACTION_DRAG_LOCATION and ACTION_DROP events, returns the x coordinate of the - * drag point. - * @return The current drag point's x coordinate, when relevant. + * Gets the X coordinate of the drag point. The value is only valid if the event action is + * {@link #ACTION_DRAG_LOCATION} or {@link #ACTION_DROP}. + * @return The current drag point's Y coordinate */ public float getX() { return mX; } /** - * For ACTION_DRAG_LOCATION and ACTION_DROP events, returns the y coordinate of the - * drag point. - * @return The current drag point's y coordinate, when relevant. + * Gets the Y coordinate of the drag point. The value is valid if the + * event action is {@link #ACTION_DRAG_ENTERED}, {@link #ACTION_DRAG_LOCATION}, + * {@link #ACTION_DROP}, or {@link #ACTION_DRAG_EXITED}. + * @return The current drag point's Y coordinate */ public float getY() { return mY; } /** - * Provides the data payload of the drag operation. This payload is only available - * for events whose action value is ACTION_DROP. - * @return The ClipData containing the data being dropped on the view. + * Returns the {@link android.content.ClipData} object sent to the system as part of the call + * to + * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. + * This method only returns valid data if the event action is {@link #ACTION_DROP}. + * @return The ClipData sent to the system by startDrag(). */ public ClipData getClipData() { return mClipData; } /** - * Provides a description of the drag operation's data payload. This payload is - * available for all DragEvents other than ACTION_DROP. - * @return A ClipDescription describing the contents of the data being dragged. + * Returns the {@link android.content.ClipDescription} object contained in the + * {@link android.content.ClipData} object sent to the system as part of the call to + * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. + * The drag handler or listener for a View can use the metadata in this object to decide if the + * View can accept the dragged View object's data. + * <p> + * This method returns valid data for all event actions. + * @return The ClipDescription that was part of the ClipData sent to the system by startDrag(). */ public ClipDescription getClipDescription() { return mClipDescription; } /** - * Provides the local state object passed as the {@code myLocalState} parameter to - * View.startDrag(). The object will always be null here if the application receiving - * the DragEvent is not the one that started the drag. + * Returns the local state object sent to the system as part of the call to + * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. + * The object is intended to provide local information about the drag and drop operation. For + * example, it can indicate whether the drag and drop operation is a copy or a move. + * <p> + * This method returns valid data for all event actions. + * </p> + * @return The local state object sent to the system by startDrag(). */ public Object getLocalState() { return mLocalState; } /** - * Provides an indication of whether the drag operation concluded successfully. - * This method is only available on ACTION_DRAG_ENDED events. - * @return {@code true} if the drag operation ended with an accepted drop; {@code false} - * otherwise. + * <p> + * Returns an indication of the result of the drag and drop operation. + * This method only returns valid data if the action type is {@link #ACTION_DRAG_ENDED}. + * The return value depends on what happens after the user releases the drag shadow. + * </p> + * <p> + * If the user releases the drag shadow on a View that can accept a drop, the system sends an + * {@link #ACTION_DROP} event to the View object's drag event listener. If the listener + * returns {@code true}, then getResult() will return {@code true}. + * If the listener returns {@code false}, then getResult() returns {@code false}. + * </p> + * <p> + * Notice that getResult() also returns {@code false} if no {@link #ACTION_DROP} is sent. This + * happens, for example, when the user releases the drag shadow over an area outside of the + * application. In this case, the system sends out {@link #ACTION_DRAG_ENDED} for the current + * operation, but never sends out {@link #ACTION_DROP}. + * </p> + * @return {@code true} if a drag event listener returned {@code true} in response to + * {@link #ACTION_DROP}. If the system did not send {@link #ACTION_DROP} before + * {@link #ACTION_DRAG_ENDED}, or if the listener returned {@code false} in response to + * {@link #ACTION_DROP}, then {@code false} is returned. */ public boolean getResult() { return mDragResult; @@ -271,6 +425,11 @@ public static final int ACTION_DRAG_EXITED = 6; } } + /** + * Returns a string containing a concise, human-readable representation of this DragEvent + * object. + * @return A string representation of the DragEvent object. + */ @Override public String toString() { return "DragEvent{" + Integer.toHexString(System.identityHashCode(this)) @@ -281,10 +440,20 @@ public static final int ACTION_DRAG_EXITED = 6; /* Parcelable interface */ + /** + * Returns information about the {@link android.os.Parcel} representation of this DragEvent + * object. + * @return Information about the {@link android.os.Parcel} representation. + */ public int describeContents() { return 0; } + /** + * Creates a {@link android.os.Parcel} object from this DragEvent object. + * @param dest A {@link android.os.Parcel} object in which to put the DragEvent object. + * @param flags Flags to store in the Parcel. + */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mAction); dest.writeFloat(mX); @@ -304,6 +473,9 @@ public static final int ACTION_DRAG_EXITED = 6; } } + /** + * A container for creating a DragEvent from a Parcel. + */ public static final Parcelable.Creator<DragEvent> CREATOR = new Parcelable.Creator<DragEvent>() { public DragEvent createFromParcel(Parcel in) { diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index d24af52..a17ed9d 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -68,6 +68,7 @@ public abstract class LayoutInflater { private boolean mFactorySet; private Factory mFactory; private Factory2 mFactory2; + private Factory2 mPrivateFactory; private Filter mFilter; private final Object[] mConstructorArgs = new Object[2]; @@ -193,6 +194,7 @@ public abstract class LayoutInflater { mContext = newContext; mFactory = original.mFactory; mFactory2 = original.mFactory2; + mPrivateFactory = original.mPrivateFactory; mFilter = original.mFilter; } @@ -300,6 +302,13 @@ public abstract class LayoutInflater { } /** + * @hide for use by framework + */ + public void setPrivateFactory(Factory2 factory) { + mPrivateFactory = factory; + } + + /** * @return The {@link Filter} currently used by this LayoutInflater to restrict the set of Views * that are allowed to be inflated. */ @@ -651,6 +660,10 @@ public abstract class LayoutInflater { else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs); else view = null; + if (view == null && mPrivateFactory != null) { + view = mPrivateFactory.onCreateView(parent, name, mContext, attrs); + } + if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java index ab515c9..372ac15 100644 --- a/core/java/android/view/MenuInflater.java +++ b/core/java/android/view/MenuInflater.java @@ -228,8 +228,8 @@ public class MenuInflater { private boolean itemAdded; private int itemId; private int itemCategoryOrder; - private String itemTitle; - private String itemTitleCondensed; + private CharSequence itemTitle; + private CharSequence itemTitleCondensed; private int itemIconResId; private char itemAlphabeticShortcut; private char itemNumericShortcut; @@ -311,8 +311,8 @@ public class MenuInflater { final int category = a.getInt(com.android.internal.R.styleable.MenuItem_menuCategory, groupCategory); final int order = a.getInt(com.android.internal.R.styleable.MenuItem_orderInCategory, groupOrder); itemCategoryOrder = (category & Menu.CATEGORY_MASK) | (order & Menu.USER_MASK); - itemTitle = a.getString(com.android.internal.R.styleable.MenuItem_title); - itemTitleCondensed = a.getString(com.android.internal.R.styleable.MenuItem_titleCondensed); + itemTitle = a.getText(com.android.internal.R.styleable.MenuItem_title); + itemTitleCondensed = a.getText(com.android.internal.R.styleable.MenuItem_titleCondensed); itemIconResId = a.getResourceId(com.android.internal.R.styleable.MenuItem_icon, 0); itemAlphabeticShortcut = getShortcut(a.getString(com.android.internal.R.styleable.MenuItem_alphabeticShortcut)); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 966bd8d..87b3d79 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -174,6 +174,7 @@ public class SurfaceView extends View { return true; } }; + private boolean mGlobalListenersAdded; public SurfaceView(Context context) { super(context); @@ -212,9 +213,13 @@ public class SurfaceView extends View { mLayout.token = getWindowToken(); mLayout.setTitle("SurfaceView"); mViewVisibility = getVisibility() == VISIBLE; - ViewTreeObserver observer = getViewTreeObserver(); - observer.addOnScrollChangedListener(mScrollChangedListener); - observer.addOnPreDrawListener(mDrawListener); + + if (!mGlobalListenersAdded) { + ViewTreeObserver observer = getViewTreeObserver(); + observer.addOnScrollChangedListener(mScrollChangedListener); + observer.addOnPreDrawListener(mDrawListener); + mGlobalListenersAdded = true; + } } @Override @@ -275,9 +280,13 @@ public class SurfaceView extends View { @Override protected void onDetachedFromWindow() { - ViewTreeObserver observer = getViewTreeObserver(); - observer.removeOnScrollChangedListener(mScrollChangedListener); - observer.removeOnPreDrawListener(mDrawListener); + if (mGlobalListenersAdded) { + ViewTreeObserver observer = getViewTreeObserver(); + observer.removeOnScrollChangedListener(mScrollChangedListener); + observer.removeOnPreDrawListener(mDrawListener); + mGlobalListenersAdded = false; + } + mRequestedVisible = false; updateWindow(false, false); mHaveFrame = false; @@ -285,6 +294,7 @@ public class SurfaceView extends View { try { mSession.remove(mWindow); } catch (RemoteException ex) { + // Not much we can do here... } mWindow = null; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d0b150b..f111f98 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -521,7 +521,7 @@ import java.util.WeakHashMap; * The framework provides basic support for views that wish to internally * scroll their content. This includes keeping track of the X and Y scroll * offset as well as mechanisms for drawing scrollbars. See - * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)}, and + * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)}, and * {@link #awakenScrollBars()} for more details. * </p> * @@ -1645,27 +1645,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @hide */ static final int OPAQUE_MASK = 0x01800000; - + /** * Indicates a prepressed state; * the short time between ACTION_DOWN and recognizing * a 'real' press. Prepressed is used to recognize quick taps * even when they are shorter than ViewConfiguration.getTapTimeout(). - * + * * @hide */ private static final int PREPRESSED = 0x02000000; - + /** * Indicates whether the view is temporarily detached. * * @hide */ static final int CANCEL_NEXT_UP_EVENT = 0x04000000; - + /** * Indicates that we should awaken scroll bars once attached - * + * * @hide */ private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000; @@ -1720,18 +1720,114 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * View has requested the status bar to be visible (the default). * - * @see #setSystemUiVisibility(int) + * @see #setSystemUiVisibility(int) */ public static final int STATUS_BAR_VISIBLE = 0; /** * View has requested the status bar to be visible (the default). * - * @see #setSystemUiVisibility(int) + * @see #setSystemUiVisibility(int) */ public static final int STATUS_BAR_HIDDEN = 0x00000001; /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to make the status bar not expandable. Unless you also + * set {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show. + */ + public static final int STATUS_BAR_DISABLE_EXPAND = 0x00010000; + + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to hide notification icons and scrolling ticker text. + */ + public static final int STATUS_BAR_DISABLE_NOTIFICATION_ICONS = 0x00020000; + + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to disable incoming notification alerts. This will not block + * icons, but it will block sound, vibrating and other visual or aural notifications. + */ + public static final int STATUS_BAR_DISABLE_NOTIFICATION_ALERTS = 0x00040000; + + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to hide only the scrolling ticker. Note that + * {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS} implies + * {@link #STATUS_BAR_DISABLE_NOTIFICATION_TICKER}. + */ + public static final int STATUS_BAR_DISABLE_NOTIFICATION_TICKER = 0x00080000; + + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to hide the center system info area. + */ + public static final int STATUS_BAR_DISABLE_SYSTEM_INFO = 0x00100000; + + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to hide only the navigation buttons. Don't use this + * unless you're a special part of the system UI (i.e., setup wizard, keyguard). + * + * THIS DOES NOT DISABLE THE BACK BUTTON + */ + public static final int STATUS_BAR_DISABLE_NAVIGATION = 0x00200000; + + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to hide only the back button. Don't use this + * unless you're a special part of the system UI (i.e., setup wizard, keyguard). + */ + public static final int STATUS_BAR_DISABLE_BACK = 0x00400000; + + /** + * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to hide only the clock. You might use this if your activity has + * its own clock making the status bar's clock redundant. + */ + public static final int STATUS_BAR_DISABLE_CLOCK = 0x00800000; + + + /** + * @hide + */ + public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = STATUS_BAR_HIDDEN; + + + /** * Controls the over-scroll mode for this view. * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)}, * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}, @@ -1773,6 +1869,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * This view's request for the visibility of the status bar. * @hide */ + @ViewDebug.ExportedProperty() int mSystemUiVisibility; /** @@ -1854,8 +1951,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private int mPrevWidth = -1; private int mPrevHeight = -1; - private boolean mLastIsOpaque; - + private boolean mLastIsOpaque; + /** * Convenience value to check for float values that are close enough to zero to be considered * zero. @@ -2129,7 +2226,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private CheckForLongPress mPendingCheckForLongPress; private CheckForTap mPendingCheckForTap = null; private PerformClick mPerformClick; - + private UnsetPressedState mUnsetPressedState; /** @@ -2170,7 +2267,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * Special tree observer used when mAttachInfo is null. */ private ViewTreeObserver mFloatingTreeObserver; - + /** * Cache the touch slop from the context that created the view. */ @@ -2210,11 +2307,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * Indicates that the view does not have a layer. - * - * @see #getLayerType() - * @see #setLayerType(int, android.graphics.Paint) + * + * @see #getLayerType() + * @see #setLayerType(int, android.graphics.Paint) * @see #LAYER_TYPE_SOFTWARE - * @see #LAYER_TYPE_HARDWARE + * @see #LAYER_TYPE_HARDWARE */ public static final int LAYER_TYPE_NONE = 0; @@ -2222,7 +2319,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * <p>Indicates that the view has a software layer. A software layer is backed * by a bitmap and causes the view to be rendered using Android's software * rendering pipeline, even if hardware acceleration is enabled.</p> - * + * * <p>Software layers have various usages:</p> * <p>When the application is not using hardware acceleration, a software layer * is useful to apply a specific color filter and/or blending mode and/or @@ -2238,11 +2335,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * potentially be slow (particularly when hardware acceleration is turned on * since the layer will have to be uploaded into a hardware texture after every * update.)</p> - * - * @see #getLayerType() - * @see #setLayerType(int, android.graphics.Paint) + * + * @see #getLayerType() + * @see #setLayerType(int, android.graphics.Paint) * @see #LAYER_TYPE_NONE - * @see #LAYER_TYPE_HARDWARE + * @see #LAYER_TYPE_HARDWARE */ public static final int LAYER_TYPE_SOFTWARE = 1; @@ -2253,7 +2350,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * rendering pipeline, but only if hardware acceleration is turned on for the * view hierarchy. When hardware acceleration is turned off, hardware layers * behave exactly as {@link #LAYER_TYPE_SOFTWARE software layers}.</p> - * + * * <p>A hardware layer is useful to apply a specific color filter and/or * blending mode and/or translucency to a view and all its children.</p> * <p>A hardware layer can be used to cache a complex view tree into a @@ -2263,14 +2360,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * <p>A hardware layer can also be used to increase the rendering quality when * rotation transformations are applied on a view. It can also be used to * prevent potential clipping issues when applying 3D transforms on a view.</p> - * - * @see #getLayerType() + * + * @see #getLayerType() * @see #setLayerType(int, android.graphics.Paint) * @see #LAYER_TYPE_NONE * @see #LAYER_TYPE_SOFTWARE */ public static final int LAYER_TYPE_HARDWARE = 2; - + @ViewDebug.ExportedProperty(category = "drawing", mapping = { @ViewDebug.IntToString(from = LAYER_TYPE_NONE, to = "NONE"), @ViewDebug.IntToString(from = LAYER_TYPE_SOFTWARE, to = "SOFTWARE"), @@ -2572,7 +2669,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility break; case R.styleable.View_onClick: if (context.isRestricted()) { - throw new IllegalStateException("The android:onClick attribute cannot " + throw new IllegalStateException("The android:onClick attribute cannot " + "be used within a restricted context"); } @@ -2811,19 +2908,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility initScrollCache(); final ScrollabilityCache scrollabilityCache = mScrollCache; - + if (scrollabilityCache.scrollBar == null) { scrollabilityCache.scrollBar = new ScrollBarDrawable(); } - + final boolean fadeScrollbars = a.getBoolean(R.styleable.View_fadeScrollbars, true); if (!fadeScrollbars) { scrollabilityCache.state = ScrollabilityCache.ON; } scrollabilityCache.fadeScrollBars = fadeScrollbars; - - + + scrollabilityCache.scrollBarFadeDuration = a.getInt( R.styleable.View_scrollbarFadeDuration, ViewConfiguration .getScrollBarFadeDuration()); @@ -2831,7 +2928,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility R.styleable.View_scrollbarDefaultDelayBeforeFade, ViewConfiguration.getScrollDefaultDelay()); - + scrollabilityCache.scrollBarSize = a.getDimensionPixelSize( com.android.internal.R.styleable.View_scrollbarSize, ViewConfiguration.get(mContext).getScaledScrollBarSize()); @@ -3067,8 +3164,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Register a callback to be invoked when a drag event is sent to this view. - * @param l The drag listener to attach to this view + * Register a drag event listener callback object for this View. The parameter is + * an implementation of {@link android.view.View.OnDragListener}. To send a drag event to a + * View, the system calls the + * {@link android.view.View.OnDragListener#onDrag(View,DragEvent)} method. + * @param l An implementation of {@link android.view.View.OnDragListener}. */ public void setOnDragListener(OnDragListener l) { mOnDragListener = l; @@ -3279,7 +3379,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (mOnFocusChangeListener != null) { mOnFocusChangeListener.onFocusChange(this, gainFocus); } - + if (mAttachInfo != null) { mAttachInfo.mKeyDispatchState.reset(this); } @@ -4427,7 +4527,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public KeyEvent.DispatcherState getKeyDispatcherState() { return mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null; } - + /** * Dispatch a key event before it is processed by any input method * associated with the view hierarchy. This can be used to intercept @@ -4505,7 +4605,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @param event The motion event to be filtered. * @return True if the event should be dispatched, false if the event should be dropped. - * + * * @see #getFilterTouchesWhenObscured */ public boolean onFilterTouchEventForSecurity(MotionEvent event) { @@ -4612,7 +4712,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * a View moves out of the screen, it might receives a display hint indicating * the view is not displayed. Applications should not <em>rely</em> on this hint * as there is no guarantee that they will receive one. - * + * * @param hint A hint about whether or not this view is displayed: * {@link #VISIBLE} or {@link #INVISIBLE}. */ @@ -4625,7 +4725,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * a View moves out of the screen, it might receives a display hint indicating * the view is not displayed. Applications should not <em>rely</em> on this hint * as there is no guarantee that they will receive one. - * + * * @param hint A hint about whether or not this view is displayed: * {@link #VISIBLE} or {@link #INVISIBLE}. */ @@ -5078,7 +5178,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mPrivateFlags |= PRESSED; refreshDrawableState(); } - + if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check removeLongPressCallback(); @@ -5581,7 +5681,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * Returns true if the transform matrix is the identity matrix. * Recomputes the matrix if necessary. - * + * * @return True if the transform matrix is the identity matrix, false otherwise. */ final boolean hasIdentityMatrix() { @@ -5922,16 +6022,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is * completely transparent and 1 means the view is completely opaque.</p> - * + * * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is * responsible for applying the opacity itself. Otherwise, calling this method is * equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and - * setting a hardware layer.</p> + * setting a hardware layer.</p> * * @param alpha The opacity of the view. * - * @see #setLayerType(int, android.graphics.Paint) - * + * @see #setLayerType(int, android.graphics.Paint) + * * @attr ref android.R.styleable#View_alpha */ public void setAlpha(float alpha) { @@ -6197,7 +6297,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * The visual x position of this view, in pixels. This is equivalent to the * {@link #setTranslationX(float) translationX} property plus the current - * {@link #getLeft() left} property. + * {@link #getLeft() left} property. * * @return The visual x position of this view, in pixels. */ @@ -6594,7 +6694,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * provides animated scrolling, the start delay should equal the duration of * the scrolling animation. * </p> - * + * * <p> * The animation starts only if at least one of the scrollbars is enabled, * as specified by {@link #isHorizontalScrollBarEnabled()} and @@ -6603,17 +6703,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * started, this method calls {@link #invalidate()}; in that case the caller * should not call {@link #invalidate()}. * </p> - * + * * <p> * This method should be invoked everytime a subclass directly updates the * scroll parameters. * </p> - * + * * @param startDelay the delay, in milliseconds, after which the animation * should start; when the delay is 0, the animation starts * immediately * @return true if the animation is played, false otherwise - * + * * @see #scrollBy(int, int) * @see #scrollTo(int, int) * @see #isHorizontalScrollBarEnabled() @@ -6624,7 +6724,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility protected boolean awakenScrollBars(int startDelay) { return awakenScrollBars(startDelay, true); } - + /** * <p> * Trigger the scrollbars to draw. When invoked this method starts an @@ -6632,30 +6732,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * provides animated scrolling, the start delay should equal the duration of * the scrolling animation. * </p> - * + * * <p> * The animation starts only if at least one of the scrollbars is enabled, * as specified by {@link #isHorizontalScrollBarEnabled()} and * {@link #isVerticalScrollBarEnabled()}. When the animation is started, * this method returns true, and false otherwise. If the animation is - * started, this method calls {@link #invalidate()} if the invalidate parameter + * started, this method calls {@link #invalidate()} if the invalidate parameter * is set to true; in that case the caller * should not call {@link #invalidate()}. * </p> - * + * * <p> * This method should be invoked everytime a subclass directly updates the * scroll parameters. * </p> - * + * * @param startDelay the delay, in milliseconds, after which the animation * should start; when the delay is 0, the animation starts * immediately - * + * * @param invalidate Wheter this method should call invalidate - * + * * @return true if the animation is played, false otherwise - * + * * @see #scrollBy(int, int) * @see #scrollTo(int, int) * @see #isHorizontalScrollBarEnabled() @@ -6665,7 +6765,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ protected boolean awakenScrollBars(int startDelay, boolean invalidate) { final ScrollabilityCache scrollCache = mScrollCache; - + if (scrollCache == null || !scrollCache.fadeScrollBars) { return false; } @@ -6798,7 +6898,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public void invalidate() { invalidate(true); } - + /** * This is where the invalidate() work actually happens. A full invalidate() * causes the drawing cache to be invalidated, but this function can be called with @@ -6860,7 +6960,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility ((View) mParent).mPrivateFlags |= INVALIDATED; } } - + /** * Used to indicate that the parent of this view should be invalidated. This functionality * is used to force the parent to rebuild its display list (when hardware-accelerated), @@ -7289,12 +7389,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility protected void recomputePadding() { setPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom); } - + /** * Define whether scrollbars will fade when the view is not scrolling. - * + * * @param fadeScrollbars wheter to enable fading - * + * */ public void setScrollbarFadingEnabled(boolean fadeScrollbars) { initScrollCache(); @@ -7306,17 +7406,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility scrollabilityCache.state = ScrollabilityCache.ON; } } - + /** - * + * * Returns true if scrollbars will fade when this view is not scrolling - * + * * @return true if scrollbar fading is enabled */ public boolean isScrollbarFadingEnabled() { - return mScrollCache != null && mScrollCache.fadeScrollBars; + return mScrollCache != null && mScrollCache.fadeScrollBars; } - + /** * <p>Specify the style of the scrollbars. The scrollbars can be overlaid or * inset. When inset, they add to the padding of the view. And the scrollbars @@ -7483,30 +7583,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * scrollbars are painted only if they have been awakened first.</p> * * @param canvas the canvas on which to draw the scrollbars - * + * * @see #awakenScrollBars(int) */ protected final void onDrawScrollBars(Canvas canvas) { // scrollbars are drawn only when the animation is running final ScrollabilityCache cache = mScrollCache; if (cache != null) { - + int state = cache.state; - + if (state == ScrollabilityCache.OFF) { return; } - + boolean invalidate = false; - + if (state == ScrollabilityCache.FADING) { // We're fading -- get our fade interpolation if (cache.interpolatorValues == null) { cache.interpolatorValues = new float[1]; } - + float[] values = cache.interpolatorValues; - + // Stops the animation if we're done if (cache.scrollBarInterpolator.timeToValues(values) == Interpolator.Result.FREEZE_END) { @@ -7514,8 +7614,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } else { cache.scrollBar.setAlpha(Math.round(values[0])); } - - // This will make the scroll bars inval themselves after + + // This will make the scroll bars inval themselves after // drawing. We only want this when we're fading so that // we prevent excessive redraws invalidate = true; @@ -7525,7 +7625,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility cache.scrollBar.setAlpha(255); } - + final int viewFlags = mViewFlags; final boolean drawHorizontalScrollBar = @@ -7545,7 +7645,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; int left, top, right, bottom; - + if (drawHorizontalScrollBar) { int size = scrollBar.getSize(false); if (size <= 0) { @@ -7557,7 +7657,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility computeHorizontalScrollExtent(), false); final int verticalScrollBarGap = drawVerticalScrollBar ? getVerticalScrollbarWidth() : 0; - top = scrollY + height - size - (mUserPaddingBottom & inside); + top = scrollY + height - size - (mUserPaddingBottom & inside); left = scrollX + (mPaddingLeft & inside); right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap; bottom = top + size; @@ -7801,7 +7901,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } void dispatchDetachedFromWindow() { - //System.out.println("Detached! " + this); AttachInfo info = mAttachInfo; if (info != null) { int vis = info.mWindowVisibility; @@ -7811,10 +7910,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } onDetachedFromWindow(); - if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) { + + if ((mPrivateFlags & SCROLL_CONTAINER_ADDED) != 0) { mAttachInfo.mScrollContainers.remove(this); mPrivateFlags &= ~SCROLL_CONTAINER_ADDED; } + mAttachInfo = null; } @@ -7941,8 +8042,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (state != BaseSavedState.EMPTY_STATE && state != null) { throw new IllegalArgumentException("Wrong state class, expecting View State but " + "received " + state.getClass().toString() + " instead. This usually happens " - + "when two views of different type have the same id in the same hierarchy. " - + "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure " + + "when two views of different type have the same id in the same hierarchy. " + + "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure " + "other views do not use the same id."); } } @@ -7967,7 +8068,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * <p>Note: if this view's parent addStateFromChildren property is enabled and this * property is enabled, an exception will be thrown.</p> - * + * * <p>Note: if the child view uses and updates additionnal states which are unknown to the * parent, these states should not be affected by this method.</p> * @@ -7998,7 +8099,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * <p>Specifies the type of layer backing this view. The layer can be * {@link #LAYER_TYPE_NONE disabled}, {@link #LAYER_TYPE_SOFTWARE software} or * {@link #LAYER_TYPE_HARDWARE hardware}.</p> - * + * * <p>A layer is associated with an optional {@link android.graphics.Paint} * instance that controls how the layer is composed on screen. The following * properties of the paint are taken into account when composing the layer:</p> @@ -8007,35 +8108,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li> * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> * </ul> - * + * * <p>If this view has an alpha value set to < 1.0 by calling * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by * this view's alpha value. Calling {@link #setAlpha(float)} is therefore * equivalent to setting a hardware layer on this view and providing a paint with * the desired alpha value.<p> - * + * * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE disabled}, * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware} * for more information on when and how to use layers.</p> - * + * * @param layerType The ype of layer to use with this view, must be one of * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or * {@link #LAYER_TYPE_HARDWARE} * @param paint The paint used to compose the layer. This argument is optional * and can be null. It is ignored when the layer type is * {@link #LAYER_TYPE_NONE} - * - * @see #getLayerType() + * + * @see #getLayerType() * @see #LAYER_TYPE_NONE * @see #LAYER_TYPE_SOFTWARE * @see #LAYER_TYPE_HARDWARE - * @see #setAlpha(float) - * + * @see #setAlpha(float) + * * @attr ref android.R.styleable#View_layerType */ public void setLayerType(int layerType, Paint paint) { if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) { - throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, " + throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, " + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE"); } @@ -8055,7 +8156,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mDrawingCache.recycle(); mDrawingCache = null; } - + if (mUnscaledDrawingCache != null) { mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache = null; @@ -8083,11 +8184,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * a view does not have a layer, and the layer type is {@link #LAYER_TYPE_NONE}. * Refer to the documentation of {@link #setLayerType(int, android.graphics.Paint)} * for more information on the different types of layers. - * + * * @return {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or * {@link #LAYER_TYPE_HARDWARE} - * - * @see #setLayerType(int, android.graphics.Paint) + * + * @see #setLayerType(int, android.graphics.Paint) * @see #LAYER_TYPE_NONE * @see #LAYER_TYPE_SOFTWARE * @see #LAYER_TYPE_HARDWARE @@ -8095,7 +8196,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public int getLayerType() { return mLayerType; } - + /** * <p>Returns a hardware layer that can be used to draw this view again * without executing its draw method.</p> @@ -8109,7 +8210,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final int width = mRight - mLeft; final int height = mBottom - mTop; - + if (width == 0 || height == 0) { return null; } @@ -8136,7 +8237,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID; - + // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { mPrivateFlags &= ~DIRTY_MASK; @@ -8144,7 +8245,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } else { draw(canvas); } - + canvas.restoreToCount(restoreCount); } finally { canvas.onPostDraw(); @@ -8163,7 +8264,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * the cache is enabled. To benefit from the cache, you must request the drawing cache by * calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not * null.</p> - * + * * <p>Enabling the drawing cache is similar to * {@link #setLayerType(int, android.graphics.Paint) setting a layer} when hardware * acceleration is turned off. When hardware acceleration is turned on, enabling the @@ -8181,7 +8282,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @see #isDrawingCacheEnabled() * @see #getDrawingCache() * @see #buildDrawingCache() - * @see #setLayerType(int, android.graphics.Paint) + * @see #setLayerType(int, android.graphics.Paint) */ public void setDrawingCacheEnabled(boolean enabled) { setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED); @@ -8203,7 +8304,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * Debugging utility which recursively outputs the dirty state of a view and its * descendants. - * + * * @hide */ public void outputDirtyFlags(String indent, boolean clear, int clearMask) { @@ -8248,11 +8349,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } return true; } - + /** * <p>Returns a display list that can be used to draw this view again * without executing its draw method.</p> - * + * * @return A DisplayList ready to replay, or null if caching is not enabled. * * @hide @@ -8301,7 +8402,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility computeScroll(); canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID; - + // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { mPrivateFlags &= ~DIRTY_MASK; @@ -8309,7 +8410,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } else { draw(canvas); } - + canvas.restoreToCount(restoreCount); } finally { canvas.onPostDraw(); @@ -8326,9 +8427,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p> - * + * * @return A non-scaled bitmap representing this view or null if cache is disabled. - * + * * @see #getDrawingCache(boolean) */ public Bitmap getDrawingCache() { @@ -8342,7 +8443,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * draw from the cache when the cache is enabled. To benefit from the cache, you must * request the drawing cache by calling this method and draw it on screen if the * returned bitmap is not null.</p> - * + * * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, * this method will create a bitmap of the same size as this view. Because this bitmap * will be drawn scaled by the parent ViewGroup, the result on screen might show @@ -8350,13 +8451,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * the auto scaling to true. Doing so, however, will generate a bitmap of a different * size than the view. This implies that your application must be able to handle this * size.</p> - * + * * @param autoScale Indicates whether the generated bitmap should be scaled based on * the current density of the screen when the application is in compatibility * mode. * * @return A bitmap representing this view or null if cache is disabled. - * + * * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() * @see #buildDrawingCache(boolean) @@ -8422,7 +8523,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p> - * + * * @see #buildDrawingCache(boolean) */ public void buildDrawingCache() { @@ -8435,7 +8536,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * <p>If you call {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p> - * + * * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, * this method will create a bitmap of the same size as this view. Because this bitmap * will be drawn scaled by the parent ViewGroup, the result on screen might show @@ -8443,10 +8544,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * the auto scaling to true. Doing so, however, will generate a bitmap of a different * size than the view. This implies that your application must be able to handle this * size.</p> - * + * * <p>You should avoid calling this method when hardware acceleration is enabled. If * you do not need the drawing cache bitmap, calling this method will increase memory - * usage and cause the view to be rendered in software once, thus negatively impacting + * usage and cause the view to be rendered in software once, thus negatively impacting * performance.</p> * * @see #getDrawingCache() @@ -8559,12 +8660,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility computeScroll(); final int restoreCount = canvas.save(); - + if (autoScale && scalingRequired) { final float scale = attachInfo.mApplicationScale; canvas.scale(scale, scale); } - + canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN; @@ -8605,14 +8706,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final float scale = attachInfo != null ? attachInfo.mApplicationScale : 1.0f; width = (int) ((width * scale) + 0.5f); height = (int) ((height * scale) + 0.5f); - + Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, height > 0 ? height : 1, quality); if (bitmap == null) { throw new OutOfMemoryError(); } bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); - + Canvas canvas; if (attachInfo != null) { canvas = attachInfo.mCanvas; @@ -8762,7 +8863,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * <p>Indicates whether this view is attached to an hardware accelerated * window or not.</p> - * + * * <p>Even if this method returns true, it does not mean that every call * to {@link #draw(android.graphics.Canvas)} will be made with an hardware * accelerated {@link android.graphics.Canvas}. For instance, if this view @@ -8770,14 +8871,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * window is hardware accelerated, * {@link android.graphics.Canvas#isHardwareAccelerated()} will likely * return false, and this method will return true.</p> - * + * * @return True if the view is attached to a window and the window is * hardware accelerated; false in any other case. */ public boolean isHardwareAccelerated() { return mAttachInfo != null && mAttachInfo.mHardwareAccelerated; } - + /** * Manually render this view (and all of its children) to the given Canvas. * The view must have already done a full layout before this function is @@ -10837,28 +10938,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** */ public void dispatchSystemUiVisibilityChanged(int visibility) { - mSystemUiVisibility = visibility; if (mOnSystemUiVisibilityChangeListener != null) { - mOnSystemUiVisibilityChangeListener.onSystemUiVisibilityChange(visibility); + mOnSystemUiVisibilityChangeListener.onSystemUiVisibilityChange( + visibility & ~PUBLIC_STATUS_BAR_VISIBILITY_MASK); } } /** - * !!! TODO: real docs - * - * The base class implementation makes the shadow the same size and appearance - * as the view itself, and positions it with its center at the touch point. + * Creates an image that the system displays during the drag and drop + * operation. This is called a "drag shadow". The default implementation + * for a DragShadowBuilder based on a View returns an image that has exactly the same + * appearance as the given View. The default also positions the center of the drag shadow + * directly under the touch point. If no View is provided (the constructor with no parameters + * is used), and {@link #onProvideShadowMetrics(Point,Point) onProvideShadowMetrics()} and + * {@link #onDrawShadow(Canvas) onDrawShadow()} are not overriden, then the + * default is an invisible drag shadow. + * <p> + * You are not required to use the View you provide to the constructor as the basis of the + * drag shadow. The {@link #onDrawShadow(Canvas) onDrawShadow()} method allows you to draw + * anything you want as the drag shadow. + * </p> + * <p> + * You pass a DragShadowBuilder object to the system when you start the drag. The system + * calls {@link #onProvideShadowMetrics(Point,Point) onProvideShadowMetrics()} to get the + * size and position of the drag shadow. It uses this data to construct a + * {@link android.graphics.Canvas} object, then it calls {@link #onDrawShadow(Canvas) onDrawShadow()} + * so that your application can draw the shadow image in the Canvas. + * </p> */ public static class DragShadowBuilder { private final WeakReference<View> mView; /** - * Construct a shadow builder object for use with the given View object. The - * default implementation will construct a drag shadow the same size and - * appearance as the supplied View. - * - * @param view A view within the application's layout whose appearance - * should be replicated as the drag shadow. + * Constructs a shadow image builder based on a View. By default, the resulting drag + * shadow will have the same appearance and dimensions as the View, with the touch point + * over the center of the View. + * @param view A View. Any View in scope can be used. */ public DragShadowBuilder(View view) { mView = new WeakReference<View>(view); @@ -10869,7 +10984,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)} * and {@link #onDrawShadow(Canvas)} methods are also overridden in order * to supply the drag shadow's dimensions and appearance without - * reference to any View object. + * reference to any View object. If they are not overridden, then the result is an + * invisible drag shadow. */ public DragShadowBuilder() { mView = new WeakReference<View>(null); @@ -10890,22 +11006,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Provide the draggable-shadow metrics for the operation: the dimensions of - * the shadow image itself, and the point within that shadow that should + * Provides the metrics for the shadow image. These include the dimensions of + * the shadow image, and the point within that shadow that should * be centered under the touch location while dragging. * <p> * The default implementation sets the dimensions of the shadow to be the - * same as the dimensions of the View object that had been supplied to the - * {@link #View.DragShadowBuilder(View)} constructor - * when the builder object was instantiated, and centers the shadow under the touch - * point. + * same as the dimensions of the View itself and centers the shadow under + * the touch point. + * </p> + * + * @param shadowSize A {@link android.graphics.Point} containing the width and height + * of the shadow image. Your application must set {@link android.graphics.Point#x} to the + * desired width and must set {@link android.graphics.Point#y} to the desired height of the + * image. * - * @param shadowSize The application should set the {@code x} member of this - * parameter to the desired shadow width, and the {@code y} member to - * the desired height. - * @param shadowTouchPoint The application should set this point to be the - * location within the shadow that should track directly underneath - * the touch point on the screen during a drag. + * @param shadowTouchPoint A {@link android.graphics.Point} for the position within the + * shadow image that should be underneath the touch point during the drag and drop + * operation. Your application must set {@link android.graphics.Point#x} to the + * X coordinate and {@link android.graphics.Point#y} to the Y coordinate of this position. */ public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { final View view = mView.get(); @@ -10918,16 +11036,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Draw the shadow image for the upcoming drag. The shadow canvas was - * created with the dimensions supplied by the + * Draws the shadow image. The system creates the {@link android.graphics.Canvas} object + * based on the dimensions it received from the * {@link #onProvideShadowMetrics(Point, Point)} callback. - * <p> - * The default implementation replicates the appearance of the View object - * that had been supplied to the - * {@link #View.DragShadowBuilder(View)} - * constructor when the builder object was instantiated. * - * @param canvas + * @param canvas A {@link android.graphics.Canvas} object in which to draw the shadow image. */ public void onDrawShadow(Canvas canvas) { final View view = mView.get(); @@ -10940,24 +11053,43 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Drag and drop. App calls startDrag(), then callbacks to the shadow builder's - * {@link DragShadowBuilder#onProvideShadowMetrics(Point, Point)} and - * {@link DragShadowBuilder#onDrawShadow(Canvas)} methods happen, then the drag - * operation is handed over to the OS. - * !!! TODO: real docs - * - * @param data !!! TODO - * @param shadowBuilder !!! TODO - * @param myLocalState An arbitrary object that will be passed as part of every DragEvent - * delivered to the calling application during the course of the current drag operation. - * This object is private to the application that called startDrag(), and is not - * visible to other applications. It provides a lightweight way for the application to - * propagate information from the initiator to the recipient of a drag within its own - * application; for example, to help disambiguate between 'copy' and 'move' semantics. - * @param flags Flags affecting the drag operation. At present no flags are defined; - * pass 0 for this parameter. - * @return {@code true} if the drag operation was initiated successfully; {@code false} if - * an error prevented the drag from taking place. + * Starts a drag and drop operation. When your application calls this method, it passes a + * {@link android.view.View.DragShadowBuilder} object to the system. The + * system calls this object's {@link DragShadowBuilder#onProvideShadowMetrics(Point, Point)} + * to get metrics for the drag shadow, and then calls the object's + * {@link DragShadowBuilder#onDrawShadow(Canvas)} to draw the drag shadow itself. + * <p> + * Once the system has the drag shadow, it begins the drag and drop operation by sending + * drag events to all the View objects in your application that are currently visible. It does + * this either by calling the View object's drag listener (an implementation of + * {@link android.view.View.OnDragListener#onDrag(View,DragEvent) onDrag()} or by calling the + * View object's {@link android.view.View#onDragEvent(DragEvent) onDragEvent()} method. + * Both are passed a {@link android.view.DragEvent} object that has a + * {@link android.view.DragEvent#getAction()} value of + * {@link android.view.DragEvent#ACTION_DRAG_STARTED}. + * </p> + * <p> + * Your application can invoke startDrag() on any attached View object. The View object does not + * need to be the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to + * be related to the View the user selected for dragging. + * </p> + * @param data A {@link android.content.ClipData} object pointing to the data to be + * transferred by the drag and drop operation. + * @param shadowBuilder A {@link android.view.View.DragShadowBuilder} object for building the + * drag shadow. + * @param myLocalState An {@link java.lang.Object} containing local data about the drag and + * drop operation. This Object is put into every DragEvent object sent by the system during the + * current drag. + * <p> + * myLocalState is a lightweight mechanism for the sending information from the dragged View + * to the target Views. For example, it can contain flags that differentiate between a + * a copy operation and a move operation. + * </p> + * @param flags Flags that control the drag and drop operation. No flags are currently defined, + * so the parameter should be set to 0. + * @return {@code true} if the method completes successfully, or + * {@code false} if it fails anywhere. Returning {@code false} means the system was unable to + * do a drag, and so no drag operation is in progress. */ public final boolean startDrag(ClipData data, DragShadowBuilder shadowBuilder, Object myLocalState, int flags) { @@ -11016,42 +11148,48 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Drag-and-drop event dispatch. The event.getAction() verb is one of the DragEvent - * constants DRAG_STARTED_EVENT, DRAG_EVENT, DROP_EVENT, and DRAG_ENDED_EVENT. - * - * For DRAG_STARTED_EVENT, event.getClipDescription() describes the content - * being dragged. onDragEvent() should return 'true' if the view can handle - * a drop of that content. A view that returns 'false' here will receive no - * further calls to onDragEvent() about the drag/drop operation. - * - * For DRAG_ENTERED, event.getClipDescription() describes the content being - * dragged. This will be the same content description passed in the - * DRAG_STARTED_EVENT invocation. - * - * For DRAG_EXITED, event.getClipDescription() describes the content being - * dragged. This will be the same content description passed in the - * DRAG_STARTED_EVENT invocation. The view should return to its approriate - * drag-acceptance visual state. - * - * For DRAG_LOCATION_EVENT, event.getX() and event.getY() give the location in View - * coordinates of the current drag point. The view must return 'true' if it - * can accept a drop of the current drag content, false otherwise. - * - * For DROP_EVENT, event.getX() and event.getY() give the location of the drop - * within the view; also, event.getClipData() returns the full data payload - * being dropped. The view should return 'true' if it consumed the dropped - * content, 'false' if it did not. - * - * For DRAG_ENDED_EVENT, the 'event' argument may be null. The view should return - * to its normal visual state. + * Handles drag events sent by the system following a call to + * {@link android.view.View#startDrag(ClipData,DragShadowBuilder,Object,int) startDrag()}. + *<p> + * When the system calls this method, it passes a + * {@link android.view.DragEvent} object. A call to + * {@link android.view.DragEvent#getAction()} returns one of the action type constants defined + * in DragEvent. The method uses these to determine what is happening in the drag and drop + * operation. + * @param event The {@link android.view.DragEvent} sent by the system. + * The {@link android.view.DragEvent#getAction()} method returns an action type constant defined + * in DragEvent, indicating the type of drag event represented by this object. + * @return {@code true} if the method was successful, otherwise {@code false}. + * <p> + * The method should return {@code true} in response to an action type of + * {@link android.view.DragEvent#ACTION_DRAG_STARTED} to receive drag events for the current + * operation. + * </p> + * <p> + * The method should also return {@code true} in response to an action type of + * {@link android.view.DragEvent#ACTION_DROP} if it consumed the drop, or + * {@code false} if it didn't. + * </p> */ public boolean onDragEvent(DragEvent event) { return false; } /** - * Views typically don't need to override dispatchDragEvent(); it just calls - * onDragEvent(event) and passes the result up appropriately. + * Detects if this View is enabled and has a drag event listener. + * If both are true, then it calls the drag event listener with the + * {@link android.view.DragEvent} it received. If the drag event listener returns + * {@code true}, then dispatchDragEvent() returns {@code true}. + * <p> + * For all other cases, the method calls the + * {@link android.view.View#onDragEvent(DragEvent) onDragEvent()} drag event handler + * method and returns its result. + * </p> + * <p> + * This ensures that a drag event is always consumed, even if the View does not have a drag + * event listener. However, if the View has a listener and the listener returns true, then + * onDragEvent() is not called. + * </p> */ public boolean dispatchDragEvent(DragEvent event) { if (mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED @@ -11068,7 +11206,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ public void onCloseSystemDialogs(String reason) { } - + /** * Given a Drawable whose bounds have been set to draw into this view, * update a Region being computed for {@link #gatherTransparentRegion} so @@ -11400,7 +11538,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mOriginalWindowAttachCount = mWindowAttachCount; } } - + private final class CheckForTap implements Runnable { public void run() { mPrivateFlags &= ~PREPRESSED; @@ -11422,7 +11560,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public void hackTurnOffWindowResizeAnim(boolean off) { mAttachInfo.mTurnOffWindowResizeAnim = off; } - + /** * Interface definition for a callback to be invoked when a key event is * dispatched to this view. The callback will be invoked before the key @@ -11485,11 +11623,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * Called when a drag event is dispatched to a view. This allows listeners * to get a chance to override base View behavior. * - * @param v The view the drag has been dispatched to. - * @param event The DragEvent object containing full information - * about the event. - * @return true if the listener consumed the DragEvent, false in order to fall - * back to the view's default handling. + * @param v The View that received the drag event. + * @param event The {@link android.view.DragEvent} object for the drag event. + * @return {@code true} if the drag event was handled successfully, or {@code false} + * if the drag event was not handled. Note that {@code false} will trigger the View + * to call its {@link #onDragEvent(DragEvent) onDragEvent()} handler. */ boolean onDrag(View v, DragEvent event); } @@ -11677,7 +11815,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility boolean mHardwareAccelerated; boolean mHardwareAccelerationRequested; HardwareRenderer mHardwareRenderer; - + /** * Scale factor used by the compatibility mode */ @@ -11692,7 +11830,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * If set, ViewRoot doesn't use its lame animation for when the window resizes. */ boolean mTurnOffWindowResizeAnim; - + /** * Left position of this view's window */ @@ -11885,7 +12023,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * instances of View.</p> */ private static class ScrollabilityCache implements Runnable { - + /** * Scrollbars are not visible */ @@ -11902,7 +12040,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public static final int FADING = 2; public boolean fadeScrollBars; - + public int fadingEdgeLength; public int scrollBarDefaultDelayBeforeFade; public int scrollBarFadeDuration; @@ -11920,7 +12058,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private static final float[] OPAQUE = { 255 }; private static final float[] TRANSPARENT = { 0.0f }; - + /** * When fading should start. This time moves into the future every time * a new scroll happens. Measured based on SystemClock.uptimeMillis() @@ -11965,7 +12103,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility paint.setXfermode(null); } } - + public void run() { long now = AnimationUtils.currentAnimationTimeMillis(); if (now >= fadeStartTime) { diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index b21af41..b1d509a 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -1551,11 +1551,13 @@ public final class ViewRoot extends Handler implements ViewParent, Log.e(TAG, "OutOfResourcesException locking surface", e); // TODO: we should ask the window manager to do something! // for now we just do nothing + mLayoutRequested = true; // ask wm for a new surface next time. return; } catch (IllegalArgumentException e) { Log.e(TAG, "IllegalArgumentException locking surface", e); // TODO: we should ask the window manager to do something! // for now we just do nothing + mLayoutRequested = true; // ask wm for a new surface next time. return; } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 7edfd7b..cb67b78 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -531,9 +531,9 @@ public final class InputMethodManager { } /** @hide */ - public void setIMEButtonVisible(IBinder imeToken, boolean visible) { + public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) { try { - mService.setIMEButtonVisible(imeToken, visible); + mService.setImeWindowStatus(imeToken, vis, backDisposition); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 6363299..fcfcc03 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -350,6 +350,7 @@ public class WebView extends AbsoluteLayout private ZoomManager mZoomManager; private Rect mGLRectViewport = new Rect(); + private boolean mGLViewportEmpty = false; /** * Transportation object for returning WebView across thread boundaries. @@ -2505,6 +2506,7 @@ public class WebView extends AbsoluteLayout // Rect.equals() checks for null input. if (!rect.equals(mLastVisibleRectSent)) { Point pos = new Point(rect.left, rect.top); + mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET); mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET, nativeMoveGeneration(), mUserScroll ? 1 : 0, pos); mLastVisibleRectSent = rect; @@ -3734,6 +3736,10 @@ public class WebView extends AbsoluteLayout return; } + if (canvas.isHardwareAccelerated()) { + mZoomManager.setHardwareAccelerated(); + } + int saveCount = canvas.save(); if (mInOverScrollMode && !getSettings() .getUseWebViewBackgroundForOverscrollBackground()) { @@ -4071,7 +4077,8 @@ public class WebView extends AbsoluteLayout } if (canvas.isHardwareAccelerated()) { - int functor = nativeGetDrawGLFunction(mGLRectViewport, getScale(), extras); + int functor = nativeGetDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport, + getScale(), extras); ((HardwareCanvas) canvas).callDrawGLFunction(functor); } else { DrawFilter df = null; @@ -5155,16 +5162,21 @@ public class WebView extends AbsoluteLayout void setGLRectViewport() { // Use the getGlobalVisibleRect() to get the intersection among the parents - getGlobalVisibleRect(mGLRectViewport); - - // Then need to invert the Y axis, just for GL - View rootView = getRootView(); - int rootViewHeight = rootView.getHeight(); - int savedWebViewBottom = mGLRectViewport.bottom; - mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeight(); - mGLRectViewport.top = rootViewHeight - savedWebViewBottom; - - nativeUpdateDrawGLFunction(mGLRectViewport); + // visible == false means we're clipped - send a null rect down to indicate that + // we should not draw + boolean visible = getGlobalVisibleRect(mGLRectViewport); + if (visible) { + // Then need to invert the Y axis, just for GL + View rootView = getRootView(); + int rootViewHeight = rootView.getHeight(); + int savedWebViewBottom = mGLRectViewport.bottom; + mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeight(); + mGLRectViewport.top = rootViewHeight - savedWebViewBottom; + mGLViewportEmpty = false; + } else { + mGLViewportEmpty = true; + } + nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport); } /** @@ -6613,15 +6625,10 @@ public class WebView extends AbsoluteLayout * @param y New y position of the WebTextView in view coordinates */ /* package */ void scrollFocusedTextInputY(int y) { - if (!inEditingMode()) { + if (!inEditingMode() || mWebViewCore == null) { return; } - int xPos = viewToContentX((mWebTextView.getLeft() + mWebTextView.getRight()) / 2); - int yPos = viewToContentY((mWebTextView.getTop() + mWebTextView.getBottom()) / 2); - int layer = nativeScrollableLayer(xPos, yPos, null, null); - if (layer != 0) { - nativeScrollLayer(layer, 0, viewToContentDimension(y)); - } + mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0, viewToContentDimension(y)); } /** @@ -6824,7 +6831,7 @@ public class WebView extends AbsoluteLayout previouslyFocusedRect); } else { result = super.requestFocus(direction, previouslyFocusedRect); - if (mWebViewCore.getSettings().getNeedInitialFocus()) { + if (mWebViewCore.getSettings().getNeedInitialFocus() && !isInTouchMode()) { // For cases such as GMail, where we gain focus from a direction, // we want to move to the first available link. // FIXME: If there are no visible links, we may not want to @@ -7968,18 +7975,15 @@ public class WebView extends AbsoluteLayout * @param node Pointer to the node touched. * @param x x-position of the touch. * @param y y-position of the touch. - * @param scrollY Only used when touching on a textarea. Otherwise, use -1. - * Tells how much the textarea is scrolled. */ private void sendMotionUp(int touchGeneration, - int frame, int node, int x, int y, int scrollY) { + int frame, int node, int x, int y) { WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData(); touchUpData.mMoveGeneration = touchGeneration; touchUpData.mFrame = frame; touchUpData.mNode = node; touchUpData.mX = x; touchUpData.mY = y; - touchUpData.mScrollY = scrollY; mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 3bde000..b949a41 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -559,7 +559,7 @@ final class WebViewCore { private native String nativeRetrieveImageSource(int x, int y); private native void nativeTouchUp(int touchGeneration, - int framePtr, int nodePtr, int x, int y, int scrollY); + int framePtr, int nodePtr, int x, int y); private native boolean nativeHandleTouchEvent(int action, int[] idArray, int[] xArray, int[] yArray, int count, int metaState); @@ -777,8 +777,6 @@ final class WebViewCore { int mNode; int mX; int mY; - // Used in the case of a scrolled textarea - int mScrollY; } static class TouchHighlightData { @@ -1086,8 +1084,13 @@ final class WebViewCore { break; case SCROLL_TEXT_INPUT: - nativeScrollFocusedTextInput( - ((Float) msg.obj).floatValue(), msg.arg1); + float xPercent; + if (msg.obj == null) { + xPercent = 0f; + } else { + xPercent = ((Float) msg.obj).floatValue(); + } + nativeScrollFocusedTextInput(xPercent, msg.arg2); break; case LOAD_URL: { @@ -1307,8 +1310,7 @@ final class WebViewCore { TouchUpData touchUpData = (TouchUpData) msg.obj; nativeTouchUp(touchUpData.mMoveGeneration, touchUpData.mFrame, touchUpData.mNode, - touchUpData.mX, touchUpData.mY, - touchUpData.mScrollY); + touchUpData.mX, touchUpData.mY); break; case TOUCH_EVENT: { diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index b47fe86..efbcd58 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -183,6 +183,9 @@ class ZoomManager { private ScaleGestureDetector mScaleDetector; private boolean mPinchToZoomAnimating = false; + private boolean mHardwareAccelerated = false; + private boolean mInHWAcceleratedZoom = false; + public ZoomManager(WebView webView, CallbackProxy callbackProxy) { mWebView = webView; mCallbackProxy = callbackProxy; @@ -384,6 +387,10 @@ class ZoomManager { scale = getReadingLevelScale(); } + if (mHardwareAccelerated) { + mInHWAcceleratedZoom = true; + } + setZoomScale(scale, reflowText); if (oldScale != mActualScale) { @@ -447,8 +454,18 @@ class ZoomManager { - titleHeight, mWebView.getViewHeight(), Math.round(mWebView.getContentHeight() * zoomScale)) + titleHeight) + mWebView.getScrollY(); - canvas.translate(tx, ty); - canvas.scale(zoomScale, zoomScale); + if (mHardwareAccelerated) { + mWebView.updateScrollCoordinates(mWebView.getScrollX() - tx, mWebView.getScrollY() - ty); + setZoomScale(zoomScale, false); + + if (mZoomScale == 0) { + // We've reached the end of the zoom animation. + mInHWAcceleratedZoom = false; + } + } else { + canvas.translate(tx, ty); + canvas.scale(zoomScale, zoomScale); + } } public boolean isZoomAnimating() { @@ -493,12 +510,14 @@ class ZoomManager { mActualScale = scale; mInvActualScale = 1 / scale; - if (!mWebView.drawHistory()) { + if (!mWebView.drawHistory() && !mInHWAcceleratedZoom) { // If history Picture is drawn, don't update scroll. They will // be updated when we get out of that mode. // update our scroll so we don't appear to jump // i.e. keep the center of the doc in the center of the view + // If this is part of a zoom on a HW accelerated canvas, we + // have already updated the scroll so don't do it again. int oldX = mWebView.getScrollX(); int oldY = mWebView.getScrollY(); float ratio = scale * oldInvScale; @@ -1020,4 +1039,8 @@ class ZoomManager { return null; } } + + public void setHardwareAccelerated() { + mHardwareAccelerated = true; + } } diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 7293572..4a34b45 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -30,6 +30,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; +import android.view.accessibility.AccessibilityEvent; import android.widget.NumberPicker.OnValueChangeListener; import java.text.ParseException; @@ -353,6 +354,16 @@ public class DatePicker extends FrameLayout { return mIsEnabled; } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY + | DateUtils.FORMAT_SHOW_YEAR; + String selectedDateUtterance = DateUtils.formatDateTime(mContext, + mCurrentDate.getTimeInMillis(), flags); + event.getText().add(selectedDateUtterance); + return true; + } + /** * Gets whether the {@link CalendarView} is shown. * @@ -641,6 +652,7 @@ public class DatePicker extends FrameLayout { * Notifies the listener, if such, for a change in the selected date. */ private void notifyDateChanged() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); if (mOnDateChangedListener != null) { mOnDateChangedListener.onDateChanged(this, getYear(), getMonth(), getDayOfMonth()); } diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index 7a74241..6a09d35 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -546,12 +546,16 @@ public class StackView extends AdapterViewAnimator { private void onLayout() { if (!mFirstLayoutHappened) { - mSlideAmount = Math.round(SLIDE_UP_RATIO * getMeasuredHeight()); - mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO * mSlideAmount); mFirstLayoutHappened = true; updateChildTransforms(); } + final int newSlideAmount = Math.round(SLIDE_UP_RATIO * getMeasuredHeight()); + if (mSlideAmount != newSlideAmount) { + mSlideAmount = newSlideAmount; + mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO * newSlideAmount); + } + if (Float.compare(mPerspectiveShiftY, mNewPerspectiveShiftY) != 0 || Float.compare(mPerspectiveShiftX, mNewPerspectiveShiftX) != 0) { diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 51ece33..1a4ff29 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -132,7 +132,17 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { mRightStrip = resources.getDrawable( com.android.internal.R.drawable.tab_bottom_right_v4); } - } + } else { + // Use modern color scheme for Eclair and beyond + if (mLeftStrip == null) { + mLeftStrip = resources.getDrawable( + com.android.internal.R.drawable.tab_bottom_left); + } + if (mRightStrip == null) { + mRightStrip = resources.getDrawable( + com.android.internal.R.drawable.tab_bottom_right); + } + } // Deal with focus, as we don't want the focus to go by default // to a tab other than the current tab diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 4ba907f..e7c33ab 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4615,9 +4615,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mInput.onKeyDown(this, (Editable)mText, keyCode, down); mInput.onKeyUp(this, (Editable)mText, keyCode, up); } - if (mError != null && !mErrorWasChanged) { - setError(null, null); - } + hideErrorIfUnchanged(); } else if (which == 2) { mMovement.onKeyUp(this, (Spannable)mText, keyCode, up); @@ -4738,23 +4736,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (mInput != null) { - /* - * Keep track of what the error was before doing the input - * so that if an input filter changed the error, we leave - * that error showing. Otherwise, we take down whatever - * error was showing when the user types something. - */ - mErrorWasChanged = false; + resetErrorChangedFlag(); boolean doDown = true; if (otherEvent != null) { try { beginBatchEdit(); - boolean handled = mInput.onKeyOther(this, (Editable) mText, - otherEvent); - if (mError != null && !mErrorWasChanged) { - setError(null, null); - } + final boolean handled = mInput.onKeyOther(this, (Editable) mText, otherEvent); + hideErrorIfUnchanged(); doDown = false; if (handled) { return -1; @@ -4769,14 +4758,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (doDown) { beginBatchEdit(); - if (mInput.onKeyDown(this, (Editable) mText, keyCode, event)) { - endBatchEdit(); - if (mError != null && !mErrorWasChanged) { - setError(null, null); - } - return 1; - } + final boolean handled = mInput.onKeyDown(this, (Editable) mText, keyCode, event); endBatchEdit(); + hideErrorIfUnchanged(); + if (handled) return 1; } } @@ -4807,6 +4792,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return 0; } + /** + * Resets the mErrorWasChanged flag, so that future calls to {@link #setError(CharSequence)} + * can be recorded. + * @hide + */ + public void resetErrorChangedFlag() { + /* + * Keep track of what the error was before doing the input + * so that if an input filter changed the error, we leave + * that error showing. Otherwise, we take down whatever + * error was showing when the user types something. + */ + mErrorWasChanged = false; + } + + /** + * @hide + */ + public void hideErrorIfUnchanged() { + if (mError != null && !mErrorWasChanged) { + setError(null, null); + } + } + @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (!isEnabled()) { @@ -7386,12 +7395,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (!mInsertionControllerEnabled) { hideInsertionPointCursorController(); - mInsertionPointCursorController = null; + if (mInsertionPointCursorController != null) { + mInsertionPointCursorController.onDetached(); + mInsertionPointCursorController = null; + } } if (!mSelectionControllerEnabled) { stopSelectionActionMode(); - mSelectionModifierCursorController = null; + if (mSelectionModifierCursorController != null) { + mSelectionModifierCursorController.onDetached(); + mSelectionModifierCursorController = null; + } } } diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 2688b95..4b37beb 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -23,9 +23,11 @@ import android.content.Context; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; +import android.text.format.DateUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.view.accessibility.AccessibilityEvent; import android.widget.NumberPicker.OnValueChangeListener; import java.text.DateFormatSymbols; @@ -88,6 +90,8 @@ public class TimePicker extends FrameLayout { // callbacks private OnTimeChangedListener mOnTimeChangedListener; + private Calendar mTempCalendar; + /** * The callback interface used to indicate the time has been adjusted. */ @@ -214,12 +218,12 @@ public class TimePicker extends FrameLayout { updateAmPmControl(); // initialize to current time - Calendar calendar = Calendar.getInstance(); + mTempCalendar = Calendar.getInstance(); setOnTimeChangedListener(NO_OP_CHANGE_LISTENER); // set to current time - setCurrentHour(calendar.get(Calendar.HOUR_OF_DAY)); - setCurrentMinute(calendar.get(Calendar.MINUTE)); + setCurrentHour(mTempCalendar.get(Calendar.HOUR_OF_DAY)); + setCurrentMinute(mTempCalendar.get(Calendar.MINUTE)); if (!isEnabled()) { setEnabled(false); @@ -406,6 +410,22 @@ public class TimePicker extends FrameLayout { return mHourSpinner.getBaseline(); } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + int flags = DateUtils.FORMAT_SHOW_TIME; + if (mIs24HourView) { + flags |= DateUtils.FORMAT_24HOUR; + } else { + flags |= DateUtils.FORMAT_12HOUR; + } + mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour()); + mTempCalendar.set(Calendar.MINUTE, getCurrentMinute()); + String selectedDateUtterance = DateUtils.formatDateTime(mContext, + mTempCalendar.getTimeInMillis(), flags); + event.getText().add(selectedDateUtterance); + return true; + } + private void updateHourControl() { if (is24HourView()) { mHourSpinner.setMinValue(0); @@ -435,9 +455,11 @@ public class TimePicker extends FrameLayout { mAmPmButton.setVisibility(View.VISIBLE); } } + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); } private void onTimeChanged() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); if (mOnTimeChangedListener != null) { mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute()); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 38ac37d..70f90d3 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -50,6 +50,6 @@ public class ChooserActivity extends ResolverActivity { initialIntents[i] = (Intent)pa[i]; } } - super.onCreate(savedInstanceState, target, title, initialIntents, false); + super.onCreate(savedInstanceState, target, title, initialIntents, null, false); } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 215e9ae..841de06 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -63,11 +63,12 @@ public class ResolverActivity extends AlertActivity implements protected void onCreate(Bundle savedInstanceState) { onCreate(savedInstanceState, new Intent(getIntent()), getResources().getText(com.android.internal.R.string.whichApplication), - null, true); + null, null, true); } protected void onCreate(Bundle savedInstanceState, Intent intent, - CharSequence title, Intent[] initialIntents, boolean alwaysUseOption) { + CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList, + boolean alwaysUseOption) { super.onCreate(savedInstanceState); mPm = getPackageManager(); intent.setComponent(null); @@ -88,7 +89,7 @@ public class ResolverActivity extends AlertActivity implements com.android.internal.R.id.clearDefaultHint); mClearDefaultHint.setVisibility(View.GONE); } - mAdapter = new ResolveListAdapter(this, intent, initialIntents); + mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList); if (mAdapter.getCount() > 1) { ap.mAdapter = mAdapter; } else if (mAdapter.getCount() == 1) { @@ -215,14 +216,16 @@ public class ResolverActivity extends AlertActivity implements private List<DisplayResolveInfo> mList; public ResolveListAdapter(Context context, Intent intent, - Intent[] initialIntents) { + Intent[] initialIntents, List<ResolveInfo> rList) { mIntent = new Intent(intent); mIntent.setComponent(null); mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - List<ResolveInfo> rList = mPm.queryIntentActivities( - intent, PackageManager.MATCH_DEFAULT_ONLY - | (mAlwaysCheck != null ? PackageManager.GET_RESOLVED_FILTER : 0)); + if (rList == null) { + rList = mPm.queryIntentActivities( + intent, PackageManager.MATCH_DEFAULT_ONLY + | (mAlwaysCheck != null ? PackageManager.GET_RESOLVED_FILTER : 0)); + } int N; if ((rList != null) && ((N = rList.size()) > 0)) { // Only display the first matches that are either of equal diff --git a/core/java/com/android/internal/nfc/LlcpSocket.java b/core/java/com/android/internal/nfc/LlcpSocket.java index 73c0925..63888ae 100644 --- a/core/java/com/android/internal/nfc/LlcpSocket.java +++ b/core/java/com/android/internal/nfc/LlcpSocket.java @@ -193,7 +193,7 @@ public class LlcpSocket { throw new IOException(); } } catch (RemoteException e) { - Log.e(TAG, "RemoteException in send(): ", e); + Log.e(TAG, "RemoteException in receive(): ", e); } return receivedLength; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index b682947..b3b80f6 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -4087,7 +4087,7 @@ public final class BatteryStatsImpl extends BatteryStats { // we have gone through a significant charge (from a very low // level to a now very high level). if (oldStatus == BatteryManager.BATTERY_STATUS_FULL - || level >= 95 + || level >= 90 || (mDischargeCurrentLevel < 20 && level >= 80)) { doWrite = true; resetAllStatsLocked(); diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 1cc068f..7f23ed5 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -32,6 +32,7 @@ oneway interface IStatusBar void animateCollapse(); void setLightsOn(boolean on); void setMenuKeyVisible(boolean visible); - void setIMEButtonVisible(in IBinder token, boolean visible); + void setImeWindowStatus(in IBinder token, int vis, int backDisposition); + void setHardKeyboardStatus(boolean available, boolean enabled); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index d1ea52e..d6ca426 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -31,7 +31,7 @@ interface IStatusBarService void setIconVisibility(String slot, boolean visible); void removeIcon(String slot); void setMenuKeyVisible(boolean visible); - void setIMEButtonVisible(in IBinder token, boolean visible); + void setImeWindowStatus(in IBinder token, int vis, int backDisposition); // ---- Methods below are for use by the status bar policy services ---- // You need the STATUS_BAR_SERVICE permission @@ -45,4 +45,5 @@ interface IStatusBarService void onClearAllNotifications(); void onNotificationClear(String pkg, String tag, int id); void setSystemUiVisibility(int vis); + void setHardKeyboardEnabled(boolean enabled); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index b2fbd3a..611d987 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -59,7 +59,7 @@ interface IInputMethodManager { void hideMySoftInput(in IBinder token, int flags); void showMySoftInput(in IBinder token, int flags); void updateStatusIcon(in IBinder token, String packageName, int iconId); - void setIMEButtonVisible(in IBinder token, boolean visible); + void setImeWindowStatus(in IBinder token, int vis, int backDisposition); InputMethodSubtype getCurrentInputMethodSubtype(); boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype); boolean switchToLastInputMethod(in IBinder token); diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java index bc749d8..0885b6e 100644 --- a/core/java/com/android/internal/widget/DigitalClock.java +++ b/core/java/com/android/internal/widget/DigitalClock.java @@ -34,6 +34,7 @@ import android.view.View; import android.widget.RelativeLayout; import android.widget.TextView; +import java.lang.ref.WeakReference; import java.text.DateFormatSymbols; import java.util.Calendar; @@ -54,26 +55,45 @@ public class DigitalClock extends RelativeLayout { private TextView mTimeDisplayForeground; private AmPm mAmPm; private ContentObserver mFormatChangeObserver; - private boolean mLive = true; - private boolean mAttached; + private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced /* called by system on minute ticks */ private final Handler mHandler = new Handler(); - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (mLive && intent.getAction().equals( - Intent.ACTION_TIMEZONE_CHANGED)) { - mCalendar = Calendar.getInstance(); - } - // Post a runnable to avoid blocking the broadcast. - mHandler.post(new Runnable() { - public void run() { - updateTime(); + private BroadcastReceiver mIntentReceiver; + + private static class TimeChangedReceiver extends BroadcastReceiver { + private WeakReference<DigitalClock> mClock; + private Context mContext; + + public TimeChangedReceiver(DigitalClock clock) { + mClock = new WeakReference<DigitalClock>(clock); + mContext = clock.getContext(); + } + + @Override + public void onReceive(Context context, Intent intent) { + // Post a runnable to avoid blocking the broadcast. + final boolean timezoneChanged = + intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED); + final DigitalClock clock = mClock.get(); + if (clock != null) { + clock.mHandler.post(new Runnable() { + public void run() { + if (timezoneChanged) { + clock.mCalendar = Calendar.getInstance(); } + clock.updateTime(); + } }); + } else { + try { + mContext.unregisterReceiver(this); + } catch (RuntimeException e) { + // Shouldn't happen + } } - }; + } + }; static class AmPm { private TextView mAmPm; @@ -99,14 +119,27 @@ public class DigitalClock extends RelativeLayout { } } - private class FormatChangeObserver extends ContentObserver { - public FormatChangeObserver() { + private static class FormatChangeObserver extends ContentObserver { + private WeakReference<DigitalClock> mClock; + private Context mContext; + public FormatChangeObserver(DigitalClock clock) { super(new Handler()); + mClock = new WeakReference<DigitalClock>(clock); + mContext = clock.getContext(); } @Override public void onChange(boolean selfChange) { - setDateFormat(); - updateTime(); + DigitalClock digitalClock = mClock.get(); + if (digitalClock != null) { + digitalClock.setDateFormat(); + digitalClock.updateTime(); + } else { + try { + mContext.getContentResolver().unregisterContentObserver(this); + } catch (RuntimeException e) { + // Shouldn't happen + } + } } } @@ -139,11 +172,11 @@ public class DigitalClock extends RelativeLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (mAttached) return; - mAttached = true; + mAttached++; - if (mLive) { - /* monitor time ticks, time changed, timezone */ + /* monitor time ticks, time changed, timezone */ + if (mIntentReceiver == null) { + mIntentReceiver = new TimeChangedReceiver(this); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED); @@ -152,9 +185,11 @@ public class DigitalClock extends RelativeLayout { } /* monitor 12/24-hour display preference */ - mFormatChangeObserver = new FormatChangeObserver(); - mContext.getContentResolver().registerContentObserver( - Settings.System.CONTENT_URI, true, mFormatChangeObserver); + if (mFormatChangeObserver == null) { + mFormatChangeObserver = new FormatChangeObserver(this); + mContext.getContentResolver().registerContentObserver( + Settings.System.CONTENT_URI, true, mFormatChangeObserver); + } updateTime(); } @@ -163,16 +198,19 @@ public class DigitalClock extends RelativeLayout { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (!mAttached) return; - mAttached = false; + mAttached--; - if (mLive) { + if (mIntentReceiver != null) { mContext.unregisterReceiver(mIntentReceiver); } - mContext.getContentResolver().unregisterContentObserver( - mFormatChangeObserver); - } + if (mFormatChangeObserver != null) { + mContext.getContentResolver().unregisterContentObserver( + mFormatChangeObserver); + } + mFormatChangeObserver = null; + mIntentReceiver = null; + } void updateTime(Calendar c) { mCalendar = c; @@ -180,9 +218,7 @@ public class DigitalClock extends RelativeLayout { } private void updateTime() { - if (mLive) { - mCalendar.setTimeInMillis(System.currentTimeMillis()); - } + mCalendar.setTimeInMillis(System.currentTimeMillis()); CharSequence newTime = DateFormat.format(mFormat, mCalendar); mTimeDisplayBackground.setText(newTime); @@ -195,8 +231,4 @@ public class DigitalClock extends RelativeLayout { ? M24 : M12; mAmPm.setShowAmPm(mFormat.equals(M12)); } - - void setLive(boolean live) { - mLive = live; - } } diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index e992e7c..9f9f020 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -138,9 +138,9 @@ public class EditableInputConnection extends BaseInputConnection { return super.commitText(text, newCursorPosition); } - CharSequence errorBefore = mTextView.getError(); + mTextView.resetErrorChangedFlag(); boolean success = super.commitText(text, newCursorPosition); - CharSequence errorAfter = mTextView.getError(); + mTextView.hideErrorIfUnchanged(); return success; } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index bf59753..2621dd0 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -16,13 +16,19 @@ package com.android.internal.widget; +import com.android.internal.R; +import com.android.internal.telephony.ITelephony; +import com.google.android.collect.Lists; + import android.app.admin.DevicePolicyManager; import android.content.ContentResolver; import android.content.Context; import android.os.FileObserver; +import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.storage.IMountService; import android.provider.Settings; import android.security.MessageDigest; import android.telephony.TelephonyManager; @@ -30,10 +36,6 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Button; -import com.android.internal.R; -import com.android.internal.telephony.ITelephony; -import com.google.android.collect.Lists; - import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -139,6 +141,7 @@ public class LockPatternUtils { int fileObserverMask = FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_TO | FileObserver.CREATE; sPasswordObserver = new FileObserver(dataSystemDirectory, fileObserverMask) { + @Override public void onEvent(int event, String path) { if (LOCK_PATTERN_FILE.equals(path)) { Log.d(TAG, "lock pattern file changed"); @@ -439,6 +442,27 @@ public class LockPatternUtils { return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; } + /** Update the encryption password if it is enabled **/ + private void updateEncryptionPassword(String password) { + DevicePolicyManager dpm = getDevicePolicyManager(); + if (dpm.getStorageEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) { + return; + } + + IBinder service = ServiceManager.getService("mount"); + if (service == null) { + Log.e(TAG, "Could not find the mount service to update the encryption password"); + return; + } + + IMountService mountService = IMountService.Stub.asInterface(service); + try { + mountService.changeEncryptionPassword(password); + } catch (RemoteException e) { + Log.e(TAG, "Error changing encryption password", e); + } + } + /** * Save a lock password. Does not ensure that the password is as good * as the requested mode, but will adjust the mode to be as good as the @@ -461,6 +485,9 @@ public class LockPatternUtils { raf.close(); DevicePolicyManager dpm = getDevicePolicyManager(); if (password != null) { + // Update the encryption password. + updateEncryptionPassword(password); + int computedQuality = computePasswordQuality(password); setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality)); if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { |
