diff options
Diffstat (limited to 'core/java/android')
47 files changed, 973 insertions, 509 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 64a2755..211be52 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -16,8 +16,6 @@ package android.accessibilityservice; -import com.android.internal.os.HandlerCaller; - import android.app.Service; import android.content.Intent; import android.os.IBinder; @@ -25,8 +23,11 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; +import com.android.internal.os.HandlerCaller; + /** * An accessibility service runs in the background and receives callbacks by the system * when {@link AccessibilityEvent}s are fired. Such events denote some state transition @@ -219,7 +220,7 @@ public abstract class AccessibilityService extends Service { private AccessibilityServiceInfo mInfo; - IAccessibilityServiceConnection mConnection; + private int mConnectionId; /** * Callback for {@link android.view.accessibility.AccessibilityEvent}s. @@ -264,9 +265,11 @@ public abstract class AccessibilityService extends Service { * AccessibilityManagerService. */ private void sendServiceInfo() { - if (mInfo != null && mConnection != null) { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (mInfo != null && connection != null) { try { - mConnection.setServiceInfo(mInfo); + connection.setServiceInfo(mInfo); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); } @@ -302,8 +305,9 @@ public abstract class AccessibilityService extends Service { mCaller = new HandlerCaller(context, this); } - public void setConnection(IAccessibilityServiceConnection connection) { - Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection); + public void setConnection(IAccessibilityServiceConnection connection, int connectionId) { + Message message = mCaller.obtainMessageIO(DO_SET_SET_CONNECTION, connectionId, + connection); mCaller.sendMessage(message); } @@ -330,8 +334,19 @@ public abstract class AccessibilityService extends Service { mTarget.onInterrupt(); return; case DO_SET_SET_CONNECTION : - mConnection = ((IAccessibilityServiceConnection) message.obj); - mTarget.onServiceConnected(); + final int connectionId = message.arg1; + IAccessibilityServiceConnection connection = + (IAccessibilityServiceConnection) message.obj; + if (connection != null) { + AccessibilityInteractionClient.getInstance().addConnection(connectionId, + connection); + mConnectionId = connectionId; + mTarget.onServiceConnected(); + } else { + AccessibilityInteractionClient.getInstance().removeConnection(connectionId); + mConnectionId = AccessibilityInteractionClient.NO_ID; + // TODO: Do we need a onServiceDisconnected callback? + } return; default : Log.w(LOG_TAG, "Unknown message type " + message.what); diff --git a/core/java/android/accessibilityservice/IEventListener.aidl b/core/java/android/accessibilityservice/IEventListener.aidl index 5b849f1..5536b3c 100644 --- a/core/java/android/accessibilityservice/IEventListener.aidl +++ b/core/java/android/accessibilityservice/IEventListener.aidl @@ -26,7 +26,7 @@ import android.view.accessibility.AccessibilityEvent; */ oneway interface IEventListener { - void setConnection(in IAccessibilityServiceConnection connection); + void setConnection(in IAccessibilityServiceConnection connection, int connectionId); void onAccessibilityEvent(in AccessibilityEvent event); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 303f81b..0c761fc 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -175,11 +175,11 @@ public final class ActivityThread { // These can be accessed by multiple threads; mPackages is the lock. // XXX For now we keep around information about all packages we have // seen, not removing entries from this map. - // NOTE: The activity manager in its process needs to call in to + // NOTE: The activity and window managers need to call in to // ActivityThread to do things like update resource configurations, - // which means this lock gets held while the activity manager holds its - // own lock. Thus you MUST NEVER call back into the activity manager - // or anything that depends on it while holding this lock. + // which means this lock gets held while the activity and window managers + // holds their own lock. Thus you MUST NEVER call back into the activity manager + // or window manager or anything that depends on them while holding this lock. final HashMap<String, WeakReference<LoadedApk>> mPackages = new HashMap<String, WeakReference<LoadedApk>>(); final HashMap<String, WeakReference<LoadedApk>> mResourcePackages diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 8ed7481..180a442 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -625,7 +625,7 @@ final class ApplicationPackageManager extends PackageManager { return info.activityInfo.loadIcon(this); } - throw new NameNotFoundException(intent.toURI()); + throw new NameNotFoundException(intent.toUri(0)); } @Override public Drawable getDefaultActivityIcon() { @@ -728,15 +728,22 @@ final class ApplicationPackageManager extends PackageManager { private Drawable getCachedIcon(ResourceName name) { synchronized (sSync) { - WeakReference<Drawable> wr = sIconCache.get(name); + WeakReference<Drawable.ConstantState> wr = sIconCache.get(name); if (DEBUG_ICONS) Log.v(TAG, "Get cached weak drawable ref for " + name + ": " + wr); if (wr != null) { // we have the activity - Drawable dr = wr.get(); - if (dr != null) { - if (DEBUG_ICONS) Log.v(TAG, "Get cached drawable for " - + name + ": " + dr); - return dr; + Drawable.ConstantState state = wr.get(); + if (state != null) { + if (DEBUG_ICONS) { + Log.v(TAG, "Get cached drawable state for " + name + ": " + state); + } + // Note: It's okay here to not use the newDrawable(Resources) variant + // of the API. The ConstantState comes from a drawable that was + // originally created by passing the proper app Resources instance + // which means the state should already contain the proper + // resources specific information (like density.) See + // BitmapDrawable.BitmapState for instance. + return state.newDrawable(); } // our entry has been purged sIconCache.remove(name); @@ -747,14 +754,12 @@ final class ApplicationPackageManager extends PackageManager { private void putCachedIcon(ResourceName name, Drawable dr) { synchronized (sSync) { - sIconCache.put(name, new WeakReference<Drawable>(dr)); - if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable for " - + name + ": " + dr); + sIconCache.put(name, new WeakReference<Drawable.ConstantState>(dr.getConstantState())); + if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable state for " + name + ": " + dr); } } - static final void handlePackageBroadcast(int cmd, String[] pkgList, - boolean hasPkgInfo) { + static void handlePackageBroadcast(int cmd, String[] pkgList, boolean hasPkgInfo) { boolean immediateGc = false; if (cmd == IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE) { immediateGc = true; @@ -1226,8 +1231,8 @@ final class ApplicationPackageManager extends PackageManager { private final IPackageManager mPM; private static final Object sSync = new Object(); - private static HashMap<ResourceName, WeakReference<Drawable> > sIconCache - = new HashMap<ResourceName, WeakReference<Drawable> >(); - private static HashMap<ResourceName, WeakReference<CharSequence> > sStringCache - = new HashMap<ResourceName, WeakReference<CharSequence> >(); + private static HashMap<ResourceName, WeakReference<Drawable.ConstantState>> sIconCache + = new HashMap<ResourceName, WeakReference<Drawable.ConstantState>>(); + private static HashMap<ResourceName, WeakReference<CharSequence>> sStringCache + = new HashMap<ResourceName, WeakReference<CharSequence>>(); } diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 8b70370..bf8fde0 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -91,13 +91,14 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, mCallBack = callBack; - setButton(BUTTON_POSITIVE, context.getText(R.string.date_time_set), this); - setButton(BUTTON_NEGATIVE, context.getText(R.string.cancel), (OnClickListener) null); + Context themeContext = getContext(); + setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_set), this); + setButton(BUTTON_NEGATIVE, themeContext.getText(R.string.cancel), (OnClickListener) null); setIcon(0); setTitle(R.string.date_picker_dialog_title); LayoutInflater inflater = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.date_picker_dialog, null); setView(view); mDatePicker = (DatePicker) view.findViewById(R.id.datePicker); diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java index a990ee9..353b415 100644 --- a/core/java/android/app/TimePickerDialog.java +++ b/core/java/android/app/TimePickerDialog.java @@ -92,16 +92,16 @@ public class TimePickerDialog extends AlertDialog mInitialMinute = minute; mIs24HourView = is24HourView; - setCanceledOnTouchOutside(false); setIcon(0); setTitle(R.string.time_picker_dialog_title); - setButton(BUTTON_POSITIVE, context.getText(R.string.date_time_set), this); - setButton(BUTTON_NEGATIVE, context.getText(R.string.cancel), + Context themeContext = getContext(); + setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_set), this); + setButton(BUTTON_NEGATIVE, themeContext.getText(R.string.cancel), (OnClickListener) null); LayoutInflater inflater = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.time_picker_dialog, null); setView(view); mTimePicker = (TimePicker) view.findViewById(R.id.timePicker); diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 96f3290..7300107 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -129,6 +129,10 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + /** * Initiate connection to a profile of the remote bluetooth device. * diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index d971652..5f5ba50 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1180,11 +1180,29 @@ public final class BluetoothAdapter { * @param proxy Profile proxy object */ public void closeProfileProxy(int profile, BluetoothProfile proxy) { - if (profile == BluetoothProfile.HEADSET) { - BluetoothHeadset headset = (BluetoothHeadset)proxy; - if (headset != null) { + if (proxy == null) return; + + switch (profile) { + case BluetoothProfile.HEADSET: + BluetoothHeadset headset = (BluetoothHeadset)proxy; headset.close(); - } + break; + case BluetoothProfile.A2DP: + BluetoothA2dp a2dp = (BluetoothA2dp)proxy; + a2dp.close(); + break; + case BluetoothProfile.INPUT_DEVICE: + BluetoothInputDevice iDev = (BluetoothInputDevice)proxy; + iDev.close(); + break; + case BluetoothProfile.PAN: + BluetoothPan pan = (BluetoothPan)proxy; + pan.close(); + break; + case BluetoothProfile.HEALTH: + BluetoothHealth health = (BluetoothHealth)proxy; + health.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index b1d0070..c9603bf 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -109,6 +109,8 @@ public final class BluetoothDeviceProfileState extends StateMachine { private BluetoothA2dpService mA2dpService; private BluetoothHeadset mHeadsetService; private BluetoothPbap mPbapService; + private PbapServiceListener mPbap; + private BluetoothAdapter mAdapter; private boolean mPbapServiceConnected; private boolean mAutoConnectionPending; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; @@ -249,11 +251,11 @@ public final class BluetoothDeviceProfileState extends StateMachine { mContext.registerReceiver(mBroadcastReceiver, filter); - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); // TODO(): Convert PBAP to the new Profile APIs. - PbapServiceListener p = new PbapServiceListener(); + mPbap = new PbapServiceListener(); mIncomingConnections = mService.getIncomingState(address); mIncomingRejectTimer = readTimerValue(); @@ -414,6 +416,26 @@ public final class BluetoothDeviceProfileState extends StateMachine { case TRANSITION_TO_STABLE: // ignore. break; + case SM_QUIT_CMD: + mContext.unregisterReceiver(mBroadcastReceiver); + mBroadcastReceiver = null; + mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService); + mBluetoothProfileServiceListener = null; + mOutgoingHandsfree = null; + mPbap = null; + mPbapService.close(); + mPbapService = null; + mIncomingHid = null; + mOutgoingHid = null; + mIncomingHandsfree = null; + mOutgoingHandsfree = null; + mIncomingA2dp = null; + mOutgoingA2dp = null; + mBondedDevice = null; + // There is a problem in the State Machine code + // where things are not cleaned up properly, when quit message + // is handled so return NOT_HANDLED as a workaround. + return NOT_HANDLED; default: return NOT_HANDLED; } diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 8f2b3d8..2bbf008 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -245,6 +245,7 @@ public final class BluetoothHeadset implements BluetoothProfile { mContext.unbindService(mConnection); mConnection = null; } + mServiceListener = null; } /** diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java index 9b2b8ca..f850c02 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -452,6 +452,10 @@ public final class BluetoothHealth implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index 282b70a..1a9e011 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -118,6 +118,10 @@ public final class BluetoothInputDevice implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + /** * Initiate connection to a profile of the remote bluetooth device. * diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 7490f9e..5d9d8be 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -139,6 +139,10 @@ public final class BluetoothPan implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + /** * Initiate connection to a profile of the remote bluetooth device. * @@ -299,4 +303,4 @@ public final class BluetoothPan implements BluetoothProfile { private static void log(String msg) { Log.d(TAG, msg); } -}
\ No newline at end of file +} diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index 4be077c..2683bef 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -69,7 +69,7 @@ public class BluetoothPbap { private IBluetoothPbap mService; private final Context mContext; - private final ServiceListener mServiceListener; + private ServiceListener mServiceListener; /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -138,6 +138,7 @@ public class BluetoothPbap { mContext.unbindService(mConnection); mConnection = null; } + mServiceListener = null; } /** diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 45a42e4..9948985 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2310,6 +2310,74 @@ public class Intent implements Parcelable, Cloneable { // --------------------------------------------------------------------- // --------------------------------------------------------------------- + // Application launch intent categories (see addCategory()). + + /** + * Used with {@link #ACTION_MAIN} to launch the browser application. + * The activity should be able to browse the Internet. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER"; + + /** + * Used with {@link #ACTION_MAIN} to launch the calculator application. + * The activity should be able to perform standard arithmetic operations. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR"; + + /** + * Used with {@link #ACTION_MAIN} to launch the calendar application. + * The activity should be able to view and manipulate calendar entries. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR"; + + /** + * Used with {@link #ACTION_MAIN} to launch the contacts application. + * The activity should be able to view and manipulate address book entries. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS"; + + /** + * Used with {@link #ACTION_MAIN} to launch the email application. + * The activity should be able to send and receive email. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL"; + + /** + * Used with {@link #ACTION_MAIN} to launch the gallery application. + * The activity should be able to view and manipulate image and video files + * stored on the device. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY"; + + /** + * Used with {@link #ACTION_MAIN} to launch the maps application. + * The activity should be able to show the user's current location and surroundings. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS"; + + /** + * Used with {@link #ACTION_MAIN} to launch the messaging application. + * The activity should be able to send and receive text messages. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING"; + + /** + * Used with {@link #ACTION_MAIN} to launch the music application. + * The activity should be able to play, browse, or manipulate music files stored on the device. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC"; + + // --------------------------------------------------------------------- + // --------------------------------------------------------------------- // Standard extra data keys. /** diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 4fc63ed..72431f3 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -156,4 +156,27 @@ public class ExtractEditText extends EditText { mIME.onViewClicked(false); } } + + /** + * Delete the range of text, supposedly valid + * @hide + */ + @Override + protected void deleteText_internal(int start, int end) { + // Do not call the super method. This will change the source TextView instead, which + // will update the ExtractTextView. + mIME.onExtractedDeleteText(start, end); + } + + /** + * Replaces the range of text [start, end[ by replacement text + * @hide + */ + @Override + protected void replaceText_internal(int start, int end, CharSequence text) { + // Do not call the super method. This will change the source TextView instead, which + // will update the ExtractTextView. + mIME.onExtractedReplaceText(start, end, text); + } + } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 60188ea..02839db 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1982,7 +1982,29 @@ public class InputMethodService extends AbstractInputMethodService { conn.setSelection(start, end); } } - + + /** + * @hide + */ + public void onExtractedDeleteText(int start, int end) { + InputConnection conn = getCurrentInputConnection(); + if (conn != null) { + conn.setSelection(start, start); + conn.deleteSurroundingText(0, end-start); + } + } + + /** + * @hide + */ + public void onExtractedReplaceText(int start, int end, CharSequence text) { + InputConnection conn = getCurrentInputConnection(); + if (conn != null) { + conn.setComposingRegion(start, end); + conn.commitText(text, 1); + } + } + /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides @@ -1998,7 +2020,7 @@ public class InputMethodService extends AbstractInputMethodService { setCandidatesViewShown(false); } } - + /** * This is called when the user has performed a cursor movement in the * extracted text view, when it is running in fullscreen mode. The default diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index 81defd6..0586d9e 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -128,6 +128,19 @@ public interface IBinder { int TWEET_TRANSACTION = ('_'<<24)|('T'<<16)|('W'<<8)|'T'; /** + * IBinder protocol transaction code: tell an app asynchronously that the + * caller likes it. The app is responsible for incrementing and maintaining + * its own like counter, and may display this value to the user to indicate the + * quality of the app. This is an optional command that applications do not + * need to handle, so the default implementation is to do nothing. + * + * <p>There is no response returned and nothing about the + * system will be functionally affected by it, but it will improve the + * app's self-esteem. + */ + int LIKE_TRANSACTION = ('_'<<24)|('L'<<16)|('I'<<8)|'K'; + + /** * Flag to {@link #transact}: this is a one-way call, meaning that the * caller returns immediately, without waiting for a result from the * callee. Applies only if the caller and callee are in different diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 5f111eb..4e01672 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -40,8 +40,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.text.Collator; /** * The Media provider contains meta data for all available media on both internal @@ -66,7 +64,10 @@ public final class MediaStore { /** * Activity Action: Launch a music player. * The activity should be able to play, browse, or manipulate music files stored on the device. + * + * @deprecated Use {@link android.content.Intent#CATEGORY_APP_MUSIC} instead. */ + @Deprecated @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ee3215c..15e4438 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4019,28 +4019,6 @@ public final class Settings { public static final String SETUP_PREPAID_DETECTION_REDIR_HOST = "setup_prepaid_detection_redir_host"; - /** - * Whether the screensaver is enabled. - * @hide - */ - public static final String SCREENSAVER_ENABLED = "screensaver_enabled"; - - /** - * The user's chosen screensaver component. - * - * This component will be launched by the PhoneWindowManager after a timeout when not on - * battery, or upon dock insertion (if SCREENSAVER_ACTIVATE_ON_DOCK is set to 1). - * @hide - */ - public static final String SCREENSAVER_COMPONENT = "screensaver_component"; - - /** - * Whether the screensaver should be automatically launched when the device is inserted - * into a (desk) dock. - * @hide - */ - public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock"; - /** {@hide} */ public static final String NETSTATS_ENABLED = "netstats_enabled"; /** {@hide} */ diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 28e231e..94fbbc8 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -145,7 +145,12 @@ public class BluetoothService extends IBluetooth.Stub { private final ArrayList<String> mUuidIntentTracker; private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker; - private final HashMap<Integer, Pair<Integer, IBinder>> mServiceRecordToPid; + private static class ServiceRecordClient { + int pid; + IBinder binder; + IBinder.DeathRecipient death; + } + private final HashMap<Integer, ServiceRecordClient> mServiceRecordToPid; private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState; private final BluetoothProfileState mA2dpProfileState; @@ -221,7 +226,7 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>(); mUuidIntentTracker = new ArrayList<String>(); mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); - mServiceRecordToPid = new HashMap<Integer, Pair<Integer, IBinder>>(); + mServiceRecordToPid = new HashMap<Integer, ServiceRecordClient>(); mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>(); mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP); mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP); @@ -1528,11 +1533,17 @@ public class BluetoothService extends IBluetooth.Stub { return -1; } - int pid = Binder.getCallingPid(); - mServiceRecordToPid.put(new Integer(handle), new Pair<Integer, IBinder>(pid, b)); + ServiceRecordClient client = new ServiceRecordClient(); + client.pid = Binder.getCallingPid(); + client.binder = b; + client.death = new Reaper(handle, client.pid, RFCOMM_RECORD_REAPER); + mServiceRecordToPid.put(new Integer(handle), client); try { - b.linkToDeath(new Reaper(handle, pid, RFCOMM_RECORD_REAPER), 0); - } catch (RemoteException e) {Log.e(TAG, "", e);} + b.linkToDeath(client.death, 0); + } catch (RemoteException e) { + Log.e(TAG, "", e); + client.death = null; + } return handle; } @@ -1547,10 +1558,15 @@ public class BluetoothService extends IBluetooth.Stub { } private synchronized void checkAndRemoveRecord(int handle, int pid) { - Pair<Integer, IBinder> pidPair = mServiceRecordToPid.get(handle); - if (pidPair != null && pid == pidPair.first) { + ServiceRecordClient client = mServiceRecordToPid.get(handle); + if (client != null && pid == client.pid) { if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle) + " for pid " + pid); + + if (client.death != null) { + client.binder.unlinkToDeath(client.death, 0); + } + mServiceRecordToPid.remove(handle); removeServiceRecordNative(handle); } @@ -1880,7 +1896,7 @@ public class BluetoothService extends IBluetooth.Stub { private void dumpApplicationServiceRecords(PrintWriter pw) { pw.println("\n--Application Service Records--"); for (Integer handle : mServiceRecordToPid.keySet()) { - Integer pid = mServiceRecordToPid.get(handle).first; + Integer pid = mServiceRecordToPid.get(handle).pid; pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); } } @@ -2374,16 +2390,18 @@ public class BluetoothService extends IBluetooth.Stub { } BluetoothDeviceProfileState addProfileState(String address, boolean setTrust) { - BluetoothDeviceProfileState state = mDeviceProfileState.get(address); - if (state != null) return state; - - state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust); + BluetoothDeviceProfileState state = + new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust); mDeviceProfileState.put(address, state); state.start(); return state; } void removeProfileState(String address) { + BluetoothDeviceProfileState state = mDeviceProfileState.get(address); + if (state == null) return; + + state.quit(); mDeviceProfileState.remove(address); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index a9a628a..18167b6 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -608,7 +608,7 @@ public abstract class WallpaperService extends Service { final int relayoutResult = mSession.relayout( mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, - View.VISIBLE, false, mWinFrame, mContentInsets, + View.VISIBLE, 0, mWinFrame, mContentInsets, mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface @@ -654,7 +654,7 @@ public abstract class WallpaperService extends Service { } redrawNeeded |= creating - || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0; + || (relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0; if (forceReport || creating || surfaceCreating || formatChanged || sizeChanged) { diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index fd00dce..46a78dc 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -428,7 +428,8 @@ class AudioPlaybackHandler { final AudioTrack audioTrack = params.getAudioTrack(); if (audioTrack == null) { - params.getDispatcher().dispatchOnError(); + // There was already a call to handleSynthesisDone for + // this token. return; } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 38699ea..a220615 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -490,6 +490,7 @@ public class TextToSpeech { private final Map<String, Uri> mUtterances; private final Bundle mParams = new Bundle(); private final TtsEngines mEnginesHelper; + private final String mPackageName; private volatile String mCurrentEngine = null; /** @@ -518,19 +519,36 @@ public class TextToSpeech { * @param engine Package name of the TTS engine to use. */ public TextToSpeech(Context context, OnInitListener listener, String engine) { + this(context, listener, engine, null); + } + + /** + * Used by the framework to instantiate TextToSpeech objects with a supplied + * package name, instead of using {@link android.content.Context#getPackageName()} + * + * @hide + */ + public TextToSpeech(Context context, OnInitListener listener, String engine, + String packageName) { mContext = context; mInitListener = listener; mRequestedEngine = engine; mEarcons = new HashMap<String, Uri>(); mUtterances = new HashMap<String, Uri>(); + mUtteranceProgressListener = null; mEnginesHelper = new TtsEngines(mContext); + if (packageName != null) { + mPackageName = packageName; + } else { + mPackageName = mContext.getPackageName(); + } initTts(); } private String getPackageName() { - return mContext.getPackageName(); + return mPackageName; } private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) { diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index f82a659..aee678a 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -450,7 +450,7 @@ public abstract class TextToSpeechService extends Service { @Override public void dispatchOnDone() { final String utteranceId = getUtteranceId(); - if (!TextUtils.isEmpty(utteranceId)) { + if (utteranceId != null) { mCallbacks.dispatchOnDone(getCallingApp(), utteranceId); } } @@ -458,7 +458,7 @@ public abstract class TextToSpeechService extends Service { @Override public void dispatchOnStart() { final String utteranceId = getUtteranceId(); - if (!TextUtils.isEmpty(utteranceId)) { + if (utteranceId != null) { mCallbacks.dispatchOnStart(getCallingApp(), utteranceId); } } @@ -466,7 +466,7 @@ public abstract class TextToSpeechService extends Service { @Override public void dispatchOnError() { final String utteranceId = getUtteranceId(); - if (!TextUtils.isEmpty(utteranceId)) { + if (utteranceId != null) { mCallbacks.dispatchOnError(getCallingApp(), utteranceId); } } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 68fea19..b73d900 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -119,6 +119,7 @@ class TextLine { * @param hasTabs true if the line might contain tabs or emoji * @param tabStops the tabStops. Can be null. */ + @SuppressWarnings("null") void set(TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; @@ -134,11 +135,12 @@ class TextLine { mSpanned = null; boolean hasReplacement = false; + SpanSet<ReplacementSpan> replacementSpans = null; if (text instanceof Spanned) { mSpanned = (Spanned) text; - ReplacementSpan[] spans = mSpanned.getSpans(start, limit, ReplacementSpan.class); - spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class); - hasReplacement = spans.length > 0; + replacementSpans = new SpanSet<ReplacementSpan>(mSpanned, start, limit, + ReplacementSpan.class); + hasReplacement = replacementSpans.numberOfSpans > 0; } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; @@ -156,10 +158,9 @@ class TextLine { // zero-width characters. char[] chars = mChars; for (int i = start, inext; i < limit; i = inext) { - inext = mSpanned.nextSpanTransition(i, limit, ReplacementSpan.class); - ReplacementSpan[] spans = mSpanned.getSpans(i, inext, ReplacementSpan.class); - spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class); - if (spans.length > 0) { + // replacementSpans cannot be null if hasReplacement is true + inext = replacementSpans.getNextTransition(i, limit); + if (replacementSpans.hasSpansIntersecting(i, inext)) { // transition into a span chars[i - start] = '\ufffc'; for (int j = i - start + 1, e = inext - start; j < e; ++j) { @@ -908,6 +909,15 @@ class TextLine { numberOfSpans = count; } + public boolean hasSpansIntersecting(int start, int end) { + for (int i = 0; i < numberOfSpans; i++) { + // equal test is valid since both intervals are not empty by construction + if (spanStarts[i] >= end || spanEnds[i] <= start) continue; + return true; + } + return false; + } + int getNextTransition(int start, int limit) { for (int i = 0; i < numberOfSpans; i++) { final int spanStart = spanStarts[i]; diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java index 106a801..3dfd44d 100644 --- a/core/java/android/text/method/Touch.java +++ b/core/java/android/text/method/Touch.java @@ -35,22 +35,30 @@ public class Touch { * Y position. */ public static void scrollTo(TextView widget, Layout layout, int x, int y) { - final int verticalPadding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom(); - final int top = layout.getLineForVertical(y); - final int bottom = layout.getLineForVertical(y + widget.getHeight() - verticalPadding); + final int horizontalPadding = widget.getTotalPaddingLeft() + widget.getTotalPaddingRight(); + final int availableWidth = widget.getWidth() - horizontalPadding; - int left = Integer.MAX_VALUE; - int right = 0; + final int top = layout.getLineForVertical(y); Alignment a = layout.getParagraphAlignment(top); boolean ltr = layout.getParagraphDirection(top) > 0; - for (int i = top; i <= bottom; i++) { - left = (int) Math.min(left, layout.getLineLeft(i)); - right = (int) Math.max(right, layout.getLineRight(i)); + int left, right; + if (widget.getHorizontallyScrolling()) { + final int verticalPadding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom(); + final int bottom = layout.getLineForVertical(y + widget.getHeight() - verticalPadding); + + left = Integer.MAX_VALUE; + right = 0; + + for (int i = top; i <= bottom; i++) { + left = (int) Math.min(left, layout.getLineLeft(i)); + right = (int) Math.max(right, layout.getLineRight(i)); + } + } else { + left = 0; + right = availableWidth; } - final int hoizontalPadding = widget.getTotalPaddingLeft() + widget.getTotalPaddingRight(); - final int availableWidth = widget.getWidth() - hoizontalPadding; final int actualWidth = right - left; if (actualWidth < availableWidth) { @@ -166,16 +174,24 @@ public class Touch { return false; } + /** + * @param widget The text view. + * @param buffer The text buffer. + */ public static int getInitialScrollX(TextView widget, Spannable buffer) { DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class); return ds.length > 0 ? ds[0].mScrollX : -1; } - + + /** + * @param widget The text view. + * @param buffer The text buffer. + */ public static int getInitialScrollY(TextView widget, Spannable buffer) { DragState[] ds = buffer.getSpans(0, buffer.length(), DragState.class); return ds.length > 0 ? ds[0].mScrollY : -1; } - + private static class DragState implements NoCopySpan { public float mX; public float mY; diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 7cf4579..366abd3 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -134,6 +134,7 @@ public class SparseArray<E> implements Cloneable { if (i != o) { keys[o] = keys[i]; values[o] = val; + values[i] = null; } o++; diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index d948ec2..4ca299f 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -737,8 +737,21 @@ class GLES20Canvas extends HardwareCanvas { // Shaders are ignored when drawing bitmaps int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE; final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right, - src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint); + + float left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } + + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index f77cf7e..ccb6489 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -219,6 +219,13 @@ public abstract class HardwareRenderer { abstract int getHeight(); /** + * Gets the current canvas associated with this HardwareRenderer. + * + * @return the current HardwareCanvas + */ + abstract HardwareCanvas getCanvas(); + + /** * Sets the directory to use as a persistent storage for hardware rendering * resources. * @@ -783,6 +790,11 @@ public abstract class HardwareRenderer { return mHeight; } + @Override + HardwareCanvas getCanvas() { + return mCanvas; + } + boolean canDraw() { return mGl != null && mCanvas != null; } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 282d7be..53d6e1f 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -54,9 +54,8 @@ interface IWindowSession { * @param requestedWidth The width the window wants to be. * @param requestedHeight The height the window wants to be. * @param viewVisibility Window root view's visibility. - * @param insetsPending Set to true if the client will be later giving - * internal insets; as a result, the window will not impact other window - * layouts until the insets are given. + * @param flags Request flags: {@link WindowManagerImpl#RELAYOUT_INSETS_PENDING}, + * {@link WindowManagerImpl#RELAYOUT_DEFER_SURFACE_DESTROY}. * @param outFrame Rect in which is placed the new position/size on * screen. * @param outContentInsets Rect in which is placed the offsets from @@ -80,11 +79,17 @@ interface IWindowSession { */ int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, - boolean insetsPending, out Rect outFrame, out Rect outContentInsets, + int flags, out Rect outFrame, out Rect outContentInsets, out Rect outVisibleInsets, out Configuration outConfig, out Surface outSurface); /** + * If a call to relayout() asked to have the surface destroy deferred, + * it must call this once it is okay to destroy that surface. + */ + void performDeferredDestroy(IWindow window); + + /** * Called by a client to report that it ran out of graphics memory. */ boolean outOfMemory(IWindow window); diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 6c3d387..f53e42c 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -579,8 +579,20 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: 3D Mode key. * Toggles the display between 2D and 3D mode. */ public static final int KEYCODE_3D_MODE = 206; - - private static final int LAST_KEYCODE = KEYCODE_BUTTON_16; + /** Key code constant: Contacts special function key. + * Used to launch an address book application. */ + public static final int KEYCODE_CONTACTS = 207; + /** Key code constant: Calendar special function key. + * Used to launch a calendar application. */ + public static final int KEYCODE_CALENDAR = 208; + /** Key code constant: Music special function key. + * Used to launch a music player application. */ + public static final int KEYCODE_MUSIC = 209; + /** Key code constant: Calculator special function key. + * Used to launch a calculator application. */ + public static final int KEYCODE_CALCULATOR = 210; + + private static final int LAST_KEYCODE = KEYCODE_CALCULATOR; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -589,6 +601,8 @@ public class KeyEvent extends InputEvent implements Parcelable { // external/webkit/WebKit/android/plugins/ANPKeyCodes.h // frameworks/base/core/res/res/values/attrs.xml // emulator? + // LAST_KEYCODE + // KEYCODE_SYMBOLIC_NAMES // // Also Android currently does not reserve code ranges for vendor- // specific key codes. If you have new key codes to have, you @@ -807,6 +821,10 @@ public class KeyEvent extends InputEvent implements Parcelable { names.append(KEYCODE_LANGUAGE_SWITCH, "KEYCODE_LANGUAGE_SWITCH"); names.append(KEYCODE_MANNER_MODE, "KEYCODE_MANNER_MODE"); names.append(KEYCODE_3D_MODE, "KEYCODE_3D_MODE"); + names.append(KEYCODE_CONTACTS, "KEYCODE_CONTACTS"); + names.append(KEYCODE_CALENDAR, "KEYCODE_CALENDAR"); + names.append(KEYCODE_MUSIC, "KEYCODE_MUSIC"); + names.append(KEYCODE_CALCULATOR, "KEYCODE_CALCULATOR"); }; // Symbolic names of all metakeys in bit order from least significant to most significant. diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 9a57ea0..0e68490 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -16,7 +16,6 @@ package android.view; -import android.util.DisplayMetrics; import com.android.internal.view.BaseIWindow; import android.content.Context; @@ -82,7 +81,6 @@ import java.util.concurrent.locks.ReentrantLock; public class SurfaceView extends View { static private final String TAG = "SurfaceView"; static private final boolean DEBUG = false; - static private final boolean localLOGV = DEBUG ? true : false; final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<SurfaceHolder.Callback>(); @@ -90,7 +88,8 @@ public class SurfaceView extends View { final int[] mLocation = new int[2]; final ReentrantLock mSurfaceLock = new ReentrantLock(); - final Surface mSurface = new Surface(); + Surface mSurface = new Surface(); // Current surface in use + Surface mNewSurface = new Surface(); // New surface we are switching to boolean mDrawingStopped = true; final WindowManager.LayoutParams mLayout @@ -145,8 +144,7 @@ public class SurfaceView extends View { int mRequestedFormat = PixelFormat.RGB_565; boolean mHaveFrame = false; - boolean mDestroyReportNeeded = false; - boolean mNewSurfaceNeeded = false; + boolean mSurfaceCreated = false; long mLastLockTime = 0; boolean mVisible = false; @@ -236,46 +234,6 @@ public class SurfaceView extends View { updateWindow(false, false); } - /** - * This method is not intended for general use. It was created - * temporarily to improve performance of 3D layers in Launcher - * and should be removed and fixed properly. - * - * Do not call this method. Ever. - * - * @hide - */ - protected void showSurface() { - if (mSession != null) { - updateWindow(true, false); - } - } - - /** - * This method is not intended for general use. It was created - * temporarily to improve performance of 3D layers in Launcher - * and should be removed and fixed properly. - * - * Do not call this method. Ever. - * - * @hide - */ - protected void hideSurface() { - if (mSession != null && mWindow != null) { - mSurfaceLock.lock(); - try { - DisplayMetrics metrics = getResources().getDisplayMetrics(); - mLayout.x = metrics.widthPixels * 3; - mSession.relayout(mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, VISIBLE, false, - mWinFrame, mContentInsets, mVisibleInsets, mConfiguration, mSurface); - } catch (RemoteException e) { - // Ignore - } finally { - mSurfaceLock.unlock(); - } - } - } - @Override protected void onDetachedFromWindow() { if (mGlobalListenersAdded) { @@ -444,14 +402,13 @@ public class SurfaceView extends View { final boolean creating = mWindow == null; final boolean formatChanged = mFormat != mRequestedFormat; final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; - final boolean visibleChanged = mVisible != mRequestedVisible - || mNewSurfaceNeeded; + final boolean visibleChanged = mVisible != mRequestedVisible; if (force || creating || formatChanged || sizeChanged || visibleChanged || mLeft != mLocation[0] || mTop != mLocation[1] || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) { - if (localLOGV) Log.i(TAG, "Changes: creating=" + creating + if (DEBUG) Log.i(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged + " visible=" + visibleChanged + " left=" + (mLeft != mLocation[0]) @@ -496,15 +453,11 @@ public class SurfaceView extends View { mVisible ? VISIBLE : GONE, mContentInsets); } - if (visibleChanged && (!visible || mNewSurfaceNeeded)) { - reportSurfaceDestroyed(); - } - - mNewSurfaceNeeded = false; - boolean realSizeChanged; boolean reportDrawNeeded; - + + int relayoutResult; + mSurfaceLock.lock(); try { mUpdateWindowNeeded = false; @@ -512,17 +465,21 @@ public class SurfaceView extends View { mReportDrawNeeded = false; mDrawingStopped = !visible; - final int relayoutResult = mSession.relayout( + if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface); + + relayoutResult = mSession.relayout( mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, - visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, - mVisibleInsets, mConfiguration, mSurface); - if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { + visible ? VISIBLE : GONE, + WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY, + mWinFrame, mContentInsets, + mVisibleInsets, mConfiguration, mNewSurface); + if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) { mReportDrawNeeded = true; } - - if (localLOGV) Log.i(TAG, "New surface: " + mSurface + + if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface + ", vis=" + visible + ", frame=" + mWinFrame); - + mSurfaceFrame.left = 0; mSurfaceFrame.top = 0; if (mTranslator == null) { @@ -547,28 +504,54 @@ public class SurfaceView extends View { try { redrawNeeded |= creating | reportDrawNeeded; - if (visible) { - mDestroyReportNeeded = true; + SurfaceHolder.Callback callbacks[] = null; - SurfaceHolder.Callback callbacks[]; - synchronized (mCallbacks) { - callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; - mCallbacks.toArray(callbacks); + final boolean surfaceChanged = + (relayoutResult&WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED) != 0; + if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { + mSurfaceCreated = false; + if (mSurface.isValid()) { + if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed"); + callbacks = getSurfaceCallbacks(); + for (SurfaceHolder.Callback c : callbacks) { + c.surfaceDestroyed(mSurfaceHolder); + } } + } + + Surface tmpSurface = mSurface; + mSurface = mNewSurface; + mNewSurface = tmpSurface; + mNewSurface.release(); - if (visibleChanged) { + if (visible) { + if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { + mSurfaceCreated = true; mIsCreating = true; + if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated"); + if (callbacks == null) { + callbacks = getSurfaceCallbacks(); + } for (SurfaceHolder.Callback c : callbacks) { c.surfaceCreated(mSurfaceHolder); } } if (creating || formatChanged || sizeChanged || visibleChanged || realSizeChanged) { + if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat + + " w=" + myWidth + " h=" + myHeight); + if (callbacks == null) { + callbacks = getSurfaceCallbacks(); + } for (SurfaceHolder.Callback c : callbacks) { c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); } } if (redrawNeeded) { + if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded"); + if (callbacks == null) { + callbacks = getSurfaceCallbacks(); + } for (SurfaceHolder.Callback c : callbacks) { if (c instanceof SurfaceHolder.Callback2) { ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( @@ -576,41 +559,34 @@ public class SurfaceView extends View { } } } - } else { - mSurface.release(); } } finally { mIsCreating = false; if (redrawNeeded) { + if (DEBUG) Log.i(TAG, "finishedDrawing"); mSession.finishDrawing(mWindow); } + mSession.performDeferredDestroy(mWindow); } } catch (RemoteException ex) { } - if (localLOGV) Log.v( + if (DEBUG) Log.v( TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + " w=" + mLayout.width + " h=" + mLayout.height + ", frame=" + mSurfaceFrame); } } - private void reportSurfaceDestroyed() { - if (mDestroyReportNeeded) { - mDestroyReportNeeded = false; - SurfaceHolder.Callback callbacks[]; - synchronized (mCallbacks) { - callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; - mCallbacks.toArray(callbacks); - } - for (SurfaceHolder.Callback c : callbacks) { - c.surfaceDestroyed(mSurfaceHolder); - } + private SurfaceHolder.Callback[] getSurfaceCallbacks() { + SurfaceHolder.Callback callbacks[]; + synchronized (mCallbacks) { + callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; + mCallbacks.toArray(callbacks); } - super.onDetachedFromWindow(); + return callbacks; } void handleGetNewSurface() { - mNewSurfaceNeeded = true; updateWindow(false, false); } @@ -636,7 +612,7 @@ public class SurfaceView extends View { Rect visibleInsets, boolean reportDraw, Configuration newConfig) { SurfaceView surfaceView = mSurfaceView.get(); if (surfaceView != null) { - if (localLOGV) Log.v( + if (DEBUG) Log.v( "SurfaceView", surfaceView + " got resized: w=" + w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight); surfaceView.mSurfaceLock.lock(); @@ -754,7 +730,7 @@ public class SurfaceView extends View { private final Canvas internalLockCanvas(Rect dirty) { mSurfaceLock.lock(); - if (localLOGV) Log.i(TAG, "Locking canvas... stopped=" + if (DEBUG) Log.i(TAG, "Locking canvas... stopped=" + mDrawingStopped + ", win=" + mWindow); Canvas c = null; @@ -774,7 +750,7 @@ public class SurfaceView extends View { } } - if (localLOGV) Log.i(TAG, "Returned canvas: " + c); + if (DEBUG) Log.i(TAG, "Returned canvas: " + c); if (c != null) { mLastLockTime = SystemClock.uptimeMillis(); return c; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5f70a39..7f5b5be 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1216,7 +1216,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, disposeResizeBuffer(); boolean completed = false; - HardwareCanvas canvas = null; + HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas(); + HardwareCanvas layerCanvas = null; try { if (mResizeBuffer == null) { mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( @@ -1225,12 +1226,12 @@ public final class ViewRootImpl extends Handler implements ViewParent, mResizeBuffer.getHeight() != mHeight) { mResizeBuffer.resize(mWidth, mHeight); } - canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas); - canvas.setViewport(mWidth, mHeight); - canvas.onPreDraw(null); - final int restoreCount = canvas.save(); + layerCanvas = mResizeBuffer.start(hwRendererCanvas); + layerCanvas.setViewport(mWidth, mHeight); + layerCanvas.onPreDraw(null); + final int restoreCount = layerCanvas.save(); - canvas.drawColor(0xff000000, PorterDuff.Mode.SRC); + layerCanvas.drawColor(0xff000000, PorterDuff.Mode.SRC); int yoff; final boolean scrolling = mScroller != null @@ -1242,27 +1243,27 @@ public final class ViewRootImpl extends Handler implements ViewParent, yoff = mScrollY; } - canvas.translate(0, -yoff); + layerCanvas.translate(0, -yoff); if (mTranslator != null) { - mTranslator.translateCanvas(canvas); + mTranslator.translateCanvas(layerCanvas); } - mView.draw(canvas); + mView.draw(layerCanvas); mResizeBufferStartTime = SystemClock.uptimeMillis(); mResizeBufferDuration = mView.getResources().getInteger( com.android.internal.R.integer.config_mediumAnimTime); completed = true; - canvas.restoreToCount(restoreCount); + layerCanvas.restoreToCount(restoreCount); } catch (OutOfMemoryError e) { Log.w(TAG, "Not enough memory for content change anim buffer", e); } finally { - if (canvas != null) { - canvas.onPostDraw(); + if (layerCanvas != null) { + layerCanvas.onPostDraw(); } if (mResizeBuffer != null) { - mResizeBuffer.end(mAttachInfo.mHardwareCanvas); + mResizeBuffer.end(hwRendererCanvas); if (!completed) { mResizeBuffer.destroy(); mResizeBuffer = null; @@ -1425,7 +1426,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (!mStopped) { boolean focusChangedDueToTouchMode = ensureTouchModeLocally( - (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0); + (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0); if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); @@ -1636,7 +1637,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, mLastDrawDurationNanos = System.nanoTime() - drawStartTime; } - if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0 + if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0 || mReportNextDraw) { if (LOCAL_LOGV) { Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle()); @@ -1669,7 +1670,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, } // We were supposed to report when we are done drawing. Since we canceled the // draw, remember it here. - if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { + if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) { mReportNextDraw = true; } if (fullRedrawNeeded) { @@ -3585,8 +3586,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), - viewVisibility, insetsPending, mWinFrame, - mPendingContentInsets, mPendingVisibleInsets, + viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0, + mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mPendingConfiguration, mSurface); //Log.d(TAG, "<<<<<< BACK FROM relayout"); if (restore) { @@ -3716,7 +3717,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, // animation info. try { if ((relayoutWindow(mWindowAttributes, viewVisibility, false) - & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { + & WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) { sWindowSession.finishDrawing(mWindow); } } catch (RemoteException e) { diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index b657204..48fe0df 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -278,10 +278,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } private void createSliders() { - final int silentableStreams = System.getInt(mContext.getContentResolver(), - System.MODE_RINGER_STREAMS_AFFECTED, - ((1 << AudioSystem.STREAM_NOTIFICATION) | (1 << AudioSystem.STREAM_RING))); - LayoutInflater inflater = (LayoutInflater) mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length); @@ -297,9 +293,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null); sc.group.setTag(sc); sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon); - if ((silentableStreams & (1 << sc.streamType)) != 0) { - sc.icon.setOnClickListener(this); - } sc.icon.setTag(sc); sc.icon.setContentDescription(res.getString(streamRes.descRes)); sc.iconRes = streamRes.iconRes; @@ -356,7 +349,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie && mAudioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate); } - sc.seekbarView.setEnabled(!muted); } private boolean isExpanded() { @@ -436,8 +428,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie mAudioService.getLastAudibleStreamVolume(streamType) : mAudioService.getStreamVolume(streamType); -// int message = UNKNOWN_VOLUME_TEXT; -// int additionalMessage = 0; mRingIsSilent = false; if (LOGD) { @@ -697,18 +687,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie public void onClick(View v) { if (v == mMoreButton) { expand(); - } else if (v.getTag() instanceof StreamControl) { - StreamControl sc = (StreamControl) v.getTag(); - boolean vibeInSilent = Settings.System.getInt(mContext.getContentResolver(), - System.VIBRATE_IN_SILENT, 1) == 1; - int newMode = mAudioManager.isSilentMode() - ? AudioManager.RINGER_MODE_NORMAL - : (vibeInSilent - ? AudioManager.RINGER_MODE_VIBRATE - : AudioManager.RINGER_MODE_SILENT); - mAudioManager.setRingerMode(newMode); - // Expand the dialog if it hasn't been expanded yet. - if (mShowCombinedVolumes && !isExpanded()) expand(); } resetTimeout(); } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index dfd1d55..d711337 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -63,15 +63,34 @@ public class WindowManagerImpl implements WindowManager { * The user is navigating with keys (not the touch screen), so * navigational focus should be shown. */ - public static final int RELAYOUT_IN_TOUCH_MODE = 0x1; + public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1; /** * This is the first time the window is being drawn, * so the client must call drawingFinished() when done */ - public static final int RELAYOUT_FIRST_TIME = 0x2; - + public static final int RELAYOUT_RES_FIRST_TIME = 0x2; + /** + * The window manager has changed the surface from the last call. + */ + public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4; + + /** + * Flag for relayout: the client will be later giving + * internal insets; as a result, the window will not impact other window + * layouts until the insets are given. + */ + public static final int RELAYOUT_INSETS_PENDING = 0x1; + + /** + * Flag for relayout: the client may be currently using the current surface, + * so if it is to be destroyed as a part of the relayout the destroy must + * be deferred until later. The client will call performDeferredDestroy() + * when it is okay. + */ + public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2; + public static final int ADD_FLAG_APP_VISIBLE = 0x2; - public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_IN_TOUCH_MODE; + public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE; public static final int ADD_OKAY = 0; public static final int ADD_BAD_APP_TOKEN = -1; diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 7bf0c83..91dcac8 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -16,7 +16,6 @@ package android.view.accessibility; -import android.accessibilityservice.IAccessibilityServiceConnection; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -590,24 +589,6 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** - * Sets the connection for interacting with the AccessibilityManagerService. - * - * @param connection The connection. - * - * @hide - */ - @Override - public void setConnection(IAccessibilityServiceConnection connection) { - super.setConnection(connection); - List<AccessibilityRecord> records = mRecords; - final int recordCount = records.size(); - for (int i = 0; i < recordCount; i++) { - AccessibilityRecord record = records.get(i); - record.setConnection(connection); - } - } - - /** * Sets if this instance is sealed. * * @param sealed Whether is sealed. @@ -821,23 +802,19 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @param parcel A parcel containing the state of a {@link AccessibilityEvent}. */ public void initFromParcel(Parcel parcel) { - if (parcel.readInt() == 1) { - mConnection = IAccessibilityServiceConnection.Stub.asInterface( - parcel.readStrongBinder()); - } - setSealed(parcel.readInt() == 1); + mSealed = (parcel.readInt() == 1); mEventType = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); + mConnectionId = parcel.readInt(); readAccessibilityRecordFromParcel(this, parcel); // Read the records. final int recordCount = parcel.readInt(); for (int i = 0; i < recordCount; i++) { AccessibilityRecord record = AccessibilityRecord.obtain(); - // Do this to write the connection only once. - record.setConnection(mConnection); readAccessibilityRecordFromParcel(record, parcel); + record.mConnectionId = mConnectionId; mRecords.add(record); } } @@ -875,16 +852,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * {@inheritDoc} */ public void writeToParcel(Parcel parcel, int flags) { - if (mConnection == null) { - parcel.writeInt(0); - } else { - parcel.writeInt(1); - parcel.writeStrongBinder(mConnection.asBinder()); - } parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mEventType); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); + parcel.writeInt(mConnectionId); writeAccessibilityRecordToParcel(this, parcel, flags); // Write the records. diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 25b980b..96653e5 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -21,6 +21,8 @@ import android.graphics.Rect; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; +import android.util.Log; +import android.util.SparseArray; import java.util.Collections; import java.util.List; @@ -61,6 +63,12 @@ import java.util.concurrent.atomic.AtomicInteger; public final class AccessibilityInteractionClient extends IAccessibilityInteractionConnectionCallback.Stub { + public static final int NO_ID = -1; + + private static final String LOG_TAG = "AccessibilityInteractionClient"; + + private static final boolean DEBUG = false; + private static final long TIMEOUT_INTERACTION_MILLIS = 5000; private static final Object sStaticLock = new Object(); @@ -83,6 +91,10 @@ public final class AccessibilityInteractionClient private final Rect mTempBounds = new Rect(); + // The connection cache is shared between all interrogating threads. + private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = + new SparseArray<IAccessibilityServiceConnection>(); + /** * @return The singleton of this class. */ @@ -111,28 +123,37 @@ public final class AccessibilityInteractionClient /** * Finds an {@link AccessibilityNodeInfo} by accessibility id. * - * @param connection A connection for interacting with the system. + * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. * @param accessibilityViewId A unique View accessibility id. * @return An {@link AccessibilityNodeInfo} if found, null otherwise. */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId( - IAccessibilityServiceConnection connection, int accessibilityWindowId, - int accessibilityViewId) { + public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, + int accessibilityWindowId, int accessibilityViewId) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( - accessibilityWindowId, accessibilityViewId, interactionId, this, - Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { - AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( - interactionId); - finalizeAccessibilityNodeInfo(info, connection, windowScale); - return info; + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + final int interactionId = mInteractionIdCounter.getAndIncrement(); + final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( + accessibilityWindowId, accessibilityViewId, interactionId, this, + Thread.currentThread().getId()); + // If the scale is zero the call has failed. + if (windowScale > 0) { + AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( + interactionId); + finalizeAccessibilityNodeInfo(info, connectionId, windowScale); + return info; + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } } } catch (RemoteException re) { - /* ignore */ + if (DEBUG) { + Log.w(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfoByAccessibilityId", re); + } } return null; } @@ -141,25 +162,36 @@ public final class AccessibilityInteractionClient * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed * in the currently active window and starts from the root View in the window. * - * @param connection A connection for interacting with the system. + * @param connectionId The id of a connection for interacting with the system. * @param viewId The id of the view. * @return An {@link AccessibilityNodeInfo} if found, null otherwise. */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow( - IAccessibilityServiceConnection connection, int viewId) { + public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int connectionId, + int viewId) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfoByViewIdInActiveWindow( - viewId, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { - AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( - interactionId); - finalizeAccessibilityNodeInfo(info, connection, windowScale); - return info; + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + final int interactionId = mInteractionIdCounter.getAndIncrement(); + final float windowScale = + connection.findAccessibilityNodeInfoByViewIdInActiveWindow(viewId, + interactionId, this, Thread.currentThread().getId()); + // If the scale is zero the call has failed. + if (windowScale > 0) { + AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( + interactionId); + finalizeAccessibilityNodeInfo(info, connectionId, windowScale); + return info; + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } } } catch (RemoteException re) { - /* ignore */ + if (DEBUG) { + Log.w(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfoByViewIdInActiveWindow", re); + } } return null; } @@ -169,25 +201,36 @@ public final class AccessibilityInteractionClient * insensitive containment. The search is performed in the currently * active window and starts from the root View in the window. * - * @param connection A connection for interacting with the system. + * @param connectionId The id of a connection for interacting with the system. * @param text The searched text. * @return A list of found {@link AccessibilityNodeInfo}s. */ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow( - IAccessibilityServiceConnection connection, String text) { + int connectionId, String text) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfosByViewTextInActiveWindow( - text, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { - List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( - interactionId); - finalizeAccessibilityNodeInfos(infos, connection, windowScale); - return infos; + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + final int interactionId = mInteractionIdCounter.getAndIncrement(); + final float windowScale = + connection.findAccessibilityNodeInfosByViewTextInActiveWindow(text, + interactionId, this, Thread.currentThread().getId()); + // If the scale is zero the call has failed. + if (windowScale > 0) { + List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( + interactionId); + finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); + return infos; + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } } } catch (RemoteException re) { - /* ignore */ + if (DEBUG) { + Log.w(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfosByViewTextInActiveWindow", re); + } } return null; } @@ -198,30 +241,39 @@ public final class AccessibilityInteractionClient * id is specified and starts from the View whose accessibility id is * specified. * - * @param connection A connection for interacting with the system. + * @param connectionId The id of a connection for interacting with the system. * @param text The searched text. * @param accessibilityWindowId A unique window id. * @param accessibilityViewId A unique View accessibility id from where to start the search. * Use {@link android.view.View#NO_ID} to start from the root. * @return A list of found {@link AccessibilityNodeInfo}s. */ - public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText( - IAccessibilityServiceConnection connection, String text, int accessibilityWindowId, - int accessibilityViewId) { + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(int connectionId, + String text, int accessibilityWindowId, int accessibilityViewId) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfosByViewText(text, - accessibilityWindowId, accessibilityViewId, interactionId, this, - Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { - List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( - interactionId); - finalizeAccessibilityNodeInfos(infos, connection, windowScale); - return infos; + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + final int interactionId = mInteractionIdCounter.getAndIncrement(); + final float windowScale = connection.findAccessibilityNodeInfosByViewText(text, + accessibilityWindowId, accessibilityViewId, interactionId, this, + Thread.currentThread().getId()); + // If the scale is zero the call has failed. + if (windowScale > 0) { + List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( + interactionId); + finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); + return infos; + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } } } catch (RemoteException re) { - /* ignore */ + if (DEBUG) { + Log.w(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfosByViewText", re); + } } return Collections.emptyList(); } @@ -229,24 +281,33 @@ public final class AccessibilityInteractionClient /** * Performs an accessibility action on an {@link AccessibilityNodeInfo}. * - * @param connection A connection for interacting with the system. + * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId The id of the window. * @param accessibilityViewId A unique View accessibility id. * @param action The action to perform. * @return Whether the action was performed. */ - public boolean performAccessibilityAction(IAccessibilityServiceConnection connection, - int accessibilityWindowId, int accessibilityViewId, int action) { + public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId, + int accessibilityViewId, int action) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final boolean success = connection.performAccessibilityAction( - accessibilityWindowId, accessibilityViewId, action, interactionId, this, - Thread.currentThread().getId()); - if (success) { - return getPerformAccessibilityActionResult(interactionId); + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + final int interactionId = mInteractionIdCounter.getAndIncrement(); + final boolean success = connection.performAccessibilityAction( + accessibilityWindowId, accessibilityViewId, action, interactionId, this, + Thread.currentThread().getId()); + if (success) { + return getPerformAccessibilityActionResult(interactionId); + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } } } catch (RemoteException re) { - /* ignore */ + if (DEBUG) { + Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re); + } } return false; } @@ -406,14 +467,14 @@ public final class AccessibilityInteractionClient * Finalize an {@link AccessibilityNodeInfo} before passing it to the client. * * @param info The info. - * @param connection The current connection to the system. + * @param connectionId The id of the connection to the system. * @param windowScale The source window compatibility scale. */ - private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info, - IAccessibilityServiceConnection connection, float windowScale) { + private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId, + float windowScale) { if (info != null) { applyCompatibilityScaleIfNeeded(info, windowScale); - info.setConnection(connection); + info.setConnectionId(connectionId); info.setSealed(true); } } @@ -422,16 +483,16 @@ public final class AccessibilityInteractionClient * Finalize {@link AccessibilityNodeInfo}s before passing them to the client. * * @param infos The {@link AccessibilityNodeInfo}s. - * @param connection The current connection to the system. + * @param connectionId The id of the connection to the system. * @param windowScale The source window compatibility scale. */ private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos, - IAccessibilityServiceConnection connection, float windowScale) { + int connectionId, float windowScale) { if (infos != null) { final int infosCount = infos.size(); for (int i = 0; i < infosCount; i++) { AccessibilityNodeInfo info = infos.get(i); - finalizeAccessibilityNodeInfo(info, connection, windowScale); + finalizeAccessibilityNodeInfo(info, connectionId, windowScale); } } } @@ -449,4 +510,39 @@ public final class AccessibilityInteractionClient return result; } } + + /** + * Gets a cached accessibility service connection. + * + * @param connectionId The connection id. + * @return The cached connection if such. + */ + public IAccessibilityServiceConnection getConnection(int connectionId) { + synchronized (sConnectionCache) { + return sConnectionCache.get(connectionId); + } + } + + /** + * Adds a cached accessibility service connection. + * + * @param connectionId The connection id. + * @param connection The connection. + */ + public void addConnection(int connectionId, IAccessibilityServiceConnection connection) { + synchronized (sConnectionCache) { + sConnectionCache.put(connectionId, connection); + } + } + + /** + * Removes a cached accessibility service connection. + * + * @param connectionId The connection id. + */ + public void removeConnection(int connectionId) { + synchronized (sConnectionCache) { + sConnectionCache.remove(connectionId); + } + } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index fa34ee7..9b0f44a 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -16,7 +16,6 @@ package android.view.accessibility; -import android.accessibilityservice.IAccessibilityServiceConnection; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; @@ -53,6 +52,8 @@ public class AccessibilityNodeInfo implements Parcelable { private static final boolean DEBUG = false; + private static final int UNDEFINED = -1; + // Actions. /** @@ -107,9 +108,9 @@ public class AccessibilityNodeInfo implements Parcelable { private boolean mSealed; // Data. - private int mAccessibilityViewId = View.NO_ID; - private int mAccessibilityWindowId = View.NO_ID; - private int mParentAccessibilityViewId = View.NO_ID; + private int mAccessibilityViewId = UNDEFINED; + private int mAccessibilityWindowId = UNDEFINED; + private int mParentAccessibilityViewId = UNDEFINED; private int mBooleanProperties; private final Rect mBoundsInParent = new Rect(); private final Rect mBoundsInScreen = new Rect(); @@ -122,7 +123,7 @@ public class AccessibilityNodeInfo implements Parcelable { private SparseIntArray mChildAccessibilityIds = new SparseIntArray(); private int mActions; - private IAccessibilityServiceConnection mConnection; + private int mConnectionId = UNDEFINED; /** * Hide constructor from clients. @@ -181,7 +182,7 @@ public class AccessibilityNodeInfo implements Parcelable { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mAccessibilityWindowId, childAccessibilityViewId); } @@ -253,7 +254,7 @@ public class AccessibilityNodeInfo implements Parcelable { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.performAccessibilityAction(mConnection, mAccessibilityWindowId, + return client.performAccessibilityAction(mConnectionId, mAccessibilityWindowId, mAccessibilityViewId, action); } @@ -277,7 +278,7 @@ public class AccessibilityNodeInfo implements Parcelable { return Collections.emptyList(); } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfosByViewText(mConnection, text, + return client.findAccessibilityNodeInfosByViewText(mConnectionId, text, mAccessibilityWindowId, mAccessibilityViewId); } @@ -297,7 +298,7 @@ public class AccessibilityNodeInfo implements Parcelable { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mAccessibilityWindowId, mParentAccessibilityViewId); } @@ -755,15 +756,16 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Sets the connection for interacting with the system. + * Sets the unique id of the IAccessibilityServiceConnection over which + * this instance can send requests to the system. * - * @param connection The client token. + * @param connectionId The connection id. * * @hide */ - public final void setConnection(IAccessibilityServiceConnection connection) { + public void setConnectionId(int connectionId) { enforceNotSealed(); - mConnection = connection; + mConnectionId = connectionId; } /** @@ -900,16 +902,11 @@ public class AccessibilityNodeInfo implements Parcelable { * </p> */ public void writeToParcel(Parcel parcel, int flags) { - if (mConnection == null) { - parcel.writeInt(0); - } else { - parcel.writeInt(1); - parcel.writeStrongBinder(mConnection.asBinder()); - } parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mAccessibilityViewId); parcel.writeInt(mAccessibilityWindowId); parcel.writeInt(mParentAccessibilityViewId); + parcel.writeInt(mConnectionId); SparseIntArray childIds = mChildAccessibilityIds; final int childIdsSize = childIds.size(); @@ -949,10 +946,10 @@ public class AccessibilityNodeInfo implements Parcelable { */ private void init(AccessibilityNodeInfo other) { mSealed = other.mSealed; - mConnection = other.mConnection; mAccessibilityViewId = other.mAccessibilityViewId; mParentAccessibilityViewId = other.mParentAccessibilityViewId; mAccessibilityWindowId = other.mAccessibilityWindowId; + mConnectionId = other.mConnectionId; mBoundsInParent.set(other.mBoundsInParent); mBoundsInScreen.set(other.mBoundsInScreen); mPackageName = other.mPackageName; @@ -970,14 +967,11 @@ public class AccessibilityNodeInfo implements Parcelable { * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. */ private void initFromParcel(Parcel parcel) { - if (parcel.readInt() == 1) { - mConnection = IAccessibilityServiceConnection.Stub.asInterface( - parcel.readStrongBinder()); - } mSealed = (parcel.readInt() == 1); mAccessibilityViewId = parcel.readInt(); mAccessibilityWindowId = parcel.readInt(); mParentAccessibilityViewId = parcel.readInt(); + mConnectionId = parcel.readInt(); SparseIntArray childIds = mChildAccessibilityIds; final int childrenSize = parcel.readInt(); @@ -1011,10 +1005,10 @@ public class AccessibilityNodeInfo implements Parcelable { */ private void clear() { mSealed = false; - mConnection = null; - mAccessibilityViewId = View.NO_ID; - mParentAccessibilityViewId = View.NO_ID; - mAccessibilityWindowId = View.NO_ID; + mAccessibilityViewId = UNDEFINED; + mParentAccessibilityViewId = UNDEFINED; + mAccessibilityWindowId = UNDEFINED; + mConnectionId = UNDEFINED; mChildAccessibilityIds.clear(); mBoundsInParent.set(0, 0, 0, 0); mBoundsInScreen.set(0, 0, 0, 0); @@ -1048,9 +1042,8 @@ public class AccessibilityNodeInfo implements Parcelable { } private boolean canPerformRequestOverConnection(int accessibilityViewId) { - return (mAccessibilityWindowId != View.NO_ID - && accessibilityViewId != View.NO_ID - && mConnection != null); + return (mConnectionId != UNDEFINED && mAccessibilityWindowId != UNDEFINED + && accessibilityViewId != UNDEFINED); } @Override diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index a4e0688..18d0f6f 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -16,7 +16,6 @@ package android.view.accessibility; -import android.accessibilityservice.IAccessibilityServiceConnection; import android.os.Parcelable; import android.view.View; @@ -78,8 +77,8 @@ public class AccessibilityRecord { int mAddedCount= UNDEFINED; int mRemovedCount = UNDEFINED; - int mSourceViewId = View.NO_ID; - int mSourceWindowId = View.NO_ID; + int mSourceViewId = UNDEFINED; + int mSourceWindowId = UNDEFINED; CharSequence mClassName; CharSequence mContentDescription; @@ -87,7 +86,8 @@ public class AccessibilityRecord { Parcelable mParcelableData; final List<CharSequence> mText = new ArrayList<CharSequence>(); - IAccessibilityServiceConnection mConnection; + + int mConnectionId = UNDEFINED; /* * Hide constructor. @@ -108,8 +108,8 @@ public class AccessibilityRecord { mSourceWindowId = source.getAccessibilityWindowId(); mSourceViewId = source.getAccessibilityViewId(); } else { - mSourceWindowId = View.NO_ID; - mSourceViewId = View.NO_ID; + mSourceWindowId = UNDEFINED; + mSourceViewId = UNDEFINED; } } @@ -119,33 +119,21 @@ public class AccessibilityRecord { * <strong>Note:</strong> It is a client responsibility to recycle the received info * by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. - * * </p> * @return The info of the source. */ public AccessibilityNodeInfo getSource() { enforceSealed(); - if (mSourceWindowId == View.NO_ID || mSourceViewId == View.NO_ID || mConnection == null) { + if (mConnectionId == UNDEFINED || mSourceWindowId == UNDEFINED + || mSourceViewId == UNDEFINED) { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mSourceWindowId, + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId, mSourceViewId); } /** - * Sets the connection for interacting with the AccessibilityManagerService. - * - * @param connection The connection. - * - * @hide - */ - public void setConnection(IAccessibilityServiceConnection connection) { - enforceNotSealed(); - mConnection = connection; - } - - /** * Gets the id of the window from which the event comes from. * * @return The window id. @@ -561,6 +549,19 @@ public class AccessibilityRecord { } /** + * Sets the unique id of the IAccessibilityServiceConnection over which + * this instance can send requests to the system. + * + * @param connectionId The connection id. + * + * @hide + */ + public void setConnectionId(int connectionId) { + enforceNotSealed(); + mConnectionId = connectionId; + } + + /** * Sets if this instance is sealed. * * @param sealed Whether is sealed. @@ -708,7 +709,7 @@ public class AccessibilityRecord { mText.addAll(record.mText); mSourceWindowId = record.mSourceWindowId; mSourceViewId = record.mSourceViewId; - mConnection = record.mConnection; + mConnectionId = record.mConnectionId; } /** @@ -732,8 +733,9 @@ public class AccessibilityRecord { mBeforeText = null; mParcelableData = null; mText.clear(); - mSourceViewId = View.NO_ID; - mSourceWindowId = View.NO_ID; + mSourceViewId = UNDEFINED; + mSourceWindowId = UNDEFINED; + mConnectionId = UNDEFINED; } @Override diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index c621ff6..c3794be 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -49,5 +49,5 @@ interface IAccessibilityManager { void removeAccessibilityInteractionConnection(IWindow windowToken); - IAccessibilityServiceConnection registerEventListener(IEventListener client); + void registerEventListener(IEventListener client); } diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index d6e36bb..9fa5593 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -345,6 +345,11 @@ public final class CookieManager { * a system private class. */ public synchronized void setCookie(WebAddress uri, String value) { + if (JniUtil.useChromiumHttpStack()) { + nativeSetCookie(uri.toString(), value, false); + return; + } + if (value != null && value.length() > MAX_COOKIE_LENGTH) { return; } @@ -500,6 +505,10 @@ public final class CookieManager { * is a system private class. */ public synchronized String getCookie(WebAddress uri) { + if (JniUtil.useChromiumHttpStack()) { + return nativeGetCookie(uri.toString(), false); + } + if (!mAcceptCookie || uri == null) { return null; } @@ -573,6 +582,8 @@ public final class CookieManager { * {@hide} Too late to release publically. */ public void waitForCookieOperationsToComplete() { + // Note that this function is applicable for both the java + // and native http stacks, and works correctly with either. synchronized (this) { while (pendingCookieOperations > 0) { try { diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 877c9ea..24eebd7 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -507,7 +507,7 @@ public class WebView extends AbsoluteLayout private float mLastVelY; // The id of the native layer being scrolled. - private int mScrollingLayer; + private int mCurrentScrollingLayerId; private Rect mScrollingLayerRect = new Rect(); // only trigger accelerated fling if the new velocity is at least @@ -1306,8 +1306,15 @@ public class WebView extends AbsoluteLayout if (AccessibilityManager.getInstance(mContext).isEnabled() && getSettings().getJavaScriptEnabled()) { // exposing the TTS for now ... - mTextToSpeech = new TextToSpeech(getContext(), null); - addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE); + final Context ctx = getContext(); + if (ctx != null) { + final String packageName = ctx.getPackageName(); + if (packageName != null) { + mTextToSpeech = new TextToSpeech(getContext(), null, null, + packageName + ".**webview**"); + addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE); + } + } } } @@ -1628,6 +1635,14 @@ public class WebView extends AbsoluteLayout clearTextEntry(); clearActionModes(); dismissFullScreenMode(); + cancelSelectDialog(); + } + + private void cancelSelectDialog() { + if (mListBoxDialog != null) { + mListBoxDialog.cancel(); + mListBoxDialog = null; + } } /** @@ -3279,6 +3294,8 @@ public class WebView extends AbsoluteLayout if (mNativeClass != 0) { nativeSetPauseDrawing(mNativeClass, true); } + + cancelSelectDialog(); } } @@ -3648,7 +3665,7 @@ public class WebView extends AbsoluteLayout if (x == mScrollingLayerRect.left && y == mScrollingLayerRect.top) { return; } - nativeScrollLayer(mScrollingLayer, x, y); + nativeScrollLayer(mCurrentScrollingLayerId, x, y); mScrollingLayerRect.left = x; mScrollingLayerRect.top = y; onScrollChanged(mScrollX, mScrollY, mScrollX, mScrollY); @@ -4453,6 +4470,7 @@ public class WebView extends AbsoluteLayout Rect vBox = contentToViewRect(contentBounds); Rect visibleRect = new Rect(); calcOurVisibleRect(visibleRect); + offsetByLayerScrollPosition(vBox); // If the textfield is on screen, place the WebTextView in // its new place, accounting for our new scroll/zoom values, // and adjust its textsize. @@ -4488,6 +4506,14 @@ public class WebView extends AbsoluteLayout } } + private void offsetByLayerScrollPosition(Rect box) { + if ((mCurrentScrollingLayerId != 0) + && (mCurrentScrollingLayerId == nativeFocusCandidateLayerId())) { + box.offsetTo(box.left - mScrollingLayerRect.left, + box.top - mScrollingLayerRect.top); + } + } + void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator, boolean isPictureAfterFirstLayout, boolean registerPageSwapCallback) { if (mNativeClass == 0) @@ -4587,14 +4613,15 @@ public class WebView extends AbsoluteLayout boolean UIAnimationsRunning = false; // Currently for each draw we compute the animation values; // We may in the future decide to do that independently. - if (mNativeClass != 0 && nativeEvaluateLayersAnimations(mNativeClass)) { + if (mNativeClass != 0 && !canvas.isHardwareAccelerated() + && nativeEvaluateLayersAnimations(mNativeClass)) { UIAnimationsRunning = true; // If we have unfinished (or unstarted) animations, // we ask for a repaint. We only need to do this in software // rendering (with hardware rendering we already have a different // method of requesting a repaint) - if (!canvas.isHardwareAccelerated()) - invalidate(); + mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED); + invalidate(); } // decide which adornments to draw @@ -4905,6 +4932,7 @@ public class WebView extends AbsoluteLayout // should be in content coordinates. Rect bounds = nativeFocusCandidateNodeBounds(); Rect vBox = contentToViewRect(bounds); + offsetByLayerScrollPosition(vBox); mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height()); if (!Rect.intersects(bounds, visibleRect)) { revealSelection(); @@ -5496,10 +5524,10 @@ public class WebView extends AbsoluteLayout mMaxAutoScrollX = getViewWidth(); mMinAutoScrollY = 0; mMaxAutoScrollY = getViewHeightWithTitle(); - mScrollingLayer = nativeScrollableLayer(viewToContentX(mSelectX), + mCurrentScrollingLayerId = nativeScrollableLayer(viewToContentX(mSelectX), viewToContentY(mSelectY), mScrollingLayerRect, mScrollingLayerBounds); - if (mScrollingLayer != 0) { + if (mCurrentScrollingLayerId != 0) { if (mScrollingLayerRect.left != mScrollingLayerRect.right) { mMinAutoScrollX = Math.max(mMinAutoScrollX, contentToViewX(mScrollingLayerBounds.left)); @@ -5985,9 +6013,9 @@ public class WebView extends AbsoluteLayout private void startScrollingLayer(float x, float y) { int contentX = viewToContentX((int) x + mScrollX); int contentY = viewToContentY((int) y + mScrollY); - mScrollingLayer = nativeScrollableLayer(contentX, contentY, + mCurrentScrollingLayerId = nativeScrollableLayer(contentX, contentY, mScrollingLayerRect, mScrollingLayerBounds); - if (mScrollingLayer != 0) { + if (mCurrentScrollingLayerId != 0) { mTouchMode = TOUCH_DRAG_LAYER_MODE; } } @@ -6218,7 +6246,7 @@ public class WebView extends AbsoluteLayout ted.mPointsInView[0] = new Point(x, y); ted.mMetaState = ev.getMetaState(); ted.mReprocess = mDeferTouchProcess; - ted.mNativeLayer = mScrollingLayer; + ted.mNativeLayer = mCurrentScrollingLayerId; ted.mNativeLayerRect.set(mScrollingLayerRect); ted.mSequence = mTouchEventQueue.nextTouchSequence(); mTouchEventQueue.preQueueTouchEventData(ted); @@ -6409,7 +6437,7 @@ public class WebView extends AbsoluteLayout ted.mPointsInView[0] = new Point(x, y); ted.mMetaState = ev.getMetaState(); ted.mReprocess = mDeferTouchProcess; - ted.mNativeLayer = mScrollingLayer; + ted.mNativeLayer = mCurrentScrollingLayerId; ted.mNativeLayerRect.set(mScrollingLayerRect); ted.mSequence = mTouchEventQueue.nextTouchSequence(); mTouchEventQueue.preQueueTouchEventData(ted); @@ -6718,7 +6746,7 @@ public class WebView extends AbsoluteLayout // directions. mTouchMode might be TOUCH_DRAG_MODE if we have // reached the edge of a layer but mScrollingLayer will be non-zero // if we initiated the drag on a layer. - if (mScrollingLayer != 0) { + if (mCurrentScrollingLayerId != 0) { final int contentX = viewToContentDimension(deltaX); final int contentY = viewToContentDimension(deltaY); @@ -7240,7 +7268,7 @@ public class WebView extends AbsoluteLayout + " vx=" + vx + " vy=" + vy + " maxX=" + maxX + " maxY=" + maxY + " scrollX=" + scrollX + " scrollY=" + scrollY - + " layer=" + mScrollingLayer); + + " layer=" + mCurrentScrollingLayerId); } // Allow sloppy flings without overscrolling at the edges. @@ -8349,7 +8377,7 @@ public class WebView extends AbsoluteLayout mSentAutoScrollMessage = false; break; } - if (mScrollingLayer == 0) { + if (mCurrentScrollingLayerId == 0) { pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0); } else { scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX, @@ -8779,10 +8807,13 @@ public class WebView extends AbsoluteLayout /** @hide Called by JNI when pages are swapped (only occurs with hardware * acceleration) */ - protected void pageSwapCallback() { + protected void pageSwapCallback(boolean notifyAnimationStarted) { if (inEditingMode()) { didUpdateWebTextViewDimensions(ANYWHERE); } + if (notifyAnimationStarted) { + mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED); + } } void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) { @@ -9560,6 +9591,7 @@ public class WebView extends AbsoluteLayout * See WebTextView.setType() */ private native int nativeFocusCandidateType(); + private native int nativeFocusCandidateLayerId(); private native boolean nativeFocusIsPlugin(); private native Rect nativeFocusNodeBounds(); /* package */ native int nativeFocusNodePointer(); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 2ad866b..d136004 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -519,7 +519,12 @@ public final class WebViewCore { /** * Update the layers' content */ - private native boolean nativeUpdateLayers(int baseLayer); + private native boolean nativeUpdateLayers(int nativeClass, int baseLayer); + + /** + * Notify webkit that animations have begun (on the hardware accelerated content) + */ + private native void nativeNotifyAnimationStarted(int nativeClass); private native boolean nativeFocusBoundsChanged(); @@ -1035,6 +1040,8 @@ public final class WebViewCore { static final int PLUGIN_SURFACE_READY = 195; + static final int NOTIFY_ANIMATION_STARTED = 196; + // private message ids private static final int DESTROY = 200; @@ -1594,6 +1601,10 @@ public final class WebViewCore { nativePluginSurfaceReady(); break; + case NOTIFY_ANIMATION_STARTED: + nativeNotifyAnimationStarted(mNativeClass); + break; + case ADD_PACKAGE_NAMES: if (BrowserFrame.sJavaBridge == null) { throw new IllegalStateException("No WebView " + @@ -2015,7 +2026,7 @@ public final class WebViewCore { return; } // Directly update the layers we last passed to the UI side - if (nativeUpdateLayers(mLastDrawData.mBaseLayer)) { + if (nativeUpdateLayers(mNativeClass, mLastDrawData.mBaseLayer)) { // If anything more complex than position has been touched, let's do a full draw webkitDraw(); } diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 84d00c9..8c57265 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -493,11 +493,19 @@ class ZoomManager { if (mHardwareAccelerated) { mWebView.updateScrollCoordinates(mWebView.getScrollX() - tx, mWebView.getScrollY() - ty); + // By adding webView matrix, we need to offset the canvas a bit + // to make the animation smooth. + canvas.translate(tx, ty); setZoomScale(zoomScale, false); if (mZoomScale == 0) { // We've reached the end of the zoom animation. mInHWAcceleratedZoom = false; + + // Ensure that the zoom level is pushed to WebCore. This has not + // yet occurred because we prevent it from happening while + // mInHWAcceleratedZoom is true. + mWebView.sendViewSizeZoom(false); } } else { canvas.translate(tx, ty); diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 1a1b8d0..d185370 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -165,6 +165,11 @@ public class NumberPicker extends LinearLayout { }; /** + * Constant for unspecified size. + */ + private static final int SIZE_UNSPECIFIED = -1; + + /** * Use a custom NumberPicker formatting callback to use two-digit minutes * strings like "01". Keeping a static formatter etc. is the most efficient * way to do this; it avoids creating temporary objects on every call to @@ -542,16 +547,20 @@ public class NumberPicker extends LinearLayout { getResources().getDisplayMetrics()); mSelectionDividerHeight = attributesArray.getDimensionPixelSize( R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight); - mMinHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minHeight, 0); + mMinHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minHeight, + SIZE_UNSPECIFIED); mMaxHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxHeight, - Integer.MAX_VALUE); - if (mMinHeight > mMaxHeight) { + SIZE_UNSPECIFIED); + if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED + && mMinHeight > mMaxHeight) { throw new IllegalArgumentException("minHeight > maxHeight"); } - mMinWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minWidth, 0); + mMinWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minWidth, + SIZE_UNSPECIFIED); mMaxWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxWidth, - Integer.MAX_VALUE); - if (mMinWidth > mMaxWidth) { + SIZE_UNSPECIFIED); + if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED + && mMinWidth > mMaxWidth) { throw new IllegalArgumentException("minWidth > maxWidth"); } mComputeMaxWidth = (mMaxWidth == Integer.MAX_VALUE); @@ -746,10 +755,10 @@ public class NumberPicker extends LinearLayout { final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight); super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); // Flag if we are measured with width or height less than the respective min. - final int desiredWidth = Math.max(mMinWidth, getMeasuredWidth()); - final int desiredHeight = Math.max(mMinHeight, getMeasuredHeight()); - final int widthSize = resolveSizeAndState(desiredWidth, newWidthMeasureSpec, 0); - final int heightSize = resolveSizeAndState(desiredHeight, newHeightMeasureSpec, 0); + final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(), + widthMeasureSpec); + final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(), + heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); } @@ -1243,6 +1252,7 @@ public class NumberPicker extends LinearLayout { } updateInputTextView(); initializeSelectorWheelIndices(); + tryComputeMaxWidth(); } @Override @@ -1368,6 +1378,9 @@ public class NumberPicker extends LinearLayout { * @return A measure spec greedily imposing the max size. */ private int makeMeasureSpec(int measureSpec, int maxSize) { + if (maxSize == SIZE_UNSPECIFIED) { + return measureSpec; + } final int size = MeasureSpec.getSize(measureSpec); final int mode = MeasureSpec.getMode(measureSpec); switch (mode) { @@ -1383,6 +1396,26 @@ public class NumberPicker extends LinearLayout { } /** + * Utility to reconcile a desired size and state, with constraints imposed by + * a MeasureSpec. Tries to respect the min size, unless a different size is + * imposed by the constraints. + * + * @param minSize The minimal desired size. + * @param measuredSize The currently measured size. + * @param measureSpec The current measure spec. + * @return The resolved size and state. + */ + private int resolveSizeAndStateRespectingMinSize(int minSize, int measuredSize, + int measureSpec) { + if (minSize != SIZE_UNSPECIFIED) { + final int desiredWidth = Math.max(minSize, measuredSize); + return resolveSizeAndState(desiredWidth, measureSpec, 0); + } else { + return measuredSize; + } + } + + /** * Resets the selector indices and clear the cached * string representation of these indices. */ diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 185cfa9..bd03fb9 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2581,6 +2581,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Returns whether the text is allowed to be wider than the View is. + * If false, the text will be wrapped to the width of the View. + * + * @attr ref android.R.styleable#TextView_scrollHorizontally + * @hide + */ + public boolean getHorizontallyScrolling() { + return mHorizontallyScrolling; + } + + /** * Makes the TextView at least this many lines tall. * * Setting this value overrides any other (minimum) height setting. A single line TextView will @@ -3816,7 +3827,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null && imm.isActive(this)) { imm.hideSoftInputFromWindow(getWindowToken(), 0); } - clearFocus(); return; } } @@ -3838,7 +3848,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener | KeyEvent.FLAG_EDITOR_ACTION))); } } - + /** * Set the private content type of the text, which is the * {@link EditorInfo#privateImeOptions EditorInfo.privateImeOptions} @@ -5594,8 +5604,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener outAttrs.extras = mInputContentType.extras; } else { outAttrs.imeOptions = EditorInfo.IME_NULL; - // May not be defined otherwise and needed by onEditorAction - mInputContentType = new InputContentType(); } if (focusSearch(FOCUS_DOWN) != null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; @@ -7613,6 +7621,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener list.get(i).onTextChanged(text, start, before, after); } } + + updateSpellCheckSpans(start, start + after); + + // Hide the controllers as soon as text is modified (typing, procedural...) + // We do not hide the span controllers, since they can be added when a new text is + // inserted into the text view (voice IME). + hideCursorControllers(); } /** @@ -7652,15 +7667,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendOnTextChanged(buffer, start, before, after); onTextChanged(buffer, start, before, after); - - updateSpellCheckSpans(start, start + after); - - // Hide the controllers if the amount of content changed - if (before != after) { - // We do not hide the span controllers, as they can be added when a new text is - // inserted into the text view - hideCursorControllers(); - } } /** @@ -7963,16 +7969,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onClick(View view) { if (view == mDeleteTextView) { - deleteText(); - } - } - - private void deleteText() { - Editable editable = (Editable) mText; - int start = editable.getSpanStart(mEasyEditSpan); - int end = editable.getSpanEnd(mEasyEditSpan); - if (start >= 0 && end >= 0) { - editable.delete(start, end); + Editable editable = (Editable) mText; + int start = editable.getSpanStart(mEasyEditSpan); + int end = editable.getSpanEnd(mEasyEditSpan); + if (start >= 0 && end >= 0) { + deleteText_internal(start, end); + } } } @@ -9096,7 +9098,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case ID_CUT: setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max))); - ((Editable) mText).delete(min, max); + deleteText_internal(min, max); stopSelectionActionMode(); return true; @@ -9127,7 +9129,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (Character.isSpaceChar(charBefore) && Character.isSpaceChar(charAfter)) { // Two spaces at beginning of paste: remove one final int originalLength = mText.length(); - ((Editable) mText).delete(min - 1, min); + deleteText_internal(min - 1, min); // Due to filters, there is no guarantee that exactly one character was // removed: count instead. final int delta = mText.length() - originalLength; @@ -9137,7 +9139,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener !Character.isSpaceChar(charAfter) && charAfter != '\n') { // No space at beginning of paste: add one final int originalLength = mText.length(); - ((Editable) mText).replace(min, min, " "); + replaceText_internal(min, min, " "); // Taking possible filters into account as above. final int delta = mText.length() - originalLength; min += delta; @@ -9151,11 +9153,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (Character.isSpaceChar(charBefore) && Character.isSpaceChar(charAfter)) { // Two spaces at end of paste: remove one - ((Editable) mText).delete(max, max + 1); + deleteText_internal(max, max + 1); } else if (!Character.isSpaceChar(charBefore) && charBefore != '\n' && !Character.isSpaceChar(charAfter) && charAfter != '\n') { // No space at end of paste: add one - ((Editable) mText).replace(max, max, " "); + replaceText_internal(max, max, " "); } } } @@ -9376,40 +9378,57 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mPositionY = mTempCoords[1]; } - public boolean isVisible(int positionX, int positionY) { - final TextView textView = TextView.this; + public void onScrollChanged() { + mScrollHasChanged = true; + } + } - if (mTempRect == null) mTempRect = new Rect(); - final Rect clip = mTempRect; - clip.left = getCompoundPaddingLeft(); - clip.top = getExtendedPaddingTop(); - clip.right = textView.getWidth() - getCompoundPaddingRight(); - clip.bottom = textView.getHeight() - getExtendedPaddingBottom(); - - final ViewParent parent = textView.getParent(); - if (parent == null || !parent.getChildVisibleRect(textView, clip, null)) { - return false; - } + private boolean isPositionVisible(int positionX, int positionY) { + synchronized (sTmpPosition) { + final float[] position = sTmpPosition; + position[0] = positionX; + position[1] = positionY; + View view = this; - int posX = mPositionX + positionX; - int posY = mPositionY + positionY; + while (view != null) { + if (view != this) { + // Local scroll is already taken into account in positionX/Y + position[0] -= view.getScrollX(); + position[1] -= view.getScrollY(); + } - // Offset by 1 to take into account 0.5 and int rounding around getPrimaryHorizontal. - return posX >= clip.left - 1 && posX <= clip.right + 1 && - posY >= clip.top && posY <= clip.bottom; - } + if (position[0] < 0 || position[1] < 0 || + position[0] > view.getWidth() || position[1] > view.getHeight()) { + return false; + } - public boolean isOffsetVisible(int offset) { - final int line = mLayout.getLineForOffset(offset); - final int lineBottom = mLayout.getLineBottom(line); - final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset); - return isVisible(primaryHorizontal + viewportToContentHorizontalOffset(), - lineBottom + viewportToContentVerticalOffset()); - } + if (!view.getMatrix().isIdentity()) { + view.getMatrix().mapPoints(position); + } - public void onScrollChanged() { - mScrollHasChanged = true; + position[0] += view.getLeft(); + position[1] += view.getTop(); + + final ViewParent parent = view.getParent(); + if (parent instanceof View) { + view = (View) parent; + } else { + // We've reached the ViewRoot, stop iterating + view = null; + } + } } + + // We've been able to walk up the view hierarchy and the position was never clipped + return true; + } + + private boolean isOffsetVisible(int offset) { + final int line = mLayout.getLineForOffset(offset); + final int lineBottom = mLayout.getLineBottom(line); + final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset); + return isPositionVisible(primaryHorizontal + viewportToContentHorizontalOffset(), + lineBottom + viewportToContentVerticalOffset()); } @Override @@ -9510,7 +9529,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void updatePosition(int parentPositionX, int parentPositionY, boolean parentPositionChanged, boolean parentScrolled) { // Either parentPositionChanged or parentScrolled is true, check if still visible - if (isShowing() && getPositionListener().isOffsetVisible(getTextOffset())) { + if (isShowing() && isOffsetVisible(getTextOffset())) { if (parentScrolled) computeLocalPosition(); updatePosition(parentPositionX, parentPositionY); } else { @@ -9867,9 +9886,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - TextView textView = (TextView) view; Editable editable = (Editable) mText; - SuggestionInfo suggestionInfo = mSuggestionInfos[position]; if (suggestionInfo.suggestionIndex == DELETE_TEXT) { @@ -9883,7 +9900,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Character.isSpaceChar(editable.charAt(spanUnionStart - 1)))) { spanUnionEnd = spanUnionEnd + 1; } - editable.replace(spanUnionStart, spanUnionEnd, ""); + deleteText_internal(spanUnionStart, spanUnionEnd); } hide(); return; @@ -9904,6 +9921,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(intent); // There is no way to know if the word was indeed added. Re-check. + // TODO The ExtractEditText should remove the span in the original text instead editable.removeSpan(suggestionInfo.suggestionSpan); updateSpellCheckSpans(spanStart, spanEnd); } else { @@ -9931,9 +9949,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int suggestionStart = suggestionInfo.suggestionStart; final int suggestionEnd = suggestionInfo.suggestionEnd; - final String suggestion = textView.getText().subSequence( + final String suggestion = suggestionInfo.text.subSequence( suggestionStart, suggestionEnd).toString(); - editable.replace(spanStart, spanEnd, suggestion); + replaceText_internal(spanStart, spanEnd, suggestion); // Notify source IME of the suggestion pick. Do this before swaping texts. if (!TextUtils.isEmpty( @@ -9957,6 +9975,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // way to assign them a valid range after replacement if (suggestionSpansStarts[i] <= spanStart && suggestionSpansEnds[i] >= spanEnd) { + // TODO The ExtractEditText should restore these spans in the original text editable.setSpan(suggestionSpans[i], suggestionSpansStarts[i], suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]); } @@ -10534,7 +10553,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - return getPositionListener().isVisible(mPositionX + mHotspotX, mPositionY); + return TextView.this.isPositionVisible(mPositionX + mHotspotX, mPositionY); } public abstract int getCurrentCursorOffset(); @@ -10823,7 +10842,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Handles can not cross and selection is at least one character final int selectionEnd = getSelectionEnd(); - if (offset >= selectionEnd) offset = selectionEnd - 1; + if (offset >= selectionEnd) offset = Math.max(0, selectionEnd - 1); positionAtCursorOffset(offset, false); } @@ -10865,7 +10884,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Handles can not cross and selection is at least one character final int selectionStart = getSelectionStart(); - if (offset <= selectionStart) offset = selectionStart + 1; + if (offset <= selectionStart) offset = Math.min(selectionStart + 1, mText.length()); positionAtCursorOffset(offset, false); } @@ -11237,7 +11256,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int max = extractRangeEndFromLong(minMax); Selection.setSelection((Spannable) mText, max); - ((Editable) mText).replace(min, max, content); + replaceText_internal(min, max, content); if (dragDropIntoItself) { int dragSourceStart = dragLocalState.start; @@ -11250,7 +11269,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Delete original selection - ((Editable) mText).delete(dragSourceStart, dragSourceEnd); + deleteText_internal(dragSourceStart, dragSourceEnd); // Make sure we do not leave two adjacent spaces. if ((dragSourceStart == 0 || @@ -11259,7 +11278,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) { final int pos = dragSourceStart == mText.length() ? dragSourceStart - 1 : dragSourceStart; - ((Editable) mText).delete(pos, pos + 1); + deleteText_internal(pos, pos + 1); } } } @@ -11420,6 +11439,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** + * Deletes the range of text [start, end[. + * @hide + */ + protected void deleteText_internal(int start, int end) { + ((Editable) mText).delete(start, end); + } + + /** + * Replaces the range of text [start, end[ by replacement text + * @hide + */ + protected void replaceText_internal(int start, int end, CharSequence text) { + ((Editable) mText).replace(start, end, text); + } + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; @@ -11500,6 +11535,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Path mHighlightPath; private boolean mHighlightPathBogus = true; private static final RectF sTempRect = new RectF(); + private static final float[] sTmpPosition = new float[2]; // XXX should be much larger private static final int VERY_WIDE = 1024*1024; |