diff options
Diffstat (limited to 'core/java')
78 files changed, 2726 insertions, 1793 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 191a696..9a50a41 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1206,7 +1206,8 @@ final class ApplicationPackageManager extends PackageManager { return mPM.getUsers(); } catch (RemoteException re) { ArrayList<UserInfo> users = new ArrayList<UserInfo>(); - UserInfo primary = new UserInfo(0, "Root!", UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); + UserInfo primary = new UserInfo(0, "Root!", null, + UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); users.add(primary); return users; } @@ -1240,9 +1241,9 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void updateUserName(int id, String name) { + public void setUserName(int id, String name) { try { - mPM.updateUserName(id, name); + mPM.setUserName(id, name); } catch (RemoteException re) { } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index b902550..5e21403 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1187,6 +1187,9 @@ class ContextImpl extends Context { @Override public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) { IServiceConnection sd; + if (conn == null) { + throw new IllegalArgumentException("connection is null"); + } if (mPackageInfo != null) { sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), mMainThread.getHandler(), flags); @@ -1217,6 +1220,9 @@ class ContextImpl extends Context { @Override public void unbindService(ServiceConnection conn) { + if (conn == null) { + throw new IllegalArgumentException("connection is null"); + } if (mPackageInfo != null) { IServiceConnection sd = mPackageInfo.forgetServiceDispatcher( getOuterContext(), conn); diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 39e2423..b0cc37b 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1923,7 +1923,7 @@ final class FragmentManagerImpl extends FragmentManager { if (mAdded != null) { for (int i=0; i<mAdded.size(); i++) { Fragment f = mAdded.get(i); - if (f != null && !f.mHidden && f.mUserVisibleHint) { + if (f != null && !f.mHidden) { if (f.onContextItemSelected(item)) { return true; } diff --git a/core/java/android/app/IInstrumentationWatcher.aidl b/core/java/android/app/IInstrumentationWatcher.aidl index 405a3d8..6c8c4d6 100644 --- a/core/java/android/app/IInstrumentationWatcher.aidl +++ b/core/java/android/app/IInstrumentationWatcher.aidl @@ -21,7 +21,7 @@ import android.content.ComponentName; import android.os.Bundle; /** @hide */ -oneway interface IInstrumentationWatcher +interface IInstrumentationWatcher { void instrumentationStatus(in ComponentName name, int resultCode, in Bundle results); diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index 35cc324..396f910 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -1,6 +1,5 @@ package android.app; -import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; import android.content.Context; @@ -119,7 +118,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } } - static class InputMethodCallback extends IInputMethodCallback.Stub { + static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback { WeakReference<NativeActivity> mNa; InputMethodCallback(NativeActivity na) { @@ -133,11 +132,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled); } } - - @Override - public void sessionCreated(IInputMethodSession session) { - // Stub -- not for use in the client. - } } @Override diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 08bc0ac..2c19c0c 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -41,7 +41,8 @@ public class AppWidgetHost { static final int HANDLE_UPDATE = 1; static final int HANDLE_PROVIDER_CHANGED = 2; - static final int HANDLE_VIEW_DATA_CHANGED = 3; + static final int HANDLE_PROVIDERS_CHANGED = 3; + static final int HANDLE_VIEW_DATA_CHANGED = 4; final static Object sServiceLock = new Object(); static IAppWidgetService sService; @@ -65,6 +66,11 @@ public class AppWidgetHost { msg.sendToTarget(); } + public void providersChanged() { + Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED); + msg.sendToTarget(); + } + public void viewDataChanged(int appWidgetId, int viewId) { Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED); msg.arg1 = appWidgetId; @@ -88,6 +94,10 @@ public class AppWidgetHost { onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj); break; } + case HANDLE_PROVIDERS_CHANGED: { + onProvidersChanged(); + break; + } case HANDLE_VIEW_DATA_CHANGED: { viewDataChanged(msg.arg1, msg.arg2); break; @@ -274,6 +284,14 @@ public class AppWidgetHost { } } + /** + * Called when the set of available widgets changes (ie. widget containing packages + * are added, updated or removed, or widget components are enabled or disabled.) + */ + protected void onProvidersChanged() { + // Do nothing + } + void updateAppWidgetView(int appWidgetId, RemoteViews views) { AppWidgetHostView v; synchronized (mViews) { diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index c9603bf..020f051 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -304,6 +304,25 @@ public final class BluetoothDeviceProfileState extends StateMachine { } } + @Override + protected void onQuitting() { + 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; + } + private class BondedDevice extends State { @Override public void enter() { @@ -416,26 +435,6 @@ 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; } @@ -1341,6 +1340,15 @@ public final class BluetoothDeviceProfileState extends StateMachine { return mDevice; } + /** + * Quit the state machine, only to be called by BluetoothService. + * + * @hide + */ + public void doQuit() { + this.quit(); + } + private void log(String message) { if (DBG) { Log.i(TAG, "Device:" + mDevice + " Message:" + message); diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 19d13ef..20e8515 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -242,8 +242,10 @@ public final class BluetoothSocket implements Closeable { // abortNative(), so this lock should immediately acquire mLock.writeLock().lock(); try { - mSocketState = SocketState.CLOSED; - destroyNative(); + if (mSocketState != SocketState.CLOSED) { + mSocketState = SocketState.CLOSED; + destroyNative(); + } } finally { mLock.writeLock().unlock(); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 621113d..2800930 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1379,6 +1379,7 @@ public abstract class Context { * description (action, category, etc) to match an * {@link IntentFilter} published by a service. * @param conn Receives information as the service is started and stopped. + * This must be a valid ServiceConnection object; it must not be null. * @param flags Operation options for the binding. May be 0, * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT}, @@ -1414,7 +1415,7 @@ public abstract class Context { * stop at any time. * * @param conn The connection interface previously supplied to - * bindService(). + * bindService(). This parameter must not be null. * * @see #bindService */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 76dfac4..f22dd44 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -571,7 +571,9 @@ import java.util.Set; * <li> {@link #EXTRA_INITIAL_INTENTS} * <li> {@link #EXTRA_INTENT} * <li> {@link #EXTRA_KEY_EVENT} + * <li> {@link #EXTRA_ORIGINATING_URL} * <li> {@link #EXTRA_PHONE_NUMBER} + * <li> {@link #EXTRA_REFERRER} * <li> {@link #EXTRA_REMOTE_INTENT_TOKEN} * <li> {@link #EXTRA_REPLACING} * <li> {@link #EXTRA_SHORTCUT_ICON} @@ -1286,6 +1288,22 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.NOT_UNKNOWN_SOURCE"; /** + * Used as a string extra field with {@link #ACTION_INSTALL_PACKAGE} and + * {@link #ACTION_VIEW} to indicate the URL from which the local APK in the Intent + * data field originated from. + */ + public static final String EXTRA_ORIGINATING_URL + = "android.intent.extra.ORIGINATING_URL"; + + /** + * Used as a string extra field with {@link #ACTION_INSTALL_PACKAGE} and + * {@link #ACTION_VIEW} to indicate the HTTP referrer associated with the Intent + * data field or {@link #EXTRA_ORIGINATING_URL}. + */ + public static final String EXTRA_REFERRER + = "android.intent.extra.REFERRER"; + + /** * Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} to install a * package. Tells the installer UI to skip the confirmation with the user * if the .apk is replacing an existing one. diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index e1434b3..cbabc7c 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -298,7 +298,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * activity's manifest. * * Default value is false (no support for RTL). - * @hide */ public static final int FLAG_SUPPORTS_RTL = 1<<22; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 90b4247..0d87df5 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -41,6 +41,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.pm.VerifierDeviceIdentity; import android.net.Uri; +import android.os.ParcelFileDescriptor; import android.content.IntentSender; /** @@ -359,7 +360,8 @@ interface IPackageManager { UserInfo createUser(in String name, int flags); boolean removeUser(int userId); - void updateUserName(int userId, String name); + void setUserName(int userId, String name); + ParcelFileDescriptor setUserIcon(int userId); void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer, int flags, in String installerPackageName, in Uri verificationURI, diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 6de69b0..f9f7e2d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2646,7 +2646,7 @@ public abstract class PackageManager { * @param name the new name for the user * @hide */ - public abstract void updateUserName(int id, String name); + public abstract void setUserName(int id, String name); /** * Changes the user's properties specified by the flags. diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index ba5331c..68a7257 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -43,12 +43,18 @@ public class UserInfo implements Parcelable { public int id; public String name; + public String iconPath; public int flags; public UserInfo(int id, String name, int flags) { + this(id, name, null, flags); + } + + public UserInfo(int id, String name, String iconPath, int flags) { this.id = id; this.name = name; this.flags = flags; + this.iconPath = iconPath; } public boolean isPrimary() { @@ -68,6 +74,7 @@ public class UserInfo implements Parcelable { public UserInfo(UserInfo orig) { name = orig.name; + iconPath = orig.iconPath; id = orig.id; flags = orig.flags; } @@ -84,6 +91,7 @@ public class UserInfo implements Parcelable { public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(id); dest.writeString(name); + dest.writeString(iconPath); dest.writeInt(flags); } @@ -100,6 +108,7 @@ public class UserInfo implements Parcelable { private UserInfo(Parcel source) { id = source.readInt(); name = source.readString(); + iconPath = source.readString(); flags = source.readInt(); } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 2df492e..281b843 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -469,13 +469,20 @@ public class TypedArray { * {@link android.view.ViewGroup}'s layout_width and layout_height * attributes. This is only here for performance reasons; applications * should use {@link #getDimensionPixelSize}. - * + * * @param index Index of the attribute to retrieve. * @param name Textual name of attribute for error reporting. * * @return Attribute dimension value multiplied by the appropriate * metric and truncated to integer pixels. + * + * @throws RuntimeException + * if this TypedArray does not contain an entry for <code>index</code> + * + * @deprecated Use {@link #getLayoutDimension(int, int)} instead. + * */ + @Deprecated public int getLayoutDimension(int index, String name) { index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index a6af5c2..1fc1226 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -50,9 +50,6 @@ public class DatabaseUtils { private static final String TAG = "DatabaseUtils"; private static final boolean DEBUG = false; - private static final boolean LOCAL_LOGV = false; - - private static final String[] countProjection = new String[]{"count(*)"}; /** One of the values returned by {@link #getSqlStatementType(String)}. */ public static final int STATEMENT_SELECT = 1; @@ -963,10 +960,15 @@ public class DatabaseUtils { } /** - * This class allows users to do multiple inserts into a table but - * compile the SQL insert statement only once, which may increase - * performance. + * This class allows users to do multiple inserts into a table using + * the same statement. + * <p> + * This class is not thread-safe. + * </p> + * + * @deprecated Use {@link SQLiteStatement} instead. */ + @Deprecated public static class InsertHelper { private final SQLiteDatabase mDb; private final String mTableName; @@ -983,6 +985,13 @@ public class DatabaseUtils { * table_info(...)" command that we depend on. */ public static final int TABLE_INFO_PRAGMA_COLUMNNAME_INDEX = 1; + + /** + * This field was accidentally exposed in earlier versions of the platform + * so we can hide it but we can't remove it. + * + * @hide + */ public static final int TABLE_INFO_PRAGMA_DEFAULT_INDEX = 4; /** @@ -1036,7 +1045,7 @@ public class DatabaseUtils { sb.append(sbv); mInsertSQL = sb.toString(); - if (LOCAL_LOGV) Log.v(TAG, "insert statement is " + mInsertSQL); + if (DEBUG) Log.v(TAG, "insert statement is " + mInsertSQL); } private SQLiteStatement getStatement(boolean allowReplace) throws SQLException { @@ -1069,24 +1078,35 @@ public class DatabaseUtils { * @return the row ID of the newly inserted row, or -1 if an * error occurred */ - private synchronized long insertInternal(ContentValues values, boolean allowReplace) { + private long insertInternal(ContentValues values, boolean allowReplace) { + // Start a transaction even though we don't really need one. + // This is to help maintain compatibility with applications that + // access InsertHelper from multiple threads even though they never should have. + // The original code used to lock the InsertHelper itself which was prone + // to deadlocks. Starting a transaction achieves the same mutual exclusion + // effect as grabbing a lock but without the potential for deadlocks. + mDb.beginTransactionNonExclusive(); try { SQLiteStatement stmt = getStatement(allowReplace); stmt.clearBindings(); - if (LOCAL_LOGV) Log.v(TAG, "--- inserting in table " + mTableName); + if (DEBUG) Log.v(TAG, "--- inserting in table " + mTableName); for (Map.Entry<String, Object> e: values.valueSet()) { final String key = e.getKey(); int i = getColumnIndex(key); DatabaseUtils.bindObjectToProgram(stmt, i, e.getValue()); - if (LOCAL_LOGV) { + if (DEBUG) { Log.v(TAG, "binding " + e.getValue() + " to column " + i + " (" + key + ")"); } } - return stmt.executeInsert(); + long result = stmt.executeInsert(); + mDb.setTransactionSuccessful(); + return result; } catch (SQLException e) { Log.e(TAG, "Error inserting " + values + " into table " + mTableName, e); return -1; + } finally { + mDb.endTransaction(); } } @@ -1223,7 +1243,7 @@ public class DatabaseUtils { + "execute"); } try { - if (LOCAL_LOGV) Log.v(TAG, "--- doing insert or replace in table " + mTableName); + if (DEBUG) Log.v(TAG, "--- doing insert or replace in table " + mTableName); return mPreparedStatement.executeInsert(); } catch (SQLException e) { Log.e(TAG, "Error executing InsertHelper with table " + mTableName, e); diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java index 397a12a..cc3e34f 100644 --- a/core/java/android/net/DhcpStateMachine.java +++ b/core/java/android/net/DhcpStateMachine.java @@ -163,8 +163,21 @@ public class DhcpStateMachine extends StateMachine { mRegisteredForPreDhcpNotification = true; } + /** + * Quit the DhcpStateMachine. + * + * @hide + */ + public void doQuit() { + quit(); + } + class DefaultState extends State { @Override + public void exit() { + mContext.unregisterReceiver(mBroadcastReceiver); + } + @Override public boolean processMessage(Message message) { if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); switch (message.what) { @@ -172,10 +185,6 @@ public class DhcpStateMachine extends StateMachine { Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName); mDhcpRenewWakeLock.release(); break; - case SM_QUIT_CMD: - mContext.unregisterReceiver(mBroadcastReceiver); - //let parent kill the state machine - return NOT_HANDLED; default: Log.e(TAG, "Error! unhandled message " + message); break; diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index 28bd289..5197c9e 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -230,6 +230,10 @@ public class EthernetDataTracker implements NetworkStateTracker { mNetworkInfo.setExtraInfo(mHwAddr); } } + + // if a DHCP client had previously been started for this interface, then stop it + NetworkUtils.stopDhcp(mIface); + reconnect(); break; } diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java index 8e666bc..5f68fdf 100644 --- a/core/java/android/net/arp/ArpPeer.java +++ b/core/java/android/net/arp/ArpPeer.java @@ -16,8 +16,12 @@ package android.net.arp; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; import android.os.SystemClock; import android.util.Log; + import java.io.IOException; import java.net.InetAddress; import java.net.Inet6Address; @@ -35,6 +39,8 @@ import libcore.net.RawSocket; * @hide */ public class ArpPeer { + private static final boolean DBG = false; + private static final String TAG = "ArpPeer"; private String mInterfaceName; private final InetAddress mMyAddr; private final byte[] mMyMac = new byte[6]; @@ -46,7 +52,6 @@ public class ArpPeer { private static final int ARP_LENGTH = 28; private static final int MAC_ADDR_LENGTH = 6; private static final int IPV4_LENGTH = 4; - private static final String TAG = "ArpPeer"; public ArpPeer(String interfaceName, InetAddress myAddr, String mac, InetAddress peer) throws SocketException { @@ -123,6 +128,41 @@ public class ArpPeer { return null; } + public static boolean doArp(String myMacAddress, LinkProperties linkProperties, + int timeoutMillis, int numArpPings, int minArpResponses) { + String interfaceName = linkProperties.getInterfaceName(); + InetAddress inetAddress = null; + InetAddress gateway = null; + boolean success; + + for (LinkAddress la : linkProperties.getLinkAddresses()) { + inetAddress = la.getAddress(); + break; + } + + for (RouteInfo route : linkProperties.getRoutes()) { + gateway = route.getGateway(); + break; + } + + try { + ArpPeer peer = new ArpPeer(interfaceName, inetAddress, myMacAddress, gateway); + int responses = 0; + for (int i=0; i < numArpPings; i++) { + if(peer.doArp(timeoutMillis) != null) responses++; + } + if (DBG) Log.d(TAG, "ARP test result: " + responses + "/" + numArpPings); + success = (responses >= minArpResponses); + peer.close(); + } catch (SocketException se) { + //Consider an Arp socket creation issue as a successful Arp + //test to avoid any wifi connectivity issues + Log.e(TAG, "ARP test initiation failure: " + se); + success = true; + } + return success; + } + public void close() { try { mSocket.close(); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 438c536..2aa6fb5 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -71,9 +71,9 @@ public abstract class BatteryStats implements Parcelable { public static final int FULL_WIFI_LOCK = 5; /** - * A constant indicating a scan wifi lock timer + * A constant indicating a wifi scan */ - public static final int SCAN_WIFI_LOCK = 6; + public static final int WIFI_SCAN = 6; /** * A constant indicating a wifi multicast timer @@ -136,7 +136,7 @@ public abstract class BatteryStats implements Parcelable { private static final String BATTERY_DATA = "bt"; private static final String BATTERY_DISCHARGE_DATA = "dc"; private static final String BATTERY_LEVEL_DATA = "lv"; - private static final String WIFI_LOCK_DATA = "wfl"; + private static final String WIFI_DATA = "wfl"; private static final String MISC_DATA = "m"; private static final String SCREEN_BRIGHTNESS_DATA = "br"; private static final String SIGNAL_STRENGTH_TIME_DATA = "sgt"; @@ -260,8 +260,8 @@ public abstract class BatteryStats implements Parcelable { public abstract void noteWifiStoppedLocked(); public abstract void noteFullWifiLockAcquiredLocked(); public abstract void noteFullWifiLockReleasedLocked(); - public abstract void noteScanWifiLockAcquiredLocked(); - public abstract void noteScanWifiLockReleasedLocked(); + public abstract void noteWifiScanStartedLocked(); + public abstract void noteWifiScanStoppedLocked(); public abstract void noteWifiMulticastEnabledLocked(); public abstract void noteWifiMulticastDisabledLocked(); public abstract void noteAudioTurnedOnLocked(); @@ -270,7 +270,7 @@ public abstract class BatteryStats implements Parcelable { public abstract void noteVideoTurnedOffLocked(); public abstract long getWifiRunningTime(long batteryRealtime, int which); public abstract long getFullWifiLockTime(long batteryRealtime, int which); - public abstract long getScanWifiLockTime(long batteryRealtime, int which); + public abstract long getWifiScanTime(long batteryRealtime, int which); public abstract long getWifiMulticastTime(long batteryRealtime, int which); public abstract long getAudioTurnedOnTime(long batteryRealtime, int which); @@ -453,7 +453,7 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE_PHONE_SCANNING_FLAG = 1<<27; public static final int STATE_WIFI_RUNNING_FLAG = 1<<26; public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<25; - public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<24; + public static final int STATE_WIFI_SCAN_FLAG = 1<<24; public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<23; // These are on the lower bits used for the command; if they change // we need to write another int of data. @@ -859,7 +859,7 @@ public abstract class BatteryStats implements Parcelable { new BitDescription(HistoryItem.STATE_WIFI_ON_FLAG, "wifi"), new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running"), new BitDescription(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG, "wifi_full_lock"), - new BitDescription(HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG, "wifi_scan_lock"), + new BitDescription(HistoryItem.STATE_WIFI_SCAN_FLAG, "wifi_scan"), new BitDescription(HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG, "wifi_multicast"), new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"), new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"), @@ -1326,15 +1326,15 @@ public abstract class BatteryStats implements Parcelable { long rx = u.getTcpBytesReceived(which); long tx = u.getTcpBytesSent(which); long fullWifiLockOnTime = u.getFullWifiLockTime(batteryRealtime, which); - long scanWifiLockOnTime = u.getScanWifiLockTime(batteryRealtime, which); + long wifiScanTime = u.getWifiScanTime(batteryRealtime, which); long uidWifiRunningTime = u.getWifiRunningTime(batteryRealtime, which); if (rx > 0 || tx > 0) dumpLine(pw, uid, category, NETWORK_DATA, rx, tx); - if (fullWifiLockOnTime != 0 || scanWifiLockOnTime != 0 + if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || uidWifiRunningTime != 0) { - dumpLine(pw, uid, category, WIFI_LOCK_DATA, - fullWifiLockOnTime, scanWifiLockOnTime, uidWifiRunningTime); + dumpLine(pw, uid, category, WIFI_DATA, + fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime); } if (u.hasUserActivity()) { @@ -1692,7 +1692,7 @@ public abstract class BatteryStats implements Parcelable { long tcpReceived = u.getTcpBytesReceived(which); long tcpSent = u.getTcpBytesSent(which); long fullWifiLockOnTime = u.getFullWifiLockTime(batteryRealtime, which); - long scanWifiLockOnTime = u.getScanWifiLockTime(batteryRealtime, which); + long wifiScanTime = u.getWifiScanTime(batteryRealtime, which); long uidWifiRunningTime = u.getWifiRunningTime(batteryRealtime, which); if (tcpReceived != 0 || tcpSent != 0) { @@ -1723,7 +1723,7 @@ public abstract class BatteryStats implements Parcelable { } } - if (fullWifiLockOnTime != 0 || scanWifiLockOnTime != 0 + if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || uidWifiRunningTime != 0) { sb.setLength(0); sb.append(prefix); sb.append(" Wifi Running: "); @@ -1731,12 +1731,12 @@ public abstract class BatteryStats implements Parcelable { sb.append("("); sb.append(formatRatioLocked(uidWifiRunningTime, whichBatteryRealtime)); sb.append(")\n"); sb.append(prefix); sb.append(" Full Wifi Lock: "); - formatTimeMs(sb, fullWifiLockOnTime / 1000); - sb.append("("); sb.append(formatRatioLocked(fullWifiLockOnTime, + formatTimeMs(sb, fullWifiLockOnTime / 1000); + sb.append("("); sb.append(formatRatioLocked(fullWifiLockOnTime, whichBatteryRealtime)); sb.append(")\n"); - sb.append(prefix); sb.append(" Scan Wifi Lock: "); - formatTimeMs(sb, scanWifiLockOnTime / 1000); - sb.append("("); sb.append(formatRatioLocked(scanWifiLockOnTime, + sb.append(prefix); sb.append(" Wifi Scan: "); + formatTimeMs(sb, wifiScanTime / 1000); + sb.append("("); sb.append(formatRatioLocked(wifiScanTime, whichBatteryRealtime)); sb.append(")"); pw.println(sb.toString()); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index e7ea355..b64a73a 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -218,17 +218,17 @@ interface INetworkManagementService /** * Start Wifi Access Point */ - void startAccessPoint(in WifiConfiguration wifiConfig, String wlanIface, String softapIface); + void startAccessPoint(in WifiConfiguration wifiConfig, String iface); /** * Stop Wifi Access Point */ - void stopAccessPoint(String wlanIface); + void stopAccessPoint(String iface); /** * Set Access Point config */ - void setAccessPoint(in WifiConfiguration wifiConfig, String wlanIface, String softapIface); + void setAccessPoint(in WifiConfiguration wifiConfig, String iface); /** ** DATA USAGE RELATED diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 903c8b3..318c0ae 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -19,19 +19,21 @@ package android.os; import android.util.Log; /** - * This class gives you control of the power state of the device. - * - * <p><b>Device battery life will be significantly affected by the use of this API.</b> Do not - * acquire WakeLocks unless you really need them, use the minimum levels possible, and be sure - * to release it as soon as you can. - * - * <p>You can obtain an instance of this class by calling + * This class gives you control of the power state of the device. + * + * <p> + * <b>Device battery life will be significantly affected by the use of this API.</b> + * Do not acquire {@link WakeLock}s unless you really need them, use the minimum levels + * possible, and be sure to release them as soon as possible. + * </p><p> + * You can obtain an instance of this class by calling * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. - * - * <p>The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}. This will - * create a {@link PowerManager.WakeLock} object. You can then use methods on this object to - * control the power state of the device. In practice it's quite simple: - * + * </p><p> + * The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}. + * This will create a {@link PowerManager.WakeLock} object. You can then use methods + * on the wake lock object to control the power state of the device. + * </p><p> + * In practice it's quite simple: * {@samplecode * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); * PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag"); @@ -39,11 +41,11 @@ import android.util.Log; * ..screen will stay on during this section.. * wl.release(); * } - * - * <p>The following flags are defined, with varying effects on system power. <i>These flags are - * mutually exclusive - you may only specify one of them.</i> - * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> + * </p><p> + * The following flags are defined, with varying effects on system power. + * <i>These flags are mutually exclusive - you may only specify one of them.</i> * + * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> * <thead> * <tr><th>Flag Value</th> * <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr> @@ -67,15 +69,16 @@ import android.util.Log; * </tr> * </tbody> * </table> - * - * <p>*<i>If you hold a partial wakelock, the CPU will continue to run, irrespective of any timers - * and even after the user presses the power button. In all other wakelocks, the CPU will run, but - * the user can still put the device to sleep using the power button.</i> - * - * <p>In addition, you can add two more flags, which affect behavior of the screen only. <i>These - * flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i> - * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> + * </p><p> + * *<i>If you hold a partial wake lock, the CPU will continue to run, regardless of any + * display timeouts or the state of the screen and even after the user presses the power button. + * In all other wake locks, the CPU will run, but the user can still put the device to sleep + * using the power button.</i> + * </p><p> + * In addition, you can add two more flags, which affect behavior of the screen only. + * <i>These flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i> * + * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> * <thead> * <tr><th>Flag Value</th> <th>Description</th></tr> * </thead> @@ -96,73 +99,77 @@ import android.util.Log; * </tr> * </tbody> * </table> - * + * </p><p> * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK} * permission in an {@code <uses-permission>} element of the application's manifest. + * </p> */ -public class PowerManager -{ +public final class PowerManager { private static final String TAG = "PowerManager"; - - /** + + /* * These internal values define the underlying power elements that we might * want to control individually. Eventually we'd like to expose them. */ - private static final int WAKE_BIT_CPU_STRONG = 1; - private static final int WAKE_BIT_CPU_WEAK = 2; - private static final int WAKE_BIT_SCREEN_DIM = 4; - private static final int WAKE_BIT_SCREEN_BRIGHT = 8; - private static final int WAKE_BIT_KEYBOARD_BRIGHT = 16; - private static final int WAKE_BIT_PROXIMITY_SCREEN_OFF = 32; - + private static final int WAKE_BIT_CPU_STRONG = 1 << 0; + private static final int WAKE_BIT_CPU_WEAK = 1 << 1; + private static final int WAKE_BIT_SCREEN_DIM = 1 << 2; + private static final int WAKE_BIT_SCREEN_BRIGHT = 1 << 3; + private static final int WAKE_BIT_KEYBOARD_BRIGHT = 1 << 4; + private static final int WAKE_BIT_PROXIMITY_SCREEN_OFF = 1 << 5; + private static final int LOCK_MASK = WAKE_BIT_CPU_STRONG - | WAKE_BIT_CPU_WEAK - | WAKE_BIT_SCREEN_DIM - | WAKE_BIT_SCREEN_BRIGHT - | WAKE_BIT_KEYBOARD_BRIGHT - | WAKE_BIT_PROXIMITY_SCREEN_OFF; + | WAKE_BIT_CPU_WEAK + | WAKE_BIT_SCREEN_DIM + | WAKE_BIT_SCREEN_BRIGHT + | WAKE_BIT_KEYBOARD_BRIGHT + | WAKE_BIT_PROXIMITY_SCREEN_OFF; /** - * Wake lock that ensures that the CPU is running. The screen might - * not be on. + * Wake lock level: Ensures that the CPU is running; the screen and keyboard + * backlight will be allowed to go off. */ public static final int PARTIAL_WAKE_LOCK = WAKE_BIT_CPU_STRONG; /** - * Wake lock that ensures that the screen and keyboard are on at + * Wake lock level: Ensures that the screen and keyboard backlight are on at * full brightness. * - * <p class="note">Most applications should strongly consider using - * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON}. - * This window flag will be correctly managed by the platform - * as the user moves between applications and doesn't require a special permission.</p> + * <p class="note"> + * Most applications should strongly consider using + * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead + * of managing their own wake locks. This window flag will be correctly managed + * by the platform as the user moves between applications and doesn't require + * a special permission. + * </p> */ public static final int FULL_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT - | WAKE_BIT_KEYBOARD_BRIGHT; + | WAKE_BIT_KEYBOARD_BRIGHT; /** + * Wake lock level: Ensures that the screen is on at full brightness; + * the keyboard backlight will be allowed to go off. + * * @deprecated Most applications should use * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead * of this type of wake lock, as it will be correctly managed by the platform * as the user moves between applications and doesn't require a special permission. - * - * Wake lock that ensures that the screen is on at full brightness; - * the keyboard backlight will be allowed to go off. */ @Deprecated public static final int SCREEN_BRIGHT_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT; /** - * Wake lock that ensures that the screen is on (but may be dimmed); + * Wake lock level: Ensures that the screen is on (but may be dimmed); * the keyboard backlight will be allowed to go off. */ public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM; /** - * Wake lock that turns the screen off when the proximity sensor activates. - * Since not all devices have proximity sensors, use - * {@link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if - * this wake lock mode is supported. + * Wake lock level: Turns the screen off when the proximity sensor activates. + * <p> + * Since not all devices have proximity sensors, use {@link #getSupportedWakeLockFlags()} + * to determine whether this wake lock level is supported. + * </p> * * {@hide} */ @@ -170,7 +177,7 @@ public class PowerManager /** * Flag for {@link WakeLock#release release(int)} to defer releasing a - * {@link #WAKE_BIT_PROXIMITY_SCREEN_OFF} wakelock until the proximity sensor returns + * {@link #WAKE_BIT_PROXIMITY_SCREEN_OFF} wake lock until the proximity sensor returns * a negative value. * * {@hide} @@ -178,25 +185,29 @@ public class PowerManager public static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1; /** + * Wake lock flag: Turn the screen on when the wake lock is acquired. + * <p> * Normally wake locks don't actually wake the device, they just cause - * it to remain on once it's already on. Think of the video player - * app as the normal behavior. Notifications that pop up and want + * the screen to remain on once it's already on. Think of the video player + * application as the normal behavior. Notifications that pop up and want * the device to be on are the exception; use this flag to be like them. - * <p> - * Does not work with PARTIAL_WAKE_LOCKs. + * </p><p> + * Cannot be used with {@link #PARTIAL_WAKE_LOCK}. + * </p> */ - public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000; + public static final int ACQUIRE_CAUSES_WAKEUP = 1 << 28; /** - * When this wake lock is released, poke the user activity timer + * Wake lock flag: When this wake lock is released, poke the user activity timer * so the screen stays on for a little longer. * <p> - * Will not turn the screen on if it is not already on. See {@link #ACQUIRE_CAUSES_WAKEUP} - * if you want that. - * <p> - * Does not work with PARTIAL_WAKE_LOCKs. + * Will not turn the screen on if it is not already on. + * See {@link #ACQUIRE_CAUSES_WAKEUP} if you want that. + * </p><p> + * Cannot be used with {@link #PARTIAL_WAKE_LOCK}. + * </p> */ - public static final int ON_AFTER_RELEASE = 0x20000000; + public static final int ON_AFTER_RELEASE = 1 << 29; /** * Brightness value to use when battery is low. @@ -222,78 +233,305 @@ public class PowerManager */ public static final int BRIGHTNESS_OFF = 0; + final IPowerManager mService; + final Handler mHandler; + + /** + * {@hide} + */ + public PowerManager(IPowerManager service, Handler handler) { + mService = service; + mHandler = handler; + } + + /** + * Creates a new wake lock with the specified level and flags. + * <p> + * The {@code levelAndFlags} parameter specifies a wake lock level and optional flags + * combined using the logical OR operator. + * </p><p> + * The wake lock levels are: {@link #PARTIAL_WAKE_LOCK}, + * {@link #FULL_WAKE_LOCK}, {@link #SCREEN_DIM_WAKE_LOCK} + * and {@link #SCREEN_BRIGHT_WAKE_LOCK}. Exactly one wake lock level must be + * specified as part of the {@code levelAndFlags} parameter. + * </p><p> + * The wake lock flags are: {@link #ACQUIRE_CAUSES_WAKEUP} + * and {@link #ON_AFTER_RELEASE}. Multiple flags can be combined as part of the + * {@code levelAndFlags} parameters. + * </p><p> + * Call {@link WakeLock#acquire() acquire()} on the object to acquire the + * wake lock, and {@link WakeLock#release release()} when you are done. + * </p><p> + * {@samplecode + * PowerManager pm = (PowerManager)mContext.getSystemService( + * Context.POWER_SERVICE); + * PowerManager.WakeLock wl = pm.newWakeLock( + * PowerManager.SCREEN_DIM_WAKE_LOCK + * | PowerManager.ON_AFTER_RELEASE, + * TAG); + * wl.acquire(); + * // ... do work... + * wl.release(); + * } + * </p><p> + * Although a wake lock can be created without special permissions, + * the {@link android.Manifest.permission#WAKE_LOCK} permission is + * required to actually acquire or release the wake lock that is returned. + * </p><p class="note"> + * If using this to keep the screen on, you should strongly consider using + * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead. + * This window flag will be correctly managed by the platform + * as the user moves between applications and doesn't require a special permission. + * </p> + * + * @param levelAndFlags Combination of wake lock level and flag values defining + * the requested behavior of the WakeLock. + * @param tag Your class name (or other tag) for debugging purposes. + * + * @see WakeLock#acquire() + * @see WakeLock#release() + * @see #PARTIAL_WAKE_LOCK + * @see #FULL_WAKE_LOCK + * @see #SCREEN_DIM_WAKE_LOCK + * @see #SCREEN_BRIGHT_WAKE_LOCK + * @see #ACQUIRE_CAUSES_WAKEUP + * @see #ON_AFTER_RELEASE + */ + public WakeLock newWakeLock(int levelAndFlags, String tag) { + switch (levelAndFlags & LOCK_MASK) { + case PARTIAL_WAKE_LOCK: + case SCREEN_DIM_WAKE_LOCK: + case SCREEN_BRIGHT_WAKE_LOCK: + case FULL_WAKE_LOCK: + case PROXIMITY_SCREEN_OFF_WAKE_LOCK: + break; + default: + throw new IllegalArgumentException("Must specify a wake lock level."); + } + if (tag == null) { + throw new IllegalArgumentException("The tag must not be null."); + } + return new WakeLock(levelAndFlags, tag); + } + + /** + * Notifies the power manager that user activity happened. + * <p> + * Turns the device from whatever state it's in to full on, and resets + * the auto-off timer. + * </p><p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @param when The time of the user activity, in the {@link SystemClock#uptimeMillis()} + * time base. This timestamp is used to correctly order the user activity with + * other power management functions. It should be set + * to the timestamp of the input event that caused the user activity. + * @param noChangeLights If true, does not cause the keyboard backlight to turn on + * because of this event. This is set when the power key is pressed. + * We want the device to stay on while the button is down, but we're about + * to turn off the screen so we don't want the keyboard backlight to turn on again. + * Otherwise the lights flash on and then off and it looks weird. + */ + public void userActivity(long when, boolean noChangeLights) { + try { + mService.userActivity(when, noChangeLights); + } catch (RemoteException e) { + } + } + + /** + * Forces the device to go to sleep. + * <p> + * Overrides all the wake locks that are held. This is what happen when the power + * key is pressed to turn off the screen. + * </p><p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @param time The time when the request to go to sleep was issued, in the + * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly + * order the user activity with other power management functions. It should be set + * to the timestamp of the input event that caused the request to go to sleep. + */ + public void goToSleep(long time) { + try { + mService.goToSleep(time); + } catch (RemoteException e) { + } + } + + /** + * Sets the brightness of the backlights (screen, keyboard, button). + * <p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @param brightness The brightness value from 0 to 255. + * + * {@hide} + */ + public void setBacklightBrightness(int brightness) { + try { + mService.setBacklightBrightness(brightness); + } catch (RemoteException e) { + } + } + + /** + * Returns the set of wake lock levels and flags for {@link #newWakeLock} + * that are supported on the device. + * <p> + * For example, to test to see if the {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} + * is supported: + * {@samplecode + * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + * int supportedFlags = pm.getSupportedWakeLockFlags(); + * boolean proximitySupported = ((supportedFlags & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) + * == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK); + * } + * </p> + * + * @return The set of supported WakeLock flags. + * + * {@hide} + */ + public int getSupportedWakeLockFlags() { + try { + return mService.getSupportedWakeLockFlags(); + } catch (RemoteException e) { + return 0; + } + } + + /** + * Returns whether the screen is currently on. + * <p> + * Only indicates whether the screen is on. The screen could be either bright or dim. + * </p><p> + * {@samplecode + * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + * boolean isScreenOn = pm.isScreenOn(); + * } + * </p> + * + * @return whether the screen is on (bright or dim). + */ + public boolean isScreenOn() { + try { + return mService.isScreenOn(); + } catch (RemoteException e) { + return false; + } + } + /** - * Class lets you say that you need to have the device on. + * Reboot the device. Will not return if the reboot is successful. * <p> - * Call release when you are done and don't need the lock anymore. + * Requires the {@link android.Manifest.permission#REBOOT} permission. + * </p> + * + * @param reason code to pass to the kernel (e.g., "recovery") to + * request special boot modes, or null. + */ + public void reboot(String reason) { + try { + mService.reboot(reason); + } catch (RemoteException e) { + } + } + + /** + * A wake lock is a mechanism to indicate that your application needs + * to have the device stay on. * <p> * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK} * permission in an {@code <uses-permission>} element of the application's manifest. + * Obtain a wake lock by calling {@link PowerManager#newWakeLock(int, String)}. + * </p><p> + * Call {@link #acquire()} to acquire the wake lock and force the device to stay + * on at the level that was requested when the wake lock was created. + * </p><p> + * Call {@link #release()} when you are done and don't need the lock anymore. + * It is very important to do this as soon as possible to avoid running down the + * device's battery excessively. + * </p> */ - public class WakeLock - { - static final int RELEASE_WAKE_LOCK = 1; - - Runnable mReleaser = new Runnable() { + public final class WakeLock { + private final int mFlags; + private final String mTag; + private final IBinder mToken; + private int mCount; + private boolean mRefCounted = true; + private boolean mHeld; + private WorkSource mWorkSource; + + private final Runnable mReleaser = new Runnable() { public void run() { release(); } }; - - int mFlags; - String mTag; - IBinder mToken; - int mCount = 0; - boolean mRefCounted = true; - boolean mHeld = false; - WorkSource mWorkSource; - - WakeLock(int flags, String tag) - { - switch (flags & LOCK_MASK) { - case PARTIAL_WAKE_LOCK: - case SCREEN_DIM_WAKE_LOCK: - case SCREEN_BRIGHT_WAKE_LOCK: - case FULL_WAKE_LOCK: - case PROXIMITY_SCREEN_OFF_WAKE_LOCK: - break; - default: - throw new IllegalArgumentException(); - } + WakeLock(int flags, String tag) { mFlags = flags; mTag = tag; mToken = new Binder(); } + @Override + protected void finalize() throws Throwable { + synchronized (mToken) { + if (mHeld) { + Log.wtf(TAG, "WakeLock finalized while still held: " + mTag); + try { + mService.releaseWakeLock(mToken, 0); + } catch (RemoteException e) { + } + } + } + } + /** - * Sets whether this WakeLock is ref counted. - * - * <p>Wake locks are reference counted by default. + * Sets whether this WakeLock is reference counted. + * <p> + * Wake locks are reference counted by default. If a wake lock is + * reference counted, then each call to {@link #acquire()} must be + * balanced by an equal number of calls to {@link #release()}. If a wake + * lock is not reference counted, then one call to {@link #release()} is + * sufficient to undo the effect of all previous calls to {@link #acquire()}. + * </p> * - * @param value true for ref counted, false for not ref counted. + * @param value True to make the wake lock reference counted, false to + * make the wake lock non-reference counted. */ - public void setReferenceCounted(boolean value) - { - mRefCounted = value; + public void setReferenceCounted(boolean value) { + synchronized (mToken) { + mRefCounted = value; + } } /** - * Makes sure the device is on at the level you asked when you created - * the wake lock. + * Acquires the wake lock. + * <p> + * Ensures that the device is on at the level requested when + * the wake lock was created. + * </p> */ - public void acquire() - { + public void acquire() { synchronized (mToken) { acquireLocked(); } } /** - * Makes sure the device is on at the level you asked when you created - * the wake lock. The lock will be released after the given timeout. - * - * @param timeout Release the lock after the give timeout in milliseconds. + * Acquires the wake lock with a timeout. + * <p> + * Ensures that the device is on at the level requested when + * the wake lock was created. The lock will be released after the given timeout + * expires. + * </p> + * + * @param timeout The timeout after which to release the wake lock, in milliseconds. */ public void acquire(long timeout) { synchronized (mToken) { @@ -301,37 +539,42 @@ public class PowerManager mHandler.postDelayed(mReleaser, timeout); } } - + private void acquireLocked() { if (!mRefCounted || mCount++ == 0) { mHandler.removeCallbacks(mReleaser); - try { - mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource); - } catch (RemoteException e) { + if (!mHeld) { + try { + mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource); + } catch (RemoteException e) { + } + mHeld = true; } - mHeld = true; } } /** - * Release your claim to the CPU or screen being on. - * + * Releases the wake lock. * <p> - * It may turn off shortly after you release it, or it may not if there - * are other wake locks held. + * This method releases your claim to the CPU or screen being on. + * The screen may turn off shortly after you release the wake lock, or it may + * not if there are other wake locks still held. + * </p> */ public void release() { release(0); } /** - * Release your claim to the CPU or screen being on. - * @param flags Combination of flag values to modify the release behavior. - * Currently only {@link #WAIT_FOR_PROXIMITY_NEGATIVE} is supported. - * + * Releases the wake lock with flags to modify the release behavior. * <p> - * It may turn off shortly after you release it, or it may not if there - * are other wake locks held. + * This method releases your claim to the CPU or screen being on. + * The screen may turn off shortly after you release the wake lock, or it may + * not if there are other wake locks still held. + * </p> + * + * @param flags Combination of flag values to modify the release behavior. + * Currently only {@link #WAIT_FOR_PROXIMITY_NEGATIVE} is supported. * * {@hide} */ @@ -339,11 +582,13 @@ public class PowerManager synchronized (mToken) { if (!mRefCounted || --mCount == 0) { mHandler.removeCallbacks(mReleaser); - try { - mService.releaseWakeLock(mToken, flags); - } catch (RemoteException e) { + if (mHeld) { + try { + mService.releaseWakeLock(mToken, flags); + } catch (RemoteException e) { + } + mHeld = false; } - mHeld = false; } if (mCount < 0) { throw new RuntimeException("WakeLock under-locked " + mTag); @@ -351,23 +596,40 @@ public class PowerManager } } - public boolean isHeld() - { + /** + * Returns true if the wake lock has been acquired but not yet released. + * + * @return True if the wake lock is held. + */ + public boolean isHeld() { synchronized (mToken) { return mHeld; } } + /** + * Sets the work source associated with the wake lock. + * <p> + * The work source is used to determine on behalf of which application + * the wake lock is being held. This is useful in the case where a + * service is performing work on behalf of an application so that the + * cost of that work can be accounted to the application. + * </p> + * + * @param ws The work source, or null if none. + */ public void setWorkSource(WorkSource ws) { synchronized (mToken) { if (ws != null && ws.size() == 0) { ws = null; } - boolean changed = true; + + final boolean changed; if (ws == null) { + changed = mWorkSource != null; mWorkSource = null; } else if (mWorkSource == null) { - changed = mWorkSource != null; + changed = true; mWorkSource = new WorkSource(ws); } else { changed = mWorkSource.diff(ws); @@ -375,6 +637,7 @@ public class PowerManager mWorkSource.set(ws); } } + if (changed && mHeld) { try { mService.updateWakeLockWorkSource(mToken, mWorkSource); @@ -384,6 +647,7 @@ public class PowerManager } } + @Override public String toString() { synchronized (mToken) { return "WakeLock{" @@ -391,194 +655,5 @@ public class PowerManager + " held=" + mHeld + ", refCount=" + mCount + "}"; } } - - @Override - protected void finalize() throws Throwable - { - synchronized (mToken) { - if (mHeld) { - Log.wtf(TAG, "WakeLock finalized while still held: " + mTag); - try { - mService.releaseWakeLock(mToken, 0); - } catch (RemoteException e) { - } - } - } - } - } - - /** - * Get a wake lock at the level of the flags parameter. Call - * {@link WakeLock#acquire() acquire()} on the object to acquire the - * wake lock, and {@link WakeLock#release release()} when you are done. - * - * {@samplecode - *PowerManager pm = (PowerManager)mContext.getSystemService( - * Context.POWER_SERVICE); - *PowerManager.WakeLock wl = pm.newWakeLock( - * PowerManager.SCREEN_DIM_WAKE_LOCK - * | PowerManager.ON_AFTER_RELEASE, - * TAG); - *wl.acquire(); - * // ... - *wl.release(); - * } - * - * <p class="note">If using this to keep the screen on, you should strongly consider using - * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead. - * This window flag will be correctly managed by the platform - * as the user moves between applications and doesn't require a special permission.</p> - * - * @param flags Combination of flag values defining the requested behavior of the WakeLock. - * @param tag Your class name (or other tag) for debugging purposes. - * - * @see WakeLock#acquire() - * @see WakeLock#release() - */ - public WakeLock newWakeLock(int flags, String tag) - { - if (tag == null) { - throw new NullPointerException("tag is null in PowerManager.newWakeLock"); - } - return new WakeLock(flags, tag); - } - - /** - * User activity happened. - * <p> - * Turns the device from whatever state it's in to full on, and resets - * the auto-off timer. - * - * @param when is used to order this correctly with the wake lock calls. - * This time should be in the {@link SystemClock#uptimeMillis - * SystemClock.uptimeMillis()} time base. - * @param noChangeLights should be true if you don't want the lights to - * turn on because of this event. This is set when the power - * key goes down. We want the device to stay on while the button - * is down, but we're about to turn off. Otherwise the lights - * flash on and then off and it looks weird. - */ - public void userActivity(long when, boolean noChangeLights) - { - try { - mService.userActivity(when, noChangeLights); - } catch (RemoteException e) { - } - } - - /** - * Force the device to go to sleep. Overrides all the wake locks that are - * held. - * - * @param time is used to order this correctly with the wake lock calls. - * The time should be in the {@link SystemClock#uptimeMillis - * SystemClock.uptimeMillis()} time base. - */ - public void goToSleep(long time) - { - try { - mService.goToSleep(time); - } catch (RemoteException e) { - } - } - - /** - * sets the brightness of the backlights (screen, keyboard, button). - * - * @param brightness value from 0 to 255 - * - * {@hide} - */ - public void setBacklightBrightness(int brightness) - { - try { - mService.setBacklightBrightness(brightness); - } catch (RemoteException e) { - } - } - - /** - * Returns the set of flags for {@link #newWakeLock(int, String) newWakeLock()} - * that are supported on the device. - * For example, to test to see if the {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} - * is supported: - * - * {@samplecode - * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - * int supportedFlags = pm.getSupportedWakeLockFlags(); - * boolean proximitySupported = ((supportedFlags & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) - * == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK); - * } - * - * @return the set of supported WakeLock flags. - * - * {@hide} - */ - public int getSupportedWakeLockFlags() - { - try { - return mService.getSupportedWakeLockFlags(); - } catch (RemoteException e) { - return 0; - } - } - - /** - * Returns whether the screen is currently on. The screen could be bright - * or dim. - * - * {@samplecode - * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - * boolean isScreenOn = pm.isScreenOn(); - * } - * - * @return whether the screen is on (bright or dim). - */ - public boolean isScreenOn() - { - try { - return mService.isScreenOn(); - } catch (RemoteException e) { - return false; - } } - - /** - * Reboot the device. Will not return if the reboot is - * successful. Requires the {@link android.Manifest.permission#REBOOT} - * permission. - * - * @param reason code to pass to the kernel (e.g., "recovery") to - * request special boot modes, or null. - */ - public void reboot(String reason) - { - try { - mService.reboot(reason); - } catch (RemoteException e) { - } - } - - private PowerManager() - { - } - - /** - * {@hide} - */ - public PowerManager(IPowerManager service, Handler handler) - { - mService = service; - mHandler = handler; - } - - /** - * TODO: It would be nice to be able to set the poke lock here, - * but I'm not sure what would be acceptable as an interface - - * either a PokeLock object (like WakeLock) or, possibly just a - * method call to set the poke lock. - */ - - IPowerManager mService; - Handler mHandler; } diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index d2050b7..59d0f7a 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -39,11 +39,12 @@ public final class Trace { public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7; public static final long TRACE_TAG_AUDIO = 1L << 8; public static final long TRACE_TAG_VIDEO = 1L << 9; + public static final long TRACE_TAG_CAMERA = 1L << 10; public static final int TRACE_FLAGS_START_BIT = 1; public static final String[] TRACE_TAGS = { "Graphics", "Input", "View", "WebView", "Window Manager", - "Activity Manager", "Sync Manager", "Audio", "Video", + "Activity Manager", "Sync Manager", "Audio", "Video", "Camera", }; public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a90dd8c..d9f6bf1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4279,6 +4279,9 @@ public final class Settings { /** Timeout for package verification. {@hide} */ public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout"; + /** Default response code for package verification. {@hide} */ + public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response"; + /** {@hide} */ public static final String READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT = "read_external_storage_enforced_default"; diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 97c0209..6296b11 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -2448,7 +2448,7 @@ public class BluetoothService extends IBluetooth.Stub { BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state == null) return; - state.quit(); + state.doQuit(); mDeviceProfileState.remove(address); } diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index c783e6a..2411cec 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -29,9 +29,12 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ResolveInfo; import android.database.ContentObserver; +import android.os.Binder; import android.os.Process; +import android.os.UserId; import android.provider.Settings; import android.util.Log; +import android.util.SparseArray; import java.util.List; @@ -48,7 +51,7 @@ public class SearchManagerService extends ISearchManager.Stub { private final Context mContext; // This field is initialized lazily in getSearchables(), and then never modified. - private Searchables mSearchables; + private SparseArray<Searchables> mSearchables; private ContentObserver mGlobalSearchObserver; @@ -66,14 +69,24 @@ public class SearchManagerService extends ISearchManager.Stub { mContext.getContentResolver()); } - private synchronized Searchables getSearchables() { + private synchronized Searchables getSearchables(int userId) { if (mSearchables == null) { - Log.i(TAG, "Building list of searchable activities"); new MyPackageMonitor().register(mContext, null, true); - mSearchables = new Searchables(mContext); - mSearchables.buildSearchableList(); + mSearchables = new SparseArray<Searchables>(); } - return mSearchables; + Searchables searchables = mSearchables.get(userId); + + long origId = Binder.clearCallingIdentity(); + boolean userExists = mContext.getPackageManager().getUser(userId) != null; + Binder.restoreCallingIdentity(origId); + + if (searchables == null && userExists) { + Log.i(TAG, "Building list of searchable activities for userId=" + userId); + searchables = new Searchables(mContext, userId); + searchables.buildSearchableList(); + mSearchables.append(userId, searchables); + } + return searchables; } /** @@ -87,7 +100,7 @@ public class SearchManagerService extends ISearchManager.Stub { public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mContext.unregisterReceiver(BootCompletedReceiver.this); - getSearchables(); + getSearchables(0); } }.start(); } @@ -109,8 +122,12 @@ public class SearchManagerService extends ISearchManager.Stub { } private void updateSearchables() { - // Update list of searchable activities - getSearchables().buildSearchableList(); + synchronized (SearchManagerService.this) { + // Update list of searchable activities + for (int i = 0; i < mSearchables.size(); i++) { + getSearchables(mSearchables.keyAt(i)).buildSearchableList(); + } + } // Inform all listeners that the list of searchables has been updated. Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); @@ -132,7 +149,11 @@ public class SearchManagerService extends ISearchManager.Stub { @Override public void onChange(boolean selfChange) { - getSearchables().buildSearchableList(); + synchronized (SearchManagerService.this) { + for (int i = 0; i < mSearchables.size(); i++) { + getSearchables(mSearchables.keyAt(i)).buildSearchableList(); + } + } Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); mContext.sendBroadcast(intent); @@ -156,32 +177,32 @@ public class SearchManagerService extends ISearchManager.Stub { Log.e(TAG, "getSearchableInfo(), activity == null"); return null; } - return getSearchables().getSearchableInfo(launchActivity); + return getSearchables(UserId.getCallingUserId()).getSearchableInfo(launchActivity); } /** * Returns a list of the searchable activities that can be included in global search. */ public List<SearchableInfo> getSearchablesInGlobalSearch() { - return getSearchables().getSearchablesInGlobalSearchList(); + return getSearchables(UserId.getCallingUserId()).getSearchablesInGlobalSearchList(); } public List<ResolveInfo> getGlobalSearchActivities() { - return getSearchables().getGlobalSearchActivities(); + return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivities(); } /** * Gets the name of the global search activity. */ public ComponentName getGlobalSearchActivity() { - return getSearchables().getGlobalSearchActivity(); + return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivity(); } /** * Gets the name of the web search activity. */ public ComponentName getWebSearchActivity() { - return getSearchables().getWebSearchActivity(); + return getSearchables(UserId.getCallingUserId()).getWebSearchActivity(); } } diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java index f24d52f..4e00ef9 100644 --- a/core/java/android/server/search/Searchables.java +++ b/core/java/android/server/search/Searchables.java @@ -16,6 +16,7 @@ package android.server.search; +import android.app.AppGlobals; import android.app.SearchManager; import android.app.SearchableInfo; import android.content.ComponentName; @@ -23,9 +24,11 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; +import android.os.RemoteException; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -38,6 +41,7 @@ import java.util.List; /** * This class maintains the information about all searchable activities. + * This is a hidden class. */ public class Searchables { @@ -65,12 +69,18 @@ public class Searchables { public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME = "com.google.android.providers.enhancedgooglesearch/.Launcher"; + // Cache the package manager instance + private IPackageManager mPm; + // User for which this Searchables caches information + private int mUserId; + /** * * @param context Context to use for looking up activities etc. */ - public Searchables (Context context) { + public Searchables (Context context, int userId) { mContext = context; + mUserId = userId; } /** @@ -195,16 +205,14 @@ public class Searchables { ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); - final PackageManager pm = mContext.getPackageManager(); - // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. List<ResolveInfo> searchList; final Intent intent = new Intent(Intent.ACTION_SEARCH); - searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); + searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA); List<ResolveInfo> webSearchInfoList; final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); - webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); + webSearchInfoList = queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); // analyze each one, generate a Searchables record, and record if (searchList != null || webSearchInfoList != null) { @@ -262,10 +270,8 @@ public class Searchables { // Step 1 : Query the package manager for a list // of activities that can handle the GLOBAL_SEARCH intent. Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); - PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = - pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - + queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); if (activities != null && !activities.isEmpty()) { // Step 2: Rank matching activities according to our heuristics. Collections.sort(activities, GLOBAL_SEARCH_RANKER); @@ -301,10 +307,8 @@ public class Searchables { Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); intent.setComponent(globalSearch); - PackageManager pm = mContext.getPackageManager(); - List<ResolveInfo> activities = - pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - + List<ResolveInfo> activities = queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); if (activities != null && !activities.isEmpty()) { return true; } @@ -374,9 +378,8 @@ public class Searchables { } Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.setPackage(globalSearchActivity.getPackageName()); - PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = - pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); if (activities != null && !activities.isEmpty()) { ActivityInfo ai = activities.get(0).activityInfo; @@ -387,6 +390,22 @@ public class Searchables { return null; } + private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { + if (mPm == null) { + mPm = AppGlobals.getPackageManager(); + } + List<ResolveInfo> activities = null; + try { + activities = + mPm.queryIntentActivities(intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + flags, mUserId); + } catch (RemoteException re) { + // Local call + } + return activities; + } + /** * Returns the list of searchable activities. */ diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index be2840b..bbaa173 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -22,6 +22,7 @@ import android.view.View; /** * Some objects that implement TextDirectionHeuristic. + * * @hide */ public class TextDirectionHeuristics { diff --git a/core/java/android/util/LocaleUtil.java b/core/java/android/util/LocaleUtil.java index 93f5cd3..60526e1 100644 --- a/core/java/android/util/LocaleUtil.java +++ b/core/java/android/util/LocaleUtil.java @@ -24,7 +24,6 @@ import libcore.icu.ICU; /** * Various utilities for Locales * - * @hide */ public class LocaleUtil { @@ -41,8 +40,7 @@ public class LocaleUtil { * {@link View#LAYOUT_DIRECTION_LTR} or * {@link View#LAYOUT_DIRECTION_RTL}. * - * Warning: this code does not support vertical scripts. - * @hide + * Be careful: this code will need to be updated when vertical scripts will be supported */ public static int getLayoutDirectionFromLocale(Locale locale) { if (locale != null && !locale.equals(Locale.ROOT)) { @@ -68,7 +66,8 @@ public class LocaleUtil { * {@link View#LAYOUT_DIRECTION_LTR} or * {@link View#LAYOUT_DIRECTION_RTL}. * - * Warning: this code does not support vertical scripts. + * Be careful: this code will need to be updated when vertical scripts will be supported + * * @hide */ private static int getLayoutDirectionFromFirstChar(Locale locale) { diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index 4547aa6..f031fe7 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -155,7 +155,6 @@ public class Gravity * * @see {@link View#LAYOUT_DIRECTION_LTR} * @see {@link View#LAYOUT_DIRECTION_RTL} - * @hide */ public static void apply(int gravity, int w, int h, Rect container, Rect outRect, int layoutDirection) { @@ -293,7 +292,6 @@ public class Gravity * * @see {@link View#LAYOUT_DIRECTION_LTR} * @see {@link View#LAYOUT_DIRECTION_RTL} - * @hide */ public static void apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect, int layoutDirection) { @@ -374,7 +372,6 @@ public class Gravity * * @see {@link View#LAYOUT_DIRECTION_LTR} * @see {@link View#LAYOUT_DIRECTION_RTL} - * @hide */ public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) { int absGravity = getAbsoluteGravity(gravity, layoutDirection); @@ -411,7 +408,6 @@ public class Gravity * @param gravity The gravity to convert to absolute (horizontal) values. * @param layoutDirection The layout direction. * @return gravity converted to absolute (horizontal) values. - * @hide */ public static int getAbsoluteGravity(int gravity, int layoutDirection) { int result = gravity; diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 26a5b26..f692e05 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -20,6 +20,7 @@ import android.graphics.Canvas; import android.os.Handler; import android.os.Message; import android.widget.FrameLayout; +import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -43,20 +44,20 @@ import java.util.HashMap; * * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService * (Context.LAYOUT_INFLATER_SERVICE);</pre> - * + * * <p> * To create a new LayoutInflater with an additional {@link Factory} for your * own views, you can use {@link #cloneInContext} to clone an existing * ViewFactory, and then call {@link #setFactory} on it to include your * Factory. - * + * * <p> * For performance reasons, view inflation relies heavily on pre-processing of * XML files that is done at build time. Therefore, it is not currently possible * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime; * it only works with an XmlPullParser returned from a compiled resource * (R.<em>something</em> file.) - * + * * @see Context#getSystemService */ public abstract class LayoutInflater { @@ -82,7 +83,7 @@ public abstract class LayoutInflater { private static final HashMap<String, Constructor<? extends View>> sConstructorMap = new HashMap<String, Constructor<? extends View>>(); - + private HashMap<String, Boolean> mFilterMap; private static final String TAG_MERGE = "merge"; @@ -93,36 +94,36 @@ public abstract class LayoutInflater { /** * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed * to be inflated. - * + * */ public interface Filter { /** * Hook to allow clients of the LayoutInflater to restrict the set of Views * that are allowed to be inflated. - * + * * @param clazz The class object for the View that is about to be inflated - * + * * @return True if this class is allowed to be inflated, or false otherwise */ @SuppressWarnings("unchecked") boolean onLoadClass(Class clazz); } - + public interface Factory { /** * Hook you can supply that is called when inflating from a LayoutInflater. * You can use this to customize the tag names available in your XML * layout files. - * + * * <p> * Note that it is good practice to prefix these custom names with your * package (i.e., com.coolcompany.apps) to avoid conflicts with system * names. - * + * * @param name Tag name to be inflated. * @param context The context the view is being created in. * @param attrs Inflation attributes as specified in XML file. - * + * * @return View Newly created view. Return null for the default * behavior. */ @@ -150,14 +151,14 @@ public abstract class LayoutInflater { private static class FactoryMerger implements Factory2 { private final Factory mF1, mF2; private final Factory2 mF12, mF22; - + FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) { mF1 = f1; mF2 = f2; mF12 = f12; mF22 = f22; } - + public View onCreateView(String name, Context context, AttributeSet attrs) { View v = mF1.onCreateView(name, context, attrs); if (v != null) return v; @@ -172,13 +173,13 @@ public abstract class LayoutInflater { : mF2.onCreateView(name, context, attrs); } } - + /** * Create a new LayoutInflater instance associated with a particular Context. * Applications will almost always want to use * {@link Context#getSystemService Context.getSystemService()} to retrieve * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}. - * + * * @param context The Context in which this LayoutInflater will create its * Views; most importantly, this supplies the theme from which the default * values for their attributes are retrieved. @@ -191,7 +192,7 @@ public abstract class LayoutInflater { * Create a new LayoutInflater instance that is a copy of an existing * LayoutInflater, optionally with its Context changed. For use in * implementing {@link #cloneInContext}. - * + * * @param original The original LayoutInflater to copy. * @param newContext The new Context to use. */ @@ -202,7 +203,7 @@ public abstract class LayoutInflater { mPrivateFactory = original.mPrivateFactory; mFilter = original.mFilter; } - + /** * Obtains the LayoutInflater from the given context. */ @@ -220,15 +221,15 @@ public abstract class LayoutInflater { * pointing to a different Context than the original. This is used by * {@link ContextThemeWrapper} to create a new LayoutInflater to go along * with the new Context theme. - * + * * @param newContext The new Context to associate with the new LayoutInflater. * May be the same as the original Context if desired. - * + * * @return Returns a brand spanking new LayoutInflater object associated with * the given Context. */ public abstract LayoutInflater cloneInContext(Context newContext); - + /** * Return the context we are running in, for access to resources, class * loader, etc. @@ -264,7 +265,7 @@ public abstract class LayoutInflater { * called on each element name as the xml is parsed. If the factory returns * a View, that is added to the hierarchy. If it returns null, the next * factory default {@link #onCreateView} method is called. - * + * * <p>If you have an existing * LayoutInflater and want to add your own factory to it, use * {@link #cloneInContext} to clone the existing instance and then you @@ -320,13 +321,13 @@ public abstract class LayoutInflater { public Filter getFilter() { return mFilter; } - + /** * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will * throw an {@link InflateException}. This filter will replace any previous filter set on this * LayoutInflater. - * + * * @param filter The Filter which restricts the set of Views that are allowed to be inflated. * This filter will replace any previous filter set on this LayoutInflater. */ @@ -340,7 +341,7 @@ public abstract class LayoutInflater { /** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. - * + * * @param resource ID for an XML layout resource to load (e.g., * <code>R.layout.main_page</code>) * @param root Optional view to be the parent of the generated hierarchy. @@ -360,7 +361,7 @@ public abstract class LayoutInflater { * reasons, view inflation relies heavily on pre-processing of XML files * that is done at build time. Therefore, it is not currently possible to * use LayoutInflater with an XmlPullParser over a plain XML file at runtime. - * + * * @param parser XML dom node containing the description of the view * hierarchy. * @param root Optional view to be the parent of the generated hierarchy. @@ -375,7 +376,7 @@ public abstract class LayoutInflater { /** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. - * + * * @param resource ID for an XML layout resource to load (e.g., * <code>R.layout.main_page</code>) * @param root Optional view to be the parent of the generated hierarchy (if @@ -407,7 +408,7 @@ public abstract class LayoutInflater { * reasons, view inflation relies heavily on pre-processing of XML files * that is done at build time. Therefore, it is not currently possible to * use LayoutInflater with an XmlPullParser over a plain XML file at runtime. - * + * * @param parser XML dom node containing the description of the view * hierarchy. * @param root Optional view to be the parent of the generated hierarchy (if @@ -442,7 +443,7 @@ public abstract class LayoutInflater { } final String name = parser.getName(); - + if (DEBUG) { System.out.println("**************************"); System.out.println("Creating root view: " @@ -528,17 +529,17 @@ public abstract class LayoutInflater { * Low-level function for instantiating a view by name. This attempts to * instantiate a view class of the given <var>name</var> found in this * LayoutInflater's ClassLoader. - * + * * <p> * There are two things that can happen in an error case: either the * exception describing the error will be thrown, or a null will be * returned. You must deal with both possibilities -- the former will happen * the first time createView() is called for a class of a particular name, * the latter every time there-after for that class name. - * + * * @param name The full name of the class to be instantiated. * @param attrs The XML attributes supplied for this instance. - * + * * @return View The newly instantiated view, or null. */ public final View createView(String name, String prefix, AttributeSet attrs) @@ -551,7 +552,7 @@ public abstract class LayoutInflater { // Class not found in the cache, see if it's real, and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); - + if (mFilter != null && clazz != null) { boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { @@ -569,7 +570,7 @@ public abstract class LayoutInflater { // New class -- remember whether it is allowed clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); - + boolean allowed = clazz != null && mFilter.onLoadClass(clazz); mFilterMap.put(name, allowed); if (!allowed) { @@ -632,10 +633,10 @@ public abstract class LayoutInflater { * given the xml element name. Override it to handle custom view objects. If * you override this in your subclass be sure to call through to * super.onCreateView(name) for names you do not recognize. - * + * * @param name The fully qualified class name of the View to be create. * @param attrs An AttributeSet of attributes to apply to the View. - * + * * @return View The View created. */ protected View onCreateView(String name, AttributeSet attrs) @@ -679,7 +680,7 @@ public abstract class LayoutInflater { if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, mContext, attrs); } - + if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); @@ -726,7 +727,7 @@ public abstract class LayoutInflater { } final String name = parser.getName(); - + if (TAG_REQUEST_FOCUS.equals(name)) { parseRequestFocus(parser, parent); } else if (TAG_INCLUDE.equals(name)) { @@ -741,7 +742,7 @@ public abstract class LayoutInflater { final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true); - viewGroup.addView(view, params); + viewGroup.addView(view, params); } else { final View view = createViewFromTag(parent, name, attrs); final ViewGroup viewGroup = (ViewGroup) parent; @@ -810,21 +811,14 @@ public abstract class LayoutInflater { // We try to load the layout params set in the <include /> tag. If // they don't exist, we will rely on the layout params set in the // included XML file. - // During a layoutparams generation, a runtime exception is thrown - // if either layout_width or layout_height is missing. We catch - // this exception and set localParams accordingly: true means we - // successfully loaded layout params from the <include /> tag, - // false means we need to rely on the included layout params. - ViewGroup.LayoutParams params = null; - try { - params = group.generateLayoutParams(attrs); - } catch (RuntimeException e) { - params = group.generateLayoutParams(childAttrs); - } finally { - if (params != null) { - view.setLayoutParams(params); - } - } + TypedArray ta = getContext().obtainStyledAttributes(attrs, + R.styleable.ViewGroup_Layout); + boolean definesBothWidthAndHeight = + ta.hasValue(R.styleable.ViewGroup_Layout_layout_width) && + ta.hasValue(R.styleable.ViewGroup_Layout_layout_height); + AttributeSet attributes = definesBothWidthAndHeight ? attrs : childAttrs; + view.setLayoutParams(group.generateLayoutParams(attributes)); + ta.recycle(); // Inflate all children. rInflate(childParser, view, childAttrs, true); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index fa555d2..a6a5427 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -343,9 +343,10 @@ import java.util.concurrent.CopyOnWriteArrayList; * Padding can be used to offset the content of the view by a specific amount of * pixels. For instance, a left padding of 2 will push the view's content by * 2 pixels to the right of the left edge. Padding can be set using the - * {@link #setPadding(int, int, int, int)} method and queried by calling - * {@link #getPaddingLeft()}, {@link #getPaddingTop()}, {@link #getPaddingRight()}, - * {@link #getPaddingBottom()}. + * {@link #setPadding(int, int, int, int)} or {@link #setPaddingRelative(int, int, int, int)} + * method and queried by calling {@link #getPaddingLeft()}, {@link #getPaddingTop()}, + * {@link #getPaddingRight()}, {@link #getPaddingBottom()}, {@link #getPaddingStart()}, + * {@link #getPaddingEnd()}. * </p> * * <p> @@ -627,6 +628,8 @@ import java.util.concurrent.CopyOnWriteArrayList; * @attr ref android.R.styleable#View_paddingLeft * @attr ref android.R.styleable#View_paddingRight * @attr ref android.R.styleable#View_paddingTop + * @attr ref android.R.styleable#View_paddingStart + * @attr ref android.R.styleable#View_paddingEnd * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_rotation * @attr ref android.R.styleable#View_rotationX @@ -648,6 +651,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag + * @attr ref android.R.styleable#View_textAlignment * @attr ref android.R.styleable#View_transformPivotX * @attr ref android.R.styleable#View_transformPivotY * @attr ref android.R.styleable#View_translationX @@ -656,7 +660,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * * @see android.view.ViewGroup */ -public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback, +public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { private static final boolean DBG = false; @@ -1801,28 +1805,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Horizontal layout direction of this view is from Left to Right. * Use with {@link #setLayoutDirection}. - * @hide */ public static final int LAYOUT_DIRECTION_LTR = 0; /** * Horizontal layout direction of this view is from Right to Left. * Use with {@link #setLayoutDirection}. - * @hide */ public static final int LAYOUT_DIRECTION_RTL = 1; /** * Horizontal layout direction of this view is inherited from its parent. * Use with {@link #setLayoutDirection}. - * @hide */ public static final int LAYOUT_DIRECTION_INHERIT = 2; /** * Horizontal layout direction of this view is from deduced from the default language * script for the locale. Use with {@link #setLayoutDirection}. - * @hide */ public static final int LAYOUT_DIRECTION_LOCALE = 3; @@ -1887,7 +1887,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Text direction is inherited thru {@link ViewGroup} - * @hide */ public static final int TEXT_DIRECTION_INHERIT = 0; @@ -1895,7 +1894,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Text direction is using "first strong algorithm". The first strong directional character * determines the paragraph direction. If there is no strong directional character, the * paragraph direction is the view's resolved layout direction. - * @hide */ public static final int TEXT_DIRECTION_FIRST_STRONG = 1; @@ -1903,31 +1901,26 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters. * If there are neither, the paragraph direction is the view's resolved layout direction. - * @hide */ public static final int TEXT_DIRECTION_ANY_RTL = 2; /** * Text direction is forced to LTR. - * @hide */ public static final int TEXT_DIRECTION_LTR = 3; /** * Text direction is forced to RTL. - * @hide */ public static final int TEXT_DIRECTION_RTL = 4; /** * Text direction is coming from the system Locale. - * @hide */ public static final int TEXT_DIRECTION_LOCALE = 5; /** * Default text direction is inherited - * @hide */ protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT; @@ -1985,7 +1978,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /* * Default text alignment. The text alignment of this View is inherited from its parent. * Use with {@link #setTextAlignment(int)} - * @hide */ public static final int TEXT_ALIGNMENT_INHERIT = 0; @@ -1994,7 +1986,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction. * * Use with {@link #setTextAlignment(int)} - * @hide */ public static final int TEXT_ALIGNMENT_GRAVITY = 1; @@ -2002,7 +1993,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Align to the start of the paragraph, e.g. ALIGN_NORMAL. * * Use with {@link #setTextAlignment(int)} - * @hide */ public static final int TEXT_ALIGNMENT_TEXT_START = 2; @@ -2010,7 +2000,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Align to the end of the paragraph, e.g. ALIGN_OPPOSITE. * * Use with {@link #setTextAlignment(int)} - * @hide */ public static final int TEXT_ALIGNMENT_TEXT_END = 3; @@ -2018,7 +2007,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Center the paragraph, e.g. ALIGN_CENTER. * * Use with {@link #setTextAlignment(int)} - * @hide */ public static final int TEXT_ALIGNMENT_CENTER = 4; @@ -2027,7 +2015,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * layoutDirection is LTR, and ALIGN_RIGHT otherwise. * * Use with {@link #setTextAlignment(int)} - * @hide */ public static final int TEXT_ALIGNMENT_VIEW_START = 5; @@ -2036,13 +2023,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * layoutDirection is LTR, and ALIGN_LEFT otherwise. * * Use with {@link #setTextAlignment(int)} - * @hide */ public static final int TEXT_ALIGNMENT_VIEW_END = 6; /** * Default text alignment is inherited - * @hide */ protected static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY; @@ -3881,6 +3866,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal scrollabilityCache.scrollBar.setAlwaysDrawVerticalTrack(true); } + // Apply layout direction to the new Drawables if needed + final int layoutDirection = getResolvedLayoutDirection(); + if (track != null) { + track.setLayoutDirection(layoutDirection); + } + if (thumb != null) { + thumb.setLayoutDirection(layoutDirection); + } + // Re-apply user/background padding so that scrollbar(s) get added resolvePadding(); } @@ -5652,9 +5646,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #LAYOUT_DIRECTION_RTL}, * {@link #LAYOUT_DIRECTION_INHERIT} or * {@link #LAYOUT_DIRECTION_LOCALE}. - * * @attr ref android.R.styleable#View_layoutDirection - * @hide */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "LTR"), @@ -5676,7 +5668,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #LAYOUT_DIRECTION_LOCALE}. * * @attr ref android.R.styleable#View_layoutDirection - * @hide */ @RemotableViewMethod public void setLayoutDirection(int layoutDirection) { @@ -5696,7 +5687,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. - * @hide */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"), @@ -5716,7 +5706,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * layout attribute and/or the inherited value from the parent * * @return true if the layout is right-to-left. - * @hide */ @ViewDebug.ExportedProperty(category = "layout") public boolean isLayoutRtl() { @@ -11495,7 +11484,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing * that the parent directionality can and will be resolved before its children. * Will call {@link View#onResolvedLayoutDirectionChanged} when resolution is done. - * @hide */ public void resolveLayoutDirection() { // Clear any previous layout direction resolution @@ -11540,14 +11528,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Called when layout direction has been resolved. * * The default implementation does nothing. - * @hide */ public void onResolvedLayoutDirectionChanged() { } /** * Resolve padding depending on layout direction. - * @hide */ public void resolvePadding() { // If the user specified the absolute padding (either with android:padding or @@ -11607,7 +11593,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @see {@link #LAYOUT_DIRECTION_LTR} * @see {@link #LAYOUT_DIRECTION_RTL} - * @hide */ public void onPaddingChanged(int layoutDirection) { } @@ -11616,7 +11601,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Check if layout direction resolution can be done. * * @return true if layout direction resolution can be done otherwise return false. - * @hide */ public boolean canResolveLayoutDirection() { switch (getLayoutDirection()) { @@ -11630,7 +11614,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Reset the resolved layout direction. Will call {@link View#onResolvedLayoutDirectionReset} * when reset is done. - * @hide */ public void resetResolvedLayoutDirection() { // Reset the current resolved bits @@ -11647,7 +11630,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * resolved layout direction, or to inform child views that inherit their layout direction. * * The default implementation does nothing. - * @hide */ public void onResolvedLayoutDirectionReset() { } @@ -11657,7 +11639,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @param locale Locale to check * @return true if the Locale uses an RTL script. - * @hide */ protected static boolean isLayoutDirectionRtl(Locale locale) { return (LAYOUT_DIRECTION_RTL == LocaleUtil.getLayoutDirectionFromLocale(locale)); @@ -13963,13 +13944,29 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** - * Return the layout direction of a given Drawable. - * - * @param who the Drawable to query - * @hide - */ - public int getResolvedLayoutDirection(Drawable who) { - return (who == mBackground) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT; + * Resolve the Drawables depending on the layout direction. This is implicitly supposing + * that the View directionality can and will be resolved before its Drawables. + * + * Will call {@link View#onResolveDrawables} when resolution is done. + */ + public void resolveDrawables() { + if (mBackground != null) { + mBackground.setLayoutDirection(getResolvedLayoutDirection()); + } + onResolveDrawables(getResolvedLayoutDirection()); + } + + /** + * Called when layout direction has been resolved. + * + * The default implementation does nothing. + * + * @param layoutDirection The resolved layout direction. + * + * @see {@link #LAYOUT_DIRECTION_LTR} + * @see {@link #LAYOUT_DIRECTION_RTL} + */ + public void onResolveDrawables(int layoutDirection) { } /** @@ -14238,8 +14235,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal padding = new Rect(); sThreadLocal.set(padding); } + background.setLayoutDirection(getResolvedLayoutDirection()); if (background.getPadding(padding)) { - switch (background.getResolvedLayoutDirectionSelf()) { + switch (background.getLayoutDirection()) { case LAYOUT_DIRECTION_RTL: setPadding(padding.right, padding.top, padding.left, padding.bottom); break; @@ -14402,13 +14400,19 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Sets the relative padding. The view may add on the space required to display * the scrollbars, depending on the style and visibility of the scrollbars. + * So the values returned from {@link #getPaddingStart}, {@link #getPaddingTop}, + * {@link #getPaddingEnd} and {@link #getPaddingBottom} may be different * from the values set in this call. * + * @attr ref android.R.styleable#View_padding + * @attr ref android.R.styleable#View_paddingBottom + * @attr ref android.R.styleable#View_paddingStart + * @attr ref android.R.styleable#View_paddingEnd + * @attr ref android.R.styleable#View_paddingTop * @param start the start padding in pixels * @param top the top padding in pixels * @param end the end padding in pixels * @param bottom the bottom padding in pixels - * @hide */ public void setPaddingRelative(int start, int top, int end, int bottom) { mUserPaddingStart = start; @@ -14462,7 +14466,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * required to display the scrollbars as well. * * @return the start padding in pixels - * @hide */ public int getPaddingStart() { return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? @@ -14486,7 +14489,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * required to display the scrollbars as well. * * @return the end padding in pixels - * @hide */ public int getPaddingEnd() { return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? @@ -14495,10 +14497,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Return if the padding as been set thru relative values - * {@link #setPaddingRelative(int, int, int, int)} + * {@link #setPaddingRelative(int, int, int, int)} or thru + * @attr ref android.R.styleable#View_paddingStart or + * @attr ref android.R.styleable#View_paddingEnd * * @return true if the padding is relative or false if it is not. - * @hide */ public boolean isPaddingRelative() { return mUserPaddingRelative; @@ -16297,7 +16300,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} - * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"), @@ -16322,7 +16324,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} - * @hide */ public void setTextDirection(int textDirection) { if (getTextDirection() != textDirection) { @@ -16352,7 +16353,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} - * @hide */ public int getResolvedTextDirection() { // The text direction will be resolved only if needed @@ -16365,7 +16365,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Resolve the text direction. Will call {@link View#onResolvedTextDirectionChanged} when * resolution is done. - * @hide */ public void resolveTextDirection() { // Reset any previous text direction resolution @@ -16426,7 +16425,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * resolution should override this method. * * The default implementation does nothing. - * @hide */ public void onResolvedTextDirectionChanged() { } @@ -16435,7 +16433,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Check if text direction resolution can be done. * * @return true if text direction resolution can be done otherwise return false. - * @hide */ public boolean canResolveTextDirection() { switch (getTextDirection()) { @@ -16450,7 +16447,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Reset resolved text direction. Text direction can be resolved with a call to * getResolvedTextDirection(). Will call {@link View#onResolvedTextDirectionReset} when * reset is done. - * @hide */ public void resetResolvedTextDirection() { mPrivateFlags2 &= ~(TEXT_DIRECTION_RESOLVED | TEXT_DIRECTION_RESOLVED_MASK); @@ -16461,7 +16457,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Called when text direction is reset. Subclasses that care about text direction reset should * override this method and do a reset of the text direction of their children. The default * implementation does nothing. - * @hide */ public void onResolvedTextDirectionReset() { } @@ -16479,7 +16474,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} - * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"), @@ -16508,7 +16502,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_ALIGNMENT_VIEW_END} * * @attr ref android.R.styleable#View_textAlignment - * @hide */ public void setTextAlignment(int textAlignment) { if (textAlignment != getTextAlignment()) { @@ -16538,7 +16531,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} - * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"), @@ -16560,7 +16552,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Resolve the text alignment. Will call {@link View#onResolvedTextAlignmentChanged} when * resolution is done. - * @hide */ public void resolveTextAlignment() { // Reset any previous text alignment resolution @@ -16625,7 +16616,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Check if text alignment resolution can be done. * * @return true if text alignment resolution can be done otherwise return false. - * @hide */ public boolean canResolveTextAlignment() { switch (getTextAlignment()) { @@ -16641,7 +16631,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * resolution should override this method. * * The default implementation does nothing. - * @hide */ public void onResolvedTextAlignmentChanged() { } @@ -16650,7 +16639,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Reset resolved text alignment. Text alignment can be resolved with a call to * getResolvedTextAlignment(). Will call {@link View#onResolvedTextAlignmentReset} when * reset is done. - * @hide */ public void resetResolvedTextAlignment() { // Reset any previous text alignment resolution @@ -16662,7 +16650,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Called when text alignment is reset. Subclasses that care about text alignment reset should * override this method and do a reset of the text alignment of their children. The default * implementation does nothing. - * @hide */ public void onResolvedTextAlignmentReset() { } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index dd671dc..00d4fc7 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -255,6 +255,35 @@ public class ViewDebug { boolean retrieveReturn() default false; } + /** + * Allows a View to inject custom children into HierarchyViewer. For example, + * WebView uses this to add its internal layer tree as a child to itself + * @hide + */ + public interface HierarchyHandler { + /** + * Dumps custom children to hierarchy viewer. + * See ViewDebug.dumpViewWithProperties(Context, View, BufferedWriter, int) + * for the format + * + * An empty implementation should simply do nothing + * + * @param out The output writer + * @param level The indentation level + */ + public void dumpViewHierarchyWithProperties(BufferedWriter out, int level); + + /** + * Returns a View to enable grabbing screenshots from custom children + * returned in dumpViewHierarchyWithProperties. + * + * @param className The className of the view to find + * @param hashCode The hashCode of the view to find + * @return the View to capture from, or null if not found + */ + public View findHierarchyView(String className, int hashCode); + } + private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null; private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null; @@ -759,6 +788,13 @@ public class ViewDebug { } else if (isRequestedView(view, className, hashCode)) { return view; } + if (view instanceof HierarchyHandler) { + final View found = ((HierarchyHandler)view) + .findHierarchyView(className, hashCode); + if (found != null) { + return found; + } + } } return null; @@ -783,6 +819,9 @@ public class ViewDebug { dumpViewWithProperties(context, view, out, level + 1); } } + if (group instanceof HierarchyHandler) { + ((HierarchyHandler)group).dumpViewHierarchyWithProperties(out, level + 1); + } } private static boolean dumpViewWithProperties(Context context, View view, diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 044627c..257b861 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5243,9 +5243,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - /** - * @hide - */ @Override public void onResolvedLayoutDirectionReset() { // Take care of resetting the children resolution too @@ -5258,9 +5255,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - /** - * @hide - */ @Override public void onResolvedTextDirectionReset() { // Take care of resetting the children resolution too @@ -5273,9 +5267,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - /** - * @hide - */ @Override public void onResolvedTextAlignmentReset() { // Take care of resetting the children resolution too @@ -5446,15 +5437,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Extracts the layout parameters from the supplied attributes. + * Extracts the <code>width</code> and <code>height</code> layout parameters + * from the supplied TypedArray, <code>a</code>, and assigns them + * to the appropriate fields. If, <code>a</code>, does not contain an + * entry for either attribute, the value, {@link ViewGroup.LayoutParams#WRAP_CONTENT}, + * is used as a default. * * @param a the style attributes to extract the parameters from * @param widthAttr the identifier of the width attribute * @param heightAttr the identifier of the height attribute */ protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { - width = a.getLayoutDimension(widthAttr, "layout_width"); - height = a.getLayoutDimension(heightAttr, "layout_height"); + width = a.getLayoutDimension(widthAttr, WRAP_CONTENT); + height = a.getLayoutDimension(heightAttr, WRAP_CONTENT); } /** @@ -5466,7 +5461,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * {@link View#LAYOUT_DIRECTION_LTR} * {@link View#LAYOUT_DIRECTION_RTL} - * @hide */ public void onResolveLayoutDirection(int layoutDirection) { } @@ -5558,7 +5552,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * The start margin in pixels of the child. * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value * to this field. - * @hide */ @ViewDebug.ExportedProperty(category = "layout") public int startMargin = DEFAULT_RELATIVE; @@ -5567,7 +5560,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * The end margin in pixels of the child. * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value * to this field. - * @hide */ @ViewDebug.ExportedProperty(category = "layout") public int endMargin = DEFAULT_RELATIVE; @@ -5686,6 +5678,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom + * * @hide */ public void setMarginsRelative(int start, int top, int end, int bottom) { @@ -5701,7 +5694,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart * * @return the start margin in pixels. - * @hide */ public int getMarginStart() { return startMargin; @@ -5713,7 +5705,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd * * @return the end margin in pixels. - * @hide */ public int getMarginEnd() { return endMargin; @@ -5726,7 +5717,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd * * @return true if either marginStart or marginEnd has been set - * @hide */ public boolean isMarginRelative() { return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE); @@ -5735,7 +5725,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins * may be overridden depending on layout direction. - * @hide */ @Override public void onResolveLayoutDirection(int layoutDirection) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 6959b84..17783a4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -76,7 +76,6 @@ import android.widget.Scroller; import com.android.internal.R; import com.android.internal.policy.PolicyManager; import com.android.internal.view.BaseSurfaceHolder; -import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.RootViewSurfaceTaker; @@ -4662,23 +4661,20 @@ public final class ViewRootImpl implements ViewParent, } } - static class InputMethodCallback extends IInputMethodCallback.Stub { + static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback { private WeakReference<ViewRootImpl> mViewAncestor; public InputMethodCallback(ViewRootImpl viewAncestor) { mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); } + @Override public void finishedEvent(int seq, boolean handled) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchImeFinishedEvent(seq, handled); } } - - public void sessionCreated(IInputMethodSession session) { - // Stub -- not for use in the client. - } } static class W extends IWindow.Stub { diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index 1c5d436..cfcf3c0 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -32,12 +32,18 @@ import java.util.concurrent.CopyOnWriteArrayList; * for more information. */ public final class ViewTreeObserver { + // Recursive listeners use CopyOnWriteArrayList private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; - private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; - private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; - private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; - private ArrayList<OnPreDrawListener> mOnPreDrawListeners; + + // Non-recursive listeners use CopyOnWriteArray + // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive + private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners; + private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; + private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; + private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; + + // These listeners cannot be mutated during dispatch private ArrayList<OnDrawListener> mOnDrawListeners; private boolean mAlive = true; @@ -147,7 +153,7 @@ public final class ViewTreeObserver { * windows behind it should be placed. */ public final Rect contentInsets = new Rect(); - + /** * Offsets from the frame of the window at which windows behind it * are visible. @@ -166,13 +172,13 @@ public final class ViewTreeObserver { * can be touched. */ public static final int TOUCHABLE_INSETS_FRAME = 0; - + /** * Option for {@link #setTouchableInsets(int)}: the area inside of * the content insets can be touched. */ public static final int TOUCHABLE_INSETS_CONTENT = 1; - + /** * Option for {@link #setTouchableInsets(int)}: the area inside of * the visible insets can be touched. @@ -195,7 +201,7 @@ public final class ViewTreeObserver { } int mTouchableInsets; - + void reset() { contentInsets.setEmpty(); visibleInsets.setEmpty(); @@ -231,7 +237,7 @@ public final class ViewTreeObserver { mTouchableInsets = other.mTouchableInsets; } } - + /** * Interface definition for a callback to be invoked when layout has * completed and the client can compute its interior insets. @@ -363,7 +369,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnGlobalLayoutListeners == null) { - mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>(); + mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>(); } mOnGlobalLayoutListeners.add(listener); @@ -413,7 +419,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnPreDrawListeners == null) { - mOnPreDrawListeners = new ArrayList<OnPreDrawListener>(); + mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>(); } mOnPreDrawListeners.add(listener); @@ -485,7 +491,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnScrollChangedListeners == null) { - mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>(); + mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>(); } mOnScrollChangedListeners.add(listener); @@ -558,7 +564,7 @@ public final class ViewTreeObserver { if (mOnComputeInternalInsetsListeners == null) { mOnComputeInternalInsetsListeners = - new CopyOnWriteArrayList<OnComputeInternalInsetsListener>(); + new CopyOnWriteArray<OnComputeInternalInsetsListener>(); } mOnComputeInternalInsetsListeners.add(listener); @@ -640,10 +646,16 @@ public final class ViewTreeObserver { // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. - final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; + final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; if (listeners != null && listeners.size() > 0) { - for (OnGlobalLayoutListener listener : listeners) { - listener.onGlobalLayout(); + CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onGlobalLayout(); + } + } finally { + listeners.end(); } } } @@ -658,17 +670,17 @@ public final class ViewTreeObserver { */ @SuppressWarnings("unchecked") public final boolean dispatchOnPreDraw() { - // NOTE: we *must* clone the listener list to perform the dispatching. - // The clone is a safe guard against listeners that - // could mutate the list by calling the various add/remove methods. This prevents - // the array from being modified while we process it. boolean cancelDraw = false; - if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) { - final ArrayList<OnPreDrawListener> listeners = - (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone(); - int numListeners = listeners.size(); - for (int i = 0; i < numListeners; ++i) { - cancelDraw |= !(listeners.get(i).onPreDraw()); + final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners; + if (listeners != null && listeners.size() > 0) { + CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + cancelDraw |= !(access.get(i).onPreDraw()); + } + } finally { + listeners.end(); } } return cancelDraw; @@ -710,10 +722,16 @@ public final class ViewTreeObserver { // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. - final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; + final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners; if (listeners != null && listeners.size() > 0) { - for (OnScrollChangedListener listener : listeners) { - listener.onScrollChanged(); + CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onScrollChanged(); + } + } finally { + listeners.end(); } } } @@ -722,11 +740,11 @@ public final class ViewTreeObserver { * Returns whether there are listeners for computing internal insets. */ final boolean hasComputeInternalInsetsListeners() { - final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = + final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; return (listeners != null && listeners.size() > 0); } - + /** * Calls all listeners to compute the current insets. */ @@ -735,12 +753,105 @@ public final class ViewTreeObserver { // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. - final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = + final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; if (listeners != null && listeners.size() > 0) { - for (OnComputeInternalInsetsListener listener : listeners) { - listener.onComputeInternalInsets(inoutInfo); + CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onComputeInternalInsets(inoutInfo); + } + } finally { + listeners.end(); + } + } + } + + /** + * Copy on write array. This array is not thread safe, and only one loop can + * iterate over this array at any given time. This class avoids allocations + * until a concurrent modification happens. + * + * Usage: + * + * CopyOnWriteArray.Access<MyData> access = array.start(); + * try { + * for (int i = 0; i < access.size(); i++) { + * MyData d = access.get(i); + * } + * } finally { + * access.end(); + * } + */ + static class CopyOnWriteArray<T> { + private ArrayList<T> mData = new ArrayList<T>(); + private ArrayList<T> mDataCopy; + + private final Access<T> mAccess = new Access<T>(); + + private boolean mStart; + + static class Access<T> { + private ArrayList<T> mData; + private int mSize; + + T get(int index) { + return mData.get(index); + } + + int size() { + return mSize; + } + } + + CopyOnWriteArray() { + } + + private ArrayList<T> getArray() { + if (mStart) { + if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData); + return mDataCopy; } + return mData; + } + + Access<T> start() { + if (mStart) throw new IllegalStateException("Iteration already started"); + mStart = true; + mDataCopy = null; + mAccess.mData = mData; + mAccess.mSize = mData.size(); + return mAccess; + } + + void end() { + if (!mStart) throw new IllegalStateException("Iteration not started"); + mStart = false; + if (mDataCopy != null) { + mData = mDataCopy; + } + mDataCopy = null; + } + + int size() { + return getArray().size(); + } + + void add(T item) { + getArray().add(item); + } + + void addAll(CopyOnWriteArray<T> array) { + getArray().addAll(array.mData); + } + + void remove(T item) { + getArray().remove(item); + } + + void clear() { + getArray().clear(); } } } diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index cf9bcdd..4d4e985c 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -104,6 +104,9 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private boolean mShowCombinedVolumes; private boolean mVoiceCapable; + // True if we want to play tones on the system stream when the master stream is specified. + private final boolean mPlayMasterStreamTones; + /** Dialog containing all the sliders */ private final Dialog mDialog; /** Dialog's content view */ @@ -282,6 +285,13 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie mMoreButton.setOnClickListener(this); } + boolean masterVolumeOnly = context.getResources().getBoolean( + com.android.internal.R.bool.config_useMasterVolume); + boolean masterVolumeKeySounds = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_useVolumeKeySounds); + + mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds; + listenToRingerMode(); } @@ -790,7 +800,16 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie * Lock on this VolumePanel instance as long as you use the returned ToneGenerator. */ private ToneGenerator getOrCreateToneGenerator(int streamType) { - if (streamType == STREAM_MASTER) return null; + if (streamType == STREAM_MASTER) { + // For devices that use the master volume setting only but still want to + // play a volume-changed tone, direct the master volume pseudostream to + // the system stream's tone generator. + if (mPlayMasterStreamTones) { + streamType = AudioManager.STREAM_SYSTEM; + } else { + return null; + } + } synchronized (this) { if (mToneGenerators[streamType] == null) { try { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index b0e90db..ec4114e 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -718,6 +718,7 @@ public abstract class Window { * per {@link #setFlags}. * @param flags The flag bits to be set. * @see #setFlags + * @see #clearFlags */ public void addFlags(int flags) { setFlags(flags, flags); @@ -728,6 +729,7 @@ public abstract class Window { * per {@link #setFlags}. * @param flags The flag bits to be cleared. * @see #setFlags + * @see #addFlags */ public void clearFlags(int flags) { setFlags(0, flags); @@ -749,6 +751,8 @@ public abstract class Window { * * @param flags The new window flags (see WindowManager.LayoutParams). * @param mask Which of the window flag bits to modify. + * @see #addFlags + * @see #clearFlags */ public void setFlags(int flags, int mask) { final WindowManager.LayoutParams attrs = getAttributes(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index d94275b..e6a29ea 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -162,6 +162,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"), @ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"), @ViewDebug.IntToString(from = TYPE_APPLICATION_ATTACHED_DIALOG, to = "TYPE_APPLICATION_ATTACHED_DIALOG"), + @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA_OVERLAY, to = "TYPE_APPLICATION_MEDIA_OVERLAY"), @ViewDebug.IntToString(from = TYPE_STATUS_BAR, to = "TYPE_STATUS_BAR"), @ViewDebug.IntToString(from = TYPE_SEARCH_BAR, to = "TYPE_SEARCH_BAR"), @ViewDebug.IntToString(from = TYPE_PHONE, to = "TYPE_PHONE"), @@ -170,8 +171,6 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_TOAST, to = "TYPE_TOAST"), @ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY, to = "TYPE_SYSTEM_OVERLAY"), @ViewDebug.IntToString(from = TYPE_PRIORITY_PHONE, to = "TYPE_PRIORITY_PHONE"), - @ViewDebug.IntToString(from = TYPE_STATUS_BAR_PANEL, to = "TYPE_STATUS_BAR_PANEL"), - @ViewDebug.IntToString(from = TYPE_STATUS_BAR_SUB_PANEL, to = "TYPE_STATUS_BAR_SUB_PANEL"), @ViewDebug.IntToString(from = TYPE_SYSTEM_DIALOG, to = "TYPE_SYSTEM_DIALOG"), @ViewDebug.IntToString(from = TYPE_KEYGUARD_DIALOG, to = "TYPE_KEYGUARD_DIALOG"), @ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR, to = "TYPE_SYSTEM_ERROR"), @@ -185,7 +184,10 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_POINTER, to = "TYPE_POINTER"), @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR, to = "TYPE_NAVIGATION_BAR"), @ViewDebug.IntToString(from = TYPE_VOLUME_OVERLAY, to = "TYPE_VOLUME_OVERLAY"), - @ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS") + @ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS"), + @ViewDebug.IntToString(from = TYPE_HIDDEN_NAV_CONSUMER, to = "TYPE_HIDDEN_NAV_CONSUMER"), + @ViewDebug.IntToString(from = TYPE_DREAM, to = "TYPE_DREAM"), + @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL") }) public int type; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d2cc2d8..89932cc 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -35,6 +35,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; +import android.os.SystemClock; import android.text.style.SuggestionSpan; import android.util.Log; import android.util.PrintWriterPrinter; @@ -225,6 +226,13 @@ public final class InputMethodManager { */ public static final int CONTROL_START_INITIAL = 1<<8; + /** + * Timeout in milliseconds for delivering a key to an IME. + */ + static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500; + + private static final int MAX_PENDING_EVENT_POOL_SIZE = 4; + final IInputMethodManager mService; final Looper mMainLooper; @@ -312,12 +320,17 @@ public final class InputMethodManager { */ IInputMethodSession mCurMethod; + PendingEvent mPendingEventPool; + int mPendingEventPoolSize; + PendingEvent mFirstPendingEvent; + // ----------------------------------------------------------- static final int MSG_DUMP = 1; static final int MSG_BIND = 2; static final int MSG_UNBIND = 3; static final int MSG_SET_ACTIVE = 4; + static final int MSG_EVENT_TIMEOUT = 5; class H extends Handler { H(Looper looper) { @@ -413,6 +426,17 @@ public final class InputMethodManager { } return; } + case MSG_EVENT_TIMEOUT: { + // Even though the message contains both the sequence number + // and the PendingEvent object itself, we only pass the + // sequence number to the timeoutEvent function because it's + // possible for the PendingEvent object to be dequeued and + // recycled concurrently. To avoid a possible race, we make + // a point of always looking up the PendingEvent within the + // queue given only the sequence number of the event. + timeoutEvent(msg.arg1); + return; + } } } } @@ -476,6 +500,18 @@ public final class InputMethodManager { }; final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); + + final IInputMethodCallback mInputMethodCallback = new IInputMethodCallback.Stub() { + @Override + public void finishedEvent(int seq, boolean handled) { + InputMethodManager.this.finishedEvent(seq, handled); + } + + @Override + public void sessionCreated(IInputMethodSession session) { + // Stub -- not for use in the client. + } + }; InputMethodManager(IInputMethodManager service, Looper looper) { mService = service; @@ -1105,6 +1141,7 @@ public final class InputMethodManager { if (res.id != null) { mBindSequence = res.sequence; mCurMethod = res.method; + mCurId = res.id; } else if (mCurMethod == null) { // This means there is no input method available. if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); @@ -1511,76 +1548,159 @@ public final class InputMethodManager { * @hide */ public void dispatchKeyEvent(Context context, int seq, KeyEvent key, - IInputMethodCallback callback) { + FinishedEventCallback callback) { + boolean handled = false; synchronized (mH) { if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); - - if (mCurMethod == null) { - try { - callback.finishedEvent(seq, false); - } catch (RemoteException e) { - } - return; - } - - if (key.getAction() == KeyEvent.ACTION_DOWN - && key.getKeyCode() == KeyEvent.KEYCODE_SYM) { - showInputMethodPicker(); - try { - callback.finishedEvent(seq, true); - } catch (RemoteException e) { - } - return; - } - try { - if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); - mCurMethod.dispatchKeyEvent(seq, key, callback); - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); - try { - callback.finishedEvent(seq, false); - } catch (RemoteException ex) { + + if (mCurMethod != null) { + if (key.getAction() == KeyEvent.ACTION_DOWN + && key.getKeyCode() == KeyEvent.KEYCODE_SYM) { + showInputMethodPickerLocked(); + handled = true; + } else { + try { + if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod); + final long startTime = SystemClock.uptimeMillis(); + mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback); + enqueuePendingEventLocked(startTime, seq, mCurId, callback); + return; + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e); + } } } } + + callback.finishedEvent(seq, handled); } /** * @hide */ void dispatchTrackballEvent(Context context, int seq, MotionEvent motion, - IInputMethodCallback callback) { + FinishedEventCallback callback) { synchronized (mH) { if (DEBUG) Log.d(TAG, "dispatchTrackballEvent"); - - if (mCurMethod == null || mCurrentTextBoxAttribute == null) { + + if (mCurMethod != null && mCurrentTextBoxAttribute != null) { try { - callback.finishedEvent(seq, false); + if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); + final long startTime = SystemClock.uptimeMillis(); + mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback); + enqueuePendingEventLocked(startTime, seq, mCurId, callback); + return; } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); } - return; } - - try { - if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod); - mCurMethod.dispatchTrackballEvent(seq, motion, callback); - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e); - try { - callback.finishedEvent(seq, false); - } catch (RemoteException ex) { - } + } + + callback.finishedEvent(seq, false); + } + + void finishedEvent(int seq, boolean handled) { + final FinishedEventCallback callback; + synchronized (mH) { + PendingEvent p = dequeuePendingEventLocked(seq); + if (p == null) { + return; // spurious, event already finished or timed out } + mH.removeMessages(MSG_EVENT_TIMEOUT, p); + callback = p.mCallback; + recyclePendingEventLocked(p); } + callback.finishedEvent(seq, handled); } - public void showInputMethodPicker() { + void timeoutEvent(int seq) { + final FinishedEventCallback callback; synchronized (mH) { - try { - mService.showInputMethodPickerFromClient(mClient); - } catch (RemoteException e) { - Log.w(TAG, "IME died: " + mCurId, e); + PendingEvent p = dequeuePendingEventLocked(seq); + if (p == null) { + return; // spurious, event already finished or timed out } + long delay = SystemClock.uptimeMillis() - p.mStartTime; + Log.w(TAG, "Timeout waiting for IME to handle input event after " + + delay + "ms: " + p.mInputMethodId); + callback = p.mCallback; + recyclePendingEventLocked(p); + } + callback.finishedEvent(seq, false); + } + + private void enqueuePendingEventLocked( + long startTime, int seq, String inputMethodId, FinishedEventCallback callback) { + PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback); + p.mNext = mFirstPendingEvent; + mFirstPendingEvent = p; + + Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p); + msg.setAsynchronous(true); + mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT); + } + + private PendingEvent dequeuePendingEventLocked(int seq) { + PendingEvent p = mFirstPendingEvent; + if (p == null) { + return null; + } + if (p.mSeq == seq) { + mFirstPendingEvent = p.mNext; + } else { + PendingEvent prev = p; + do { + p = prev.mNext; + if (p == null) { + return null; + } + } while (p.mSeq != seq); + prev.mNext = p.mNext; + } + p.mNext = null; + return p; + } + + private PendingEvent obtainPendingEventLocked( + long startTime, int seq, String inputMethodId, FinishedEventCallback callback) { + PendingEvent p = mPendingEventPool; + if (p != null) { + mPendingEventPoolSize -= 1; + mPendingEventPool = p.mNext; + p.mNext = null; + } else { + p = new PendingEvent(); + } + + p.mStartTime = startTime; + p.mSeq = seq; + p.mInputMethodId = inputMethodId; + p.mCallback = callback; + return p; + } + + private void recyclePendingEventLocked(PendingEvent p) { + p.mInputMethodId = null; + p.mCallback = null; + + if (mPendingEventPoolSize < MAX_PENDING_EVENT_POOL_SIZE) { + mPendingEventPoolSize += 1; + p.mNext = mPendingEventPool; + mPendingEventPool = p; + } + } + + public void showInputMethodPicker() { + synchronized (mH) { + showInputMethodPickerLocked(); + } + } + + private void showInputMethodPickerLocked() { + try { + mService.showInputMethodPickerFromClient(mClient); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); } } @@ -1773,4 +1893,22 @@ public final class InputMethodManager { + " mCursorCandStart=" + mCursorCandStart + " mCursorCandEnd=" + mCursorCandEnd); } + + /** + * Callback that is invoked when an input event that was dispatched to + * the IME has been finished. + * @hide + */ + public interface FinishedEventCallback { + public void finishedEvent(int seq, boolean handled); + } + + private static final class PendingEvent { + public PendingEvent mNext; + + public long mStartTime; + public int mSeq; + public String mInputMethodId; + public FinishedEventCallback mCallback; + } } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 5108990..fe812af 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -74,6 +74,7 @@ class BrowserFrame extends Handler { private final CallbackProxy mCallbackProxy; private final WebSettingsClassic mSettings; private final Context mContext; + private final WebViewDatabaseClassic mDatabase; private final WebViewCore mWebViewCore; /* package */ boolean mLoadInitFromJava; private int mLoadType; @@ -242,6 +243,7 @@ class BrowserFrame extends Handler { mSettings = settings; mContext = context; mCallbackProxy = proxy; + mDatabase = WebViewDatabaseClassic.getInstance(appContext); mWebViewCore = w; mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy); @@ -424,8 +426,7 @@ class BrowserFrame extends Handler { if (h != null) { String url = WebTextView.urlForAutoCompleteData(h.getUrl()); if (url != null) { - WebViewDatabaseClassic.getInstance(mContext).setFormData( - url, data); + mDatabase.setFormData(url, data); } } } @@ -497,9 +498,8 @@ class BrowserFrame extends Handler { if (item != null) { WebAddress uri = new WebAddress(item.getUrl()); String schemePlusHost = uri.getScheme() + uri.getHost(); - String[] up = - WebViewDatabaseClassic.getInstance(mContext) - .getUsernamePassword(schemePlusHost); + String[] up = mDatabase.getUsernamePassword( + schemePlusHost); if (up != null && up[0] != null) { setUsernamePassword(up[0], up[1]); } @@ -800,10 +800,10 @@ class BrowserFrame extends Handler { // the post data (there could be another form on the // page and that was posted instead. String postString = new String(postData); - WebViewDatabaseClassic db = WebViewDatabaseClassic.getInstance(mContext); if (postString.contains(URLEncoder.encode(username)) && postString.contains(URLEncoder.encode(password))) { - String[] saved = db.getUsernamePassword(schemePlusHost); + String[] saved = mDatabase.getUsernamePassword( + schemePlusHost); if (saved != null) { // null username implies that user has chosen not to // save password @@ -811,8 +811,7 @@ class BrowserFrame extends Handler { // non-null username implies that user has // chosen to save password, so update the // recorded password - db.setUsernamePassword(schemePlusHost, username, - password); + mDatabase.setUsernamePassword(schemePlusHost, username, password); } } else { // CallbackProxy will handle creating the resume diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index 2d9f60d..8b7cecf 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -71,7 +71,7 @@ class CallbackProxy extends Handler { // Start with 100 to indicate it is not in load for the empty page. private volatile int mLatestProgress = 100; // Back/Forward list - private final WebBackForwardList mBackForwardList; + private final WebBackForwardListClassic mBackForwardList; // Back/Forward list client private volatile WebBackForwardListClient mWebBackForwardListClient; // Used to call startActivity during url override. @@ -117,7 +117,6 @@ class CallbackProxy extends Handler { private static final int ADD_HISTORY_ITEM = 135; private static final int HISTORY_INDEX_CHANGED = 136; private static final int AUTH_CREDENTIALS = 137; - private static final int SET_INSTALLABLE_WEBAPP = 138; private static final int NOTIFY_SEARCHBOX_LISTENERS = 139; private static final int AUTO_LOGIN = 140; private static final int CLIENT_CERT_REQUEST = 141; @@ -188,7 +187,7 @@ class CallbackProxy extends Handler { // Used to start a default activity. mContext = context; mWebView = w; - mBackForwardList = new WebBackForwardList(this); + mBackForwardList = new WebBackForwardListClassic(this); } protected synchronized void blockMessages() { @@ -249,7 +248,7 @@ class CallbackProxy extends Handler { * Get the Back/Forward list to return to the user or to update the cached * history list. */ - public WebBackForwardList getBackForwardList() { + public WebBackForwardListClassic getBackForwardList() { return mBackForwardList; } @@ -403,17 +402,18 @@ class CallbackProxy extends Handler { break; case PROCEEDED_AFTER_SSL_ERROR: - if (mWebViewClient != null) { - mWebViewClient.onProceededAfterSslError(mWebView.getWebView(), + if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) { + ((WebViewClientClassicExt) mWebViewClient).onProceededAfterSslError( + mWebView.getWebView(), (SslError) msg.obj); } break; case CLIENT_CERT_REQUEST: - if (mWebViewClient != null) { - HashMap<String, Object> map = - (HashMap<String, Object>) msg.obj; - mWebViewClient.onReceivedClientCertRequest(mWebView.getWebView(), + if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) { + HashMap<String, Object> map = (HashMap<String, Object>) msg.obj; + ((WebViewClientClassicExt) mWebViewClient).onReceivedClientCertRequest( + mWebView.getWebView(), (ClientCertRequestHandler) map.get("handler"), (String) map.get("host_and_port")); } @@ -857,11 +857,6 @@ class CallbackProxy extends Handler { host, realm, username, password); break; } - case SET_INSTALLABLE_WEBAPP: - if (mWebChromeClient != null) { - mWebChromeClient.setInstallableWebApp(); - } - break; case NOTIFY_SEARCHBOX_LISTENERS: { SearchBoxImpl searchBox = (SearchBoxImpl) mWebView.getSearchBox(); @@ -1081,7 +1076,7 @@ class CallbackProxy extends Handler { } public void onProceededAfterSslError(SslError error) { - if (mWebViewClient == null) { + if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) { return; } Message msg = obtainMessage(PROCEEDED_AFTER_SSL_ERROR); @@ -1092,7 +1087,7 @@ class CallbackProxy extends Handler { public void onReceivedClientCertRequest(ClientCertRequestHandler handler, String host_and_port) { // Do an unsynchronized quick check to avoid posting if no callback has // been set. - if (mWebViewClient == null) { + if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) { handler.cancel(); return; } @@ -1301,7 +1296,7 @@ class CallbackProxy extends Handler { public void onReceivedIcon(Bitmap icon) { // The current item might be null if the icon was already stored in the // database and this is a new WebView. - WebHistoryItem i = mBackForwardList.getCurrentItem(); + WebHistoryItemClassic i = mBackForwardList.getCurrentItem(); if (i != null) { i.setFavicon(icon); } @@ -1316,7 +1311,7 @@ class CallbackProxy extends Handler { /* package */ void onReceivedTouchIconUrl(String url, boolean precomposed) { // We should have a current item but we do not want to crash so check // for null. - WebHistoryItem i = mBackForwardList.getCurrentItem(); + WebHistoryItemClassic i = mBackForwardList.getCurrentItem(); if (i != null) { i.setTouchIconUrl(url, precomposed); } @@ -1608,13 +1603,6 @@ class CallbackProxy extends Handler { sendMessage(msg); } - void setInstallableWebApp() { - if (mWebChromeClient == null) { - return; - } - sendMessage(obtainMessage(SET_INSTALLABLE_WEBAPP)); - } - boolean canShowAlertDialog() { // We can only display the alert dialog if mContext is // an Activity context. diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java index 4e99335..276bcae 100644 --- a/core/java/android/webkit/CookieSyncManager.java +++ b/core/java/android/webkit/CookieSyncManager.java @@ -86,10 +86,8 @@ public final class CookieSyncManager extends WebSyncManager { throw new IllegalArgumentException("Invalid context argument"); } - JniUtil.setContext(context); - Context appContext = context.getApplicationContext(); if (sRef == null) { - sRef = new CookieSyncManager(appContext); + sRef = new CookieSyncManager(context); } return sRef; } diff --git a/core/java/android/webkit/MockGeolocation.java b/core/java/android/webkit/MockGeolocation.java index fbda492..885c6c2 100644 --- a/core/java/android/webkit/MockGeolocation.java +++ b/core/java/android/webkit/MockGeolocation.java @@ -17,21 +17,29 @@ package android.webkit; /** - * This class is simply a container for the methods used to configure WebKit's - * mock Geolocation service for use in LayoutTests. + * Used to configure the mock Geolocation client for the LayoutTests. * @hide */ public final class MockGeolocation { + private WebViewCore mWebViewCore; - // Global instance of a MockGeolocation - private static MockGeolocation sMockGeolocation; + public MockGeolocation(WebViewCore webViewCore) { + mWebViewCore = webViewCore; + } + + /** + * Sets use of the mock Geolocation client. Also resets that client. + */ + public void setUseMock() { + nativeSetUseMock(mWebViewCore); + } /** * Set the position for the mock Geolocation service. */ public void setPosition(double latitude, double longitude, double accuracy) { // This should only ever be called on the WebKit thread. - nativeSetPosition(latitude, longitude, accuracy); + nativeSetPosition(mWebViewCore, latitude, longitude, accuracy); } /** @@ -39,21 +47,18 @@ public final class MockGeolocation { */ public void setError(int code, String message) { // This should only ever be called on the WebKit thread. - nativeSetError(code, message); + nativeSetError(mWebViewCore, code, message); } - /** - * Get the global instance of MockGeolocation. - * @return The global MockGeolocation instance. - */ - public static MockGeolocation getInstance() { - if (sMockGeolocation == null) { - sMockGeolocation = new MockGeolocation(); - } - return sMockGeolocation; + public void setPermission(boolean allow) { + // This should only ever be called on the WebKit thread. + nativeSetPermission(mWebViewCore, allow); } // Native functions - private static native void nativeSetPosition(double latitude, double longitude, double accuracy); - private static native void nativeSetError(int code, String message); + private static native void nativeSetUseMock(WebViewCore webViewCore); + private static native void nativeSetPosition(WebViewCore webViewCore, double latitude, + double longitude, double accuracy); + private static native void nativeSetError(WebViewCore webViewCore, int code, String message); + private static native void nativeSetPermission(WebViewCore webViewCore, boolean allow); } diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java index c161085..096d4cda 100644 --- a/core/java/android/webkit/ViewStateSerializer.java +++ b/core/java/android/webkit/ViewStateSerializer.java @@ -16,7 +16,6 @@ package android.webkit; import android.graphics.Point; -import android.graphics.Region; import android.webkit.WebViewCore.DrawData; import java.io.DataInputStream; @@ -68,6 +67,15 @@ class ViewStateSerializer { return draw; } + public static void dumpLayerHierarchy(int baseLayer, OutputStream out, int level) { + nativeDumpLayerHierarchy(baseLayer, level, out, + new byte[WORKING_STREAM_STORAGE]); + } + + + private static native void nativeDumpLayerHierarchy(int baseLayer, int level, + OutputStream out, byte[] storage); + private static native boolean nativeSerializeViewState(int baseLayer, OutputStream stream, byte[] storage); diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java index 79e634e..bfef2e7 100644 --- a/core/java/android/webkit/WebBackForwardList.java +++ b/core/java/android/webkit/WebBackForwardList.java @@ -17,7 +17,6 @@ package android.webkit; import java.io.Serializable; -import java.util.ArrayList; /** * This class contains the back/forward list for a WebView. @@ -25,22 +24,11 @@ import java.util.ArrayList; * inspect the entries in the list. */ public class WebBackForwardList implements Cloneable, Serializable { - // Current position in the list. - private int mCurrentIndex; - // ArrayList of WebHistoryItems for maintaining our copy. - private ArrayList<WebHistoryItem> mArray; - // Flag to indicate that the list is invalid - private boolean mClearPending; - // CallbackProxy to issue client callbacks. - private final CallbackProxy mCallbackProxy; /** - * Construct a back/forward list used by clients of WebView. + * @hide */ - /*package*/ WebBackForwardList(CallbackProxy proxy) { - mCurrentIndex = -1; - mArray = new ArrayList<WebHistoryItem>(); - mCallbackProxy = proxy; + public WebBackForwardList() { } /** @@ -49,7 +37,7 @@ public class WebBackForwardList implements Cloneable, Serializable { * @return The current history item. */ public synchronized WebHistoryItem getCurrentItem() { - return getItemAtIndex(mCurrentIndex); + throw new MustOverrideException(); } /** @@ -58,7 +46,7 @@ public class WebBackForwardList implements Cloneable, Serializable { * @return The current index from 0...n or -1 if the list is empty. */ public synchronized int getCurrentIndex() { - return mCurrentIndex; + throw new MustOverrideException(); } /** @@ -67,10 +55,7 @@ public class WebBackForwardList implements Cloneable, Serializable { * @param index The index to retrieve. */ public synchronized WebHistoryItem getItemAtIndex(int index) { - if (index < 0 || index >= getSize()) { - return null; - } - return mArray.get(index); + throw new MustOverrideException(); } /** @@ -78,78 +63,7 @@ public class WebBackForwardList implements Cloneable, Serializable { * @return The size of the list. */ public synchronized int getSize() { - return mArray.size(); - } - - /** - * Mark the back/forward list as having a pending clear. This is used on the - * UI side to mark the list as being invalid during the clearHistory method. - */ - /*package*/ synchronized void setClearPending() { - mClearPending = true; - } - - /** - * Return the status of the clear flag. This is used on the UI side to - * determine if the list is valid for checking things like canGoBack. - */ - /*package*/ synchronized boolean getClearPending() { - return mClearPending; - } - - /** - * Add a new history item to the list. This will remove all items after the - * current item and append the new item to the end of the list. Called from - * the WebCore thread only. Synchronized because the UI thread may be - * reading the array or the current index. - * @param item A new history item. - */ - /*package*/ synchronized void addHistoryItem(WebHistoryItem item) { - // Update the current position because we are going to add the new item - // in that slot. - ++mCurrentIndex; - // If the current position is not at the end, remove all history items - // after the current item. - final int size = mArray.size(); - final int newPos = mCurrentIndex; - if (newPos != size) { - for (int i = size - 1; i >= newPos; i--) { - final WebHistoryItem h = mArray.remove(i); - } - } - // Add the item to the list. - mArray.add(item); - if (mCallbackProxy != null) { - mCallbackProxy.onNewHistoryItem(item); - } - } - - /** - * Clear the back/forward list. Called from the WebCore thread. - */ - /*package*/ synchronized void close(int nativeFrame) { - // Clear the array first because nativeClose will call addHistoryItem - // with the current item. - mArray.clear(); - mCurrentIndex = -1; - nativeClose(nativeFrame); - // Reset the clear flag - mClearPending = false; - } - - /* Remove the item at the given index. Called by JNI only. */ - private synchronized void removeHistoryItem(int index) { - // XXX: This is a special case. Since the callback is only triggered - // when removing the first item, we can assert that the index is 0. - // This lets us change the current index without having to query the - // native BackForwardList. - if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) { - throw new AssertionError(); - } - final WebHistoryItem h = mArray.remove(index); - // XXX: If we ever add another callback for removing history items at - // any index, this will no longer be valid. - mCurrentIndex--; + throw new MustOverrideException(); } /** @@ -158,39 +72,7 @@ public class WebBackForwardList implements Cloneable, Serializable { * webkit package classes. */ protected synchronized WebBackForwardList clone() { - WebBackForwardList l = new WebBackForwardList(null); - if (mClearPending) { - // If a clear is pending, return a copy with only the current item. - l.addHistoryItem(getCurrentItem()); - return l; - } - l.mCurrentIndex = mCurrentIndex; - int size = getSize(); - l.mArray = new ArrayList<WebHistoryItem>(size); - for (int i = 0; i < size; i++) { - // Add a copy of each WebHistoryItem - l.mArray.add(mArray.get(i).clone()); - } - return l; - } - - /** - * Set the new history index. - * @param newIndex The new history index. - */ - /*package*/ synchronized void setCurrentIndex(int newIndex) { - mCurrentIndex = newIndex; - if (mCallbackProxy != null) { - mCallbackProxy.onIndexChanged(getItemAtIndex(newIndex), newIndex); - } + throw new MustOverrideException(); } - /** - * Restore the history index. - */ - /*package*/ static native synchronized void restoreIndex(int nativeFrame, - int index); - - /* Close the native list. */ - private static native void nativeClose(int nativeFrame); } diff --git a/core/java/android/webkit/WebBackForwardListClassic.java b/core/java/android/webkit/WebBackForwardListClassic.java new file mode 100644 index 0000000..2a14e6b --- /dev/null +++ b/core/java/android/webkit/WebBackForwardListClassic.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import java.io.Serializable; +import java.util.ArrayList; + +/* package */ class WebBackForwardListClassic extends WebBackForwardList implements Cloneable, + Serializable { + + // Current position in the list. + private int mCurrentIndex; + // ArrayList of WebHistoryItems for maintaining our copy. + private ArrayList<WebHistoryItemClassic> mArray; + // Flag to indicate that the list is invalid + private boolean mClearPending; + // CallbackProxy to issue client callbacks. + private final CallbackProxy mCallbackProxy; + + /*package*/ WebBackForwardListClassic(CallbackProxy proxy) { + mCurrentIndex = -1; + mArray = new ArrayList<WebHistoryItemClassic>(); + mCallbackProxy = proxy; + } + + public synchronized WebHistoryItemClassic getCurrentItem() { + return getItemAtIndex(mCurrentIndex); + } + + public synchronized int getCurrentIndex() { + return mCurrentIndex; + } + + public synchronized WebHistoryItemClassic getItemAtIndex(int index) { + if (index < 0 || index >= getSize()) { + return null; + } + return mArray.get(index); + } + + public synchronized int getSize() { + return mArray.size(); + } + + /** + * Mark the back/forward list as having a pending clear. This is used on the + * UI side to mark the list as being invalid during the clearHistory method. + */ + /*package*/ synchronized void setClearPending() { + mClearPending = true; + } + + /** + * Return the status of the clear flag. This is used on the UI side to + * determine if the list is valid for checking things like canGoBack. + */ + /*package*/ synchronized boolean getClearPending() { + return mClearPending; + } + + /** + * Add a new history item to the list. This will remove all items after the + * current item and append the new item to the end of the list. Called from + * the WebCore thread only. Synchronized because the UI thread may be + * reading the array or the current index. + * @param item A new history item. + */ + /*package*/ synchronized void addHistoryItem(WebHistoryItem item) { + // Update the current position because we are going to add the new item + // in that slot. + ++mCurrentIndex; + // If the current position is not at the end, remove all history items + // after the current item. + final int size = mArray.size(); + final int newPos = mCurrentIndex; + if (newPos != size) { + for (int i = size - 1; i >= newPos; i--) { + final WebHistoryItem h = mArray.remove(i); + } + } + // Add the item to the list. + mArray.add((WebHistoryItemClassic) item); + if (mCallbackProxy != null) { + mCallbackProxy.onNewHistoryItem(item); + } + } + + /** + * Clear the back/forward list. Called from the WebCore thread. + */ + /*package*/ synchronized void close(int nativeFrame) { + // Clear the array first because nativeClose will call addHistoryItem + // with the current item. + mArray.clear(); + mCurrentIndex = -1; + nativeClose(nativeFrame); + // Reset the clear flag + mClearPending = false; + } + + /* Remove the item at the given index. Called by JNI only. */ + private synchronized void removeHistoryItem(int index) { + // XXX: This is a special case. Since the callback is only triggered + // when removing the first item, we can assert that the index is 0. + // This lets us change the current index without having to query the + // native BackForwardList. + if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) { + throw new AssertionError(); + } + final WebHistoryItem h = mArray.remove(index); + // XXX: If we ever add another callback for removing history items at + // any index, this will no longer be valid. + mCurrentIndex--; + } + + public synchronized WebBackForwardListClassic clone() { + WebBackForwardListClassic l = new WebBackForwardListClassic(null); + if (mClearPending) { + // If a clear is pending, return a copy with only the current item. + l.addHistoryItem(getCurrentItem()); + return l; + } + l.mCurrentIndex = mCurrentIndex; + int size = getSize(); + l.mArray = new ArrayList<WebHistoryItemClassic>(size); + for (int i = 0; i < size; i++) { + // Add a copy of each WebHistoryItem + l.mArray.add(mArray.get(i).clone()); + } + return l; + } + + /** + * Set the new history index. + * @param newIndex The new history index. + */ + /*package*/ synchronized void setCurrentIndex(int newIndex) { + mCurrentIndex = newIndex; + if (mCallbackProxy != null) { + mCallbackProxy.onIndexChanged(getItemAtIndex(newIndex), newIndex); + } + } + + /** + * Restore the history index. + */ + /*package*/ static native synchronized void restoreIndex(int nativeFrame, + int index); + + /* Close the native list. */ + private static native void nativeClose(int nativeFrame); +} diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 4e8790b..01c047b 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -297,7 +297,12 @@ public class WebChromeClient { * will continue to occur if the script does not finish at the next check * point. * @return boolean Whether the JavaScript execution should be interrupted. + * @deprecated This method is no longer supported and will not be invoked. */ + // This method was only called when using the JSC javascript engine. V8 became + // the default JS engine with Froyo and support for building with JSC was + // removed in b/5495373. V8 does not have a mechanism for making a callback such + // as this. public boolean onJsTimeout() { return true; } @@ -372,13 +377,6 @@ public class WebChromeClient { } /** - * Tell the client that the page being viewed is web app capable, - * i.e. has specified the fullscreen-web-app-capable meta tag. - * @hide - */ - public void setInstallableWebApp() { } - - /** * Tell the client that the page being viewed has an autofillable * form and the user would like to set a profile up. * @param msg A Message to send once the user has successfully diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java index 788d05c..3e0b177 100644 --- a/core/java/android/webkit/WebHistoryItem.java +++ b/core/java/android/webkit/WebHistoryItem.java @@ -18,9 +18,6 @@ package android.webkit; import android.graphics.Bitmap; -import java.net.MalformedURLException; -import java.net.URL; - /** * A convenience class for accessing fields in an entry in the back/forward list * of a WebView. Each WebHistoryItem is a snapshot of the requested history @@ -28,67 +25,8 @@ import java.net.URL; * @see WebBackForwardList */ public class WebHistoryItem implements Cloneable { - // Global identifier count. - private static int sNextId = 0; - // Unique identifier. - private final int mId; - // A point to a native WebHistoryItem instance which contains the actual data - private int mNativeBridge; - // The favicon for this item. - private Bitmap mFavicon; - // The pre-flattened data used for saving the state. - private byte[] mFlattenedData; - // The apple-touch-icon url for use when adding the site to the home screen, - // as obtained from a <link> element in the page. - private String mTouchIconUrlFromLink; - // If no <link> is specified, this holds the default location of the - // apple-touch-icon. - private String mTouchIconUrlServerDefault; - // Custom client data that is not flattened or read by native code. - private Object mCustomData; - - /** - * Basic constructor that assigns a unique id to the item. Called by JNI - * only. - */ - private WebHistoryItem(int nativeBridge) { - synchronized (WebHistoryItem.class) { - mId = sNextId++; - } - mNativeBridge = nativeBridge; - nativeRef(mNativeBridge); - } - - protected void finalize() throws Throwable { - if (mNativeBridge != 0) { - nativeUnref(mNativeBridge); - mNativeBridge = 0; - } - } - /** - * Construct a new WebHistoryItem with initial flattened data. - * @param data The pre-flattened data coming from restoreState. - */ - /*package*/ WebHistoryItem(byte[] data) { - mFlattenedData = data; - synchronized (WebHistoryItem.class) { - mId = sNextId++; - } - } - - /** - * Construct a clone of a WebHistoryItem from the given item. - * @param item The history item to clone. - */ - private WebHistoryItem(WebHistoryItem item) { - mFlattenedData = item.mFlattenedData; - mId = item.mId; - mFavicon = item.mFavicon; - mNativeBridge = item.mNativeBridge; - if (mNativeBridge != 0) { - nativeRef(mNativeBridge); - } + /* package */ WebHistoryItem() { } /** @@ -100,7 +38,7 @@ public class WebHistoryItem implements Cloneable { */ @Deprecated public int getId() { - return mId; + throw new MustOverrideException(); } /** @@ -112,8 +50,7 @@ public class WebHistoryItem implements Cloneable { * to synchronize this method. */ public String getUrl() { - if (mNativeBridge == 0) return null; - return nativeGetUrl(mNativeBridge); + throw new MustOverrideException(); } /** @@ -123,8 +60,7 @@ public class WebHistoryItem implements Cloneable { * @return The original url of this history item. */ public String getOriginalUrl() { - if (mNativeBridge == 0) return null; - return nativeGetOriginalUrl(mNativeBridge); + throw new MustOverrideException(); } /** @@ -134,8 +70,7 @@ public class WebHistoryItem implements Cloneable { * to synchronize this method. */ public String getTitle() { - if (mNativeBridge == 0) return null; - return nativeGetTitle(mNativeBridge); + throw new MustOverrideException(); } /** @@ -145,119 +80,14 @@ public class WebHistoryItem implements Cloneable { * to synchronize this method. */ public Bitmap getFavicon() { - if (mFavicon == null && mNativeBridge != 0) { - mFavicon = nativeGetFavicon(mNativeBridge); - } - return mFavicon; - } - - /** - * Return the touch icon url. - * If no touch icon <link> tag was specified, returns - * <host>/apple-touch-icon.png. The DownloadTouchIcon class that - * attempts to retrieve the touch icon will handle the case where - * that file does not exist. An icon set by a <link> tag is always - * used in preference to an icon saved on the server. - * @hide - */ - public String getTouchIconUrl() { - if (mTouchIconUrlFromLink != null) { - return mTouchIconUrlFromLink; - } else if (mTouchIconUrlServerDefault != null) { - return mTouchIconUrlServerDefault; - } - - try { - URL url = new URL(getOriginalUrl()); - mTouchIconUrlServerDefault = new URL(url.getProtocol(), url.getHost(), url.getPort(), - "/apple-touch-icon.png").toString(); - } catch (MalformedURLException e) { - return null; - } - return mTouchIconUrlServerDefault; - } - - /** - * Return the custom data provided by the client. - * @hide - */ - public Object getCustomData() { - return mCustomData; - } - - /** - * Set the custom data field. - * @param data An Object containing any data the client wishes to associate - * with the item. - * @hide - */ - public void setCustomData(Object data) { - // NOTE: WebHistoryItems are used in multiple threads. However, the - // public facing apis are all getters with the exception of this one - // api. Since this api is exclusive to clients, we don't make any - // promises about thread safety. - mCustomData = data; - } - - /** - * Set the favicon. - * @param icon A Bitmap containing the favicon for this history item. - * Note: The VM ensures 32-bit atomic read/write operations so we don't have - * to synchronize this method. - */ - /*package*/ void setFavicon(Bitmap icon) { - mFavicon = icon; - } - - /** - * Set the touch icon url. Will not overwrite an icon that has been - * set already from a <link> tag, unless the new icon is precomposed. - * @hide - */ - /*package*/ void setTouchIconUrl(String url, boolean precomposed) { - if (precomposed || mTouchIconUrlFromLink == null) { - mTouchIconUrlFromLink = url; - } - } - - /** - * Get the pre-flattened data. - * Note: The VM ensures 32-bit atomic read/write operations so we don't have - * to synchronize this method. - */ - /*package*/ byte[] getFlattenedData() { - if (mNativeBridge != 0) { - return nativeGetFlattenedData(mNativeBridge); - } - return mFlattenedData; - } - - /** - * Inflate this item. - * Note: The VM ensures 32-bit atomic read/write operations so we don't have - * to synchronize this method. - */ - /*package*/ void inflate(int nativeFrame) { - mNativeBridge = inflate(nativeFrame, mFlattenedData); - mFlattenedData = null; + throw new MustOverrideException(); } /** * Clone the history item for use by clients of WebView. */ protected synchronized WebHistoryItem clone() { - return new WebHistoryItem(this); + throw new MustOverrideException(); } - /* Natively inflate this item, this method is called in the WebCore thread. - */ - private native int inflate(int nativeFrame, byte[] data); - private native void nativeRef(int nptr); - private native void nativeUnref(int nptr); - private native String nativeGetTitle(int nptr); - private native String nativeGetUrl(int nptr); - private native String nativeGetOriginalUrl(int nptr); - private native byte[] nativeGetFlattenedData(int nptr); - private native Bitmap nativeGetFavicon(int nptr); - } diff --git a/core/java/android/webkit/WebHistoryItemClassic.java b/core/java/android/webkit/WebHistoryItemClassic.java new file mode 100644 index 0000000..1620fbf --- /dev/null +++ b/core/java/android/webkit/WebHistoryItemClassic.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.graphics.Bitmap; + +import java.net.MalformedURLException; +import java.net.URL; + +/* package */ class WebHistoryItemClassic extends WebHistoryItem implements Cloneable { + // Global identifier count. + private static int sNextId = 0; + // Unique identifier. + private final int mId; + // A point to a native WebHistoryItem instance which contains the actual data + private int mNativeBridge; + // The favicon for this item. + private Bitmap mFavicon; + // The pre-flattened data used for saving the state. + private byte[] mFlattenedData; + // The apple-touch-icon url for use when adding the site to the home screen, + // as obtained from a <link> element in the page. + private String mTouchIconUrlFromLink; + // If no <link> is specified, this holds the default location of the + // apple-touch-icon. + private String mTouchIconUrlServerDefault; + // Custom client data that is not flattened or read by native code. + private Object mCustomData; + + /** + * Basic constructor that assigns a unique id to the item. Called by JNI + * only. + */ + private WebHistoryItemClassic(int nativeBridge) { + synchronized (WebHistoryItemClassic.class) { + mId = sNextId++; + } + mNativeBridge = nativeBridge; + nativeRef(mNativeBridge); + } + + protected void finalize() throws Throwable { + if (mNativeBridge != 0) { + nativeUnref(mNativeBridge); + mNativeBridge = 0; + } + } + + /** + * Construct a new WebHistoryItem with initial flattened data. + * @param data The pre-flattened data coming from restoreState. + */ + /*package*/ WebHistoryItemClassic(byte[] data) { + mFlattenedData = data; + synchronized (WebHistoryItemClassic.class) { + mId = sNextId++; + } + } + + /** + * Construct a clone of a WebHistoryItem from the given item. + * @param item The history item to clone. + */ + private WebHistoryItemClassic(WebHistoryItemClassic item) { + mFlattenedData = item.mFlattenedData; + mId = item.mId; + mFavicon = item.mFavicon; + mNativeBridge = item.mNativeBridge; + if (mNativeBridge != 0) { + nativeRef(mNativeBridge); + } + } + + @Deprecated + public int getId() { + return mId; + } + + public String getUrl() { + if (mNativeBridge == 0) return null; + return nativeGetUrl(mNativeBridge); + } + + public String getOriginalUrl() { + if (mNativeBridge == 0) return null; + return nativeGetOriginalUrl(mNativeBridge); + } + + public String getTitle() { + if (mNativeBridge == 0) return null; + return nativeGetTitle(mNativeBridge); + } + + public Bitmap getFavicon() { + if (mFavicon == null && mNativeBridge != 0) { + mFavicon = nativeGetFavicon(mNativeBridge); + } + return mFavicon; + } + + /** + * Return the touch icon url. + * If no touch icon <link> tag was specified, returns + * <host>/apple-touch-icon.png. The DownloadTouchIcon class that + * attempts to retrieve the touch icon will handle the case where + * that file does not exist. An icon set by a <link> tag is always + * used in preference to an icon saved on the server. + * @hide + */ + public String getTouchIconUrl() { + if (mTouchIconUrlFromLink != null) { + return mTouchIconUrlFromLink; + } else if (mTouchIconUrlServerDefault != null) { + return mTouchIconUrlServerDefault; + } + + try { + URL url = new URL(getOriginalUrl()); + mTouchIconUrlServerDefault = new URL(url.getProtocol(), url.getHost(), url.getPort(), + "/apple-touch-icon.png").toString(); + } catch (MalformedURLException e) { + return null; + } + return mTouchIconUrlServerDefault; + } + + /** + * Return the custom data provided by the client. + * @hide + */ + public Object getCustomData() { + return mCustomData; + } + + /** + * Set the custom data field. + * @param data An Object containing any data the client wishes to associate + * with the item. + * @hide + */ + public void setCustomData(Object data) { + // NOTE: WebHistoryItems are used in multiple threads. However, the + // public facing apis are all getters with the exception of this one + // api. Since this api is exclusive to clients, we don't make any + // promises about thread safety. + mCustomData = data; + } + + /** + * Set the favicon. + * @param icon A Bitmap containing the favicon for this history item. + * Note: The VM ensures 32-bit atomic read/write operations so we don't have + * to synchronize this method. + */ + /*package*/ void setFavicon(Bitmap icon) { + mFavicon = icon; + } + + /** + * Set the touch icon url. Will not overwrite an icon that has been + * set already from a <link> tag, unless the new icon is precomposed. + * @hide + */ + /*package*/ void setTouchIconUrl(String url, boolean precomposed) { + if (precomposed || mTouchIconUrlFromLink == null) { + mTouchIconUrlFromLink = url; + } + } + + /** + * Get the pre-flattened data. + * Note: The VM ensures 32-bit atomic read/write operations so we don't have + * to synchronize this method. + */ + /*package*/ byte[] getFlattenedData() { + if (mNativeBridge != 0) { + return nativeGetFlattenedData(mNativeBridge); + } + return mFlattenedData; + } + + /** + * Inflate this item. + * Note: The VM ensures 32-bit atomic read/write operations so we don't have + * to synchronize this method. + */ + /*package*/ void inflate(int nativeFrame) { + mNativeBridge = inflate(nativeFrame, mFlattenedData); + mFlattenedData = null; + } + + public synchronized WebHistoryItemClassic clone() { + return new WebHistoryItemClassic(this); + } + + /* Natively inflate this item, this method is called in the WebCore thread. + */ + private native int inflate(int nativeFrame, byte[] data); + private native void nativeRef(int nptr); + private native void nativeUnref(int nptr); + private native String nativeGetTitle(int nptr); + private native String nativeGetUrl(int nptr); + private native String nativeGetOriginalUrl(int nptr); + private native byte[] nativeGetFlattenedData(int nptr); + private native Bitmap nativeGetFavicon(int nptr); + +} diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index fa3cb20..f2a041a 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -153,7 +153,8 @@ public abstract class WebSettings { } /** - * Enables dumping the pages navigation cache to a text file. + * Enables dumping the pages navigation cache to a text file. The default + * is false. * * @deprecated This method is now obsolete. */ @@ -165,6 +166,8 @@ public abstract class WebSettings { /** * Gets whether dumping the navigation cache is enabled. * + * @return whether dumping the navigation cache is enabled + * @see #setNavDump * @deprecated This method is now obsolete. */ @Deprecated @@ -285,14 +288,18 @@ public abstract class WebSettings { } /** - * Sets whether the WebView loads a page with overview mode. + * Sets whether the WebView loads pages in overview mode. The default is + * false. */ public void setLoadWithOverviewMode(boolean overview) { throw new MustOverrideException(); } /** - * Gets whether this WebView loads pages with overview mode. + * Gets whether this WebView loads pages in overview mode. + * + * @return whether this WebView loads pages in overview mode + * @see #setLoadWithOverviewMode */ public boolean getLoadWithOverviewMode() { throw new MustOverrideException(); @@ -344,38 +351,45 @@ public abstract class WebSettings { } /** - * Sets whether the WebView is saving form data. + * Sets whether the WebView should save form data. The default is true, + * unless in private browsing mode, when the value is always false. */ public void setSaveFormData(boolean save) { throw new MustOverrideException(); } /** - * Gets whether the WebView is saving form data and displaying prior - * entries/autofill++. Always false in private browsing mode. + * Gets whether the WebView saves form data. Always false in private + * browsing mode. + * + * @return whether the WebView saves form data + * @see #setSaveFormData */ public boolean getSaveFormData() { throw new MustOverrideException(); } /** - * Stores whether the WebView is saving password. + * Sets whether the WebView should save passwords. The default is true. */ public void setSavePassword(boolean save) { throw new MustOverrideException(); } /** - * Gets whether the WebView is saving password. + * Gets whether the WebView saves passwords. + * + * @return whether the WebView saves passwords + * @see #setSavePassword */ public boolean getSavePassword() { throw new MustOverrideException(); } /** - * Sets the text zoom of the page in percent. Default is 100. + * Sets the text zoom of the page in percent. The default is 100. * - * @param textZoom the percent value for increasing or decreasing the text + * @param textZoom the text zoom in percent */ public synchronized void setTextZoom(int textZoom) { throw new MustOverrideException(); @@ -384,7 +398,7 @@ public abstract class WebSettings { /** * Gets the text zoom of the page in percent. * - * @return a percent value describing the text zoom + * @return the text zoom of the page in percent * @see #setTextSizeZoom */ public synchronized int getTextZoom() { @@ -392,11 +406,10 @@ public abstract class WebSettings { } /** - * Sets the text size of the page. + * Sets the text size of the page. The default is {@link TextSize#NORMAL}. * - * @param t the TextSize value for increasing or decreasing the text - * @see WebSettings.TextSize - * @deprecated Use {@link #setTextZoom(int)} instead. + * @param t the text size as a {@link TextSize} value + * @deprecated Use {@link #setTextZoom} instead. */ public synchronized void setTextSize(TextSize t) { throw new MustOverrideException(); @@ -404,33 +417,33 @@ public abstract class WebSettings { /** * Gets the text size of the page. If the text size was previously specified - * in percent using {@link #setTextZoom(int)}, this will return - * the closest matching {@link TextSize}. + * in percent using {@link #setTextZoom}, this will return the closest + * matching {@link TextSize}. * - * @return a TextSize enum value describing the text size - * @see WebSettings.TextSize - * @deprecated Use {@link #getTextZoom()} instead. + * @return the text size as a {@link TextSize} value + * @see #setTextSize + * @deprecated Use {@link #getTextZoom} instead. */ public synchronized TextSize getTextSize() { throw new MustOverrideException(); } /** - * Sets the default zoom density of the page. This should be called from UI - * thread. + * Sets the default zoom density of the page. This must be called from the UI + * thread. The default is {@link ZoomDensity#MEDIUM}. * - * @param zoom a ZoomDensity value - * @see WebSettings.ZoomDensity + * @param zoom the zoom density */ public void setDefaultZoom(ZoomDensity zoom) { throw new MustOverrideException(); } /** - * Gets the default zoom density of the page. This should be called from UI - * thread. - * @return a ZoomDensity value - * @see WebSettings.ZoomDensity + * Gets the default zoom density of the page. This should be called from + * the UI thread. + * + * @return the zoom density + * @see #setDefaultZoom */ public ZoomDensity getDefaultZoom() { throw new MustOverrideException(); @@ -438,6 +451,7 @@ public abstract class WebSettings { /** * Enables using light touches to make a selection and activate mouseovers. + * The default is false. */ public void setLightTouchEnabled(boolean enabled) { throw new MustOverrideException(); @@ -445,6 +459,9 @@ public abstract class WebSettings { /** * Gets whether light touches are enabled. + * + * @return whether light touches are enabled + * @see #setLightTouchEnabled */ public boolean getLightTouchEnabled() { throw new MustOverrideException(); @@ -474,11 +491,16 @@ public abstract class WebSettings { } /** - * Tells the WebView about user-agent string. + * Sets the user-agent string using an integer code. + * <ul> + * <li>0 means the WebView should use an Android user-agent string</li> + * <li>1 means the WebView should use a desktop user-agent string</li> + * </ul> + * Other values are ignored. The default is an Android user-agent string, + * i.e. code value 0. * - * @param ua 0 if the WebView should use an Android user-agent string, - * 1 if the WebView should use a desktop user-agent string - * @deprecated Please use setUserAgentString instead. + * @param ua the integer code for the user-agent string + * @deprecated Please use {@link #setUserAgentString} instead. */ @Deprecated public synchronized void setUserAgent(int ua) { @@ -486,12 +508,17 @@ public abstract class WebSettings { } /** - * Gets the user-agent as an int. + * Gets the user-agent as an integer code. + * <ul> + * <li>-1 means the WebView is using a custom user-agent string set with + * {@link #setUserAgentString}</li> + * <li>0 means the WebView should use an Android user-agent string</li> + * <li>1 means the WebView should use a desktop user-agent string</li> + * </ul> * - * @return 0 if the WebView is using an Android user-agent string, - * 1 if the WebView is using a desktop user-agent string, - * -1 if the WebView is using user defined user-agent string - * @deprecated Please use getUserAgentString instead. + * @return the integer code for the user-agent string + * @see #setUserAgent + * @deprecated Please use {@link #getUserAgentString} instead. */ @Deprecated public synchronized int getUserAgent() { @@ -499,7 +526,9 @@ public abstract class WebSettings { } /** - * Tells the WebView to use the wide viewport. + * Tells the WebView to use a wide viewport. The default is false. + * + * @param use whether to use a wide viewport */ public synchronized void setUseWideViewPort(boolean use) { throw new MustOverrideException(); @@ -509,26 +538,28 @@ public abstract class WebSettings { * Gets whether the WebView is using a wide viewport. * * @return true if the WebView is using a wide viewport + * @see #setUseWideViewPort */ public synchronized boolean getUseWideViewPort() { throw new MustOverrideException(); } /** - * Tells the WebView whether it supports multiple windows. TRUE means - * that {@link WebChromeClient#onCreateWindow(WebView, boolean, - * boolean, Message)} is implemented by the host application. + * Sets whether the WebView whether supports multiple windows. If set to + * true, {@link WebChromeClient#onCreateWindow} must be implemented by the + * host application. The default is false. + * + * @param support whether to suport multiple windows */ public synchronized void setSupportMultipleWindows(boolean support) { throw new MustOverrideException(); } /** - * Gets whether the WebView is supporting multiple windows. + * Gets whether the WebView supports multiple windows. * - * @return true if the WebView is supporting multiple windows. This means - * that {@link WebChromeClient#onCreateWindow(WebView, boolean, - * boolean, Message)} is implemented by the host application. + * @return true if the WebView supports multiple windows + * @see #setSupportMultipleWindows */ public synchronized boolean supportMultipleWindows() { throw new MustOverrideException(); @@ -536,10 +567,9 @@ public abstract class WebSettings { /** * Sets the underlying layout algorithm. This will cause a relayout of the - * WebView. The default is NARROW_COLUMNS. + * WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}. * - * @param l a LayoutAlgorithm enum specifying the algorithm to use - * @see WebSettings.LayoutAlgorithm + * @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value */ public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) { throw new MustOverrideException(); @@ -548,10 +578,8 @@ public abstract class WebSettings { /** * Gets the current layout algorithm. * - * @return a LayoutAlgorithm enum value describing the layout algorithm - * being used + * @return the layout algorithm in use, as a {@link LayoutAlgorithm} value * @see #setLayoutAlgorithm - * @see WebSettings.LayoutAlgorithm */ public synchronized LayoutAlgorithm getLayoutAlgorithm() { throw new MustOverrideException(); @@ -596,7 +624,7 @@ public abstract class WebSettings { } /** - * Sets the sans-serif font family name. + * Sets the sans-serif font family name. The default is "sans-serif". * * @param font a font family name */ @@ -608,6 +636,7 @@ public abstract class WebSettings { * Gets the sans-serif font family name. * * @return the sans-serif font family name as a string + * @see #setSansSerifFontFamily */ public synchronized String getSansSerifFontFamily() { throw new MustOverrideException(); @@ -883,9 +912,9 @@ public abstract class WebSettings { public abstract void setAllowFileAccessFromFileURLs(boolean flag); /** - * Tells the WebView to enable plugins. + * Sets whether the WebView should enable plugins. The default is false. * - * @param flag true if the WebView should load plugins + * @param flag true if plugins should be enabled * @deprecated This method has been deprecated in favor of * {@link #setPluginState} */ @@ -898,7 +927,8 @@ public abstract class WebSettings { * Tells the WebView to enable, disable, or have plugins on demand. On * demand mode means that if a plugin exists that can handle the embedded * content, a placeholder icon will be shown instead of the plugin. When - * the placeholder is clicked, the plugin will be enabled. + * the placeholder is clicked, the plugin will be enabled. The default is + * {@link PluginState#OFF}. * * @param state a PluginState value */ @@ -921,23 +951,27 @@ public abstract class WebSettings { /** * Sets the path to where database storage API databases should be saved. - * Note that the WebCore Database Tracker only allows the path to be set once. + * In order for the database storage API to function correctly, this method + * must be called with a path to which the application can write. This + * method should only be called once: repeated calls are ignored. * - * @param databasePath a String path to the directory where databases should - * be saved. May be the empty string but should never - * be null. + * @param databasePath a path to the directory where databases should be + * saved. */ // This will update WebCore when the Sync runs in the C++ side. + // Note that the WebCore Database Tracker only allows the path to be set + // once. public synchronized void setDatabasePath(String databasePath) { throw new MustOverrideException(); } /** - * Sets the path where the Geolocation permissions database should be saved. + * Sets the path where the Geolocation databases should be saved. In order + * for Geolocation permissions and cached positions to be persisted, this + * method must be called with a path to which the application can write. * - * @param databasePath a String path to the directory where the Geolocation - * permissions database should be saved. May be the - * empty string but should never be null. + * @param databasePath a path to the directory where databases should be + * saved. */ // This will update WebCore when the Sync runs in the C++ side. public synchronized void setGeolocationDatabasePath(String databasePath) { @@ -945,7 +979,10 @@ public abstract class WebSettings { } /** - * Tells the WebView to enable Application Caches API. + * Sets whether the Application Caches API should be enabled. The default + * is false. Note that in order for the Application Caches API to be + * enabled, a valid database path must also be supplied to + * {@link #setAppCachePath}. * * @param flag true if the WebView should enable Application Caches */ @@ -954,20 +991,22 @@ public abstract class WebSettings { } /** - * Sets a custom path to the Application Caches files. The client - * must ensure it exists before this call. + * Sets the path to the Application Caches files. In order for the + * Application Caches API to be enabled, this method must be called with a + * path to which the application can write. This method should only be + * called once: repeated calls are ignored. * * @param appCachePath a String path to the directory containing - * Application Caches files. The appCache path can be - * the empty string but should not be null. Passing - * null for this parameter will result in a no-op. + * Application Caches files. + * @see setAppCacheEnabled */ public synchronized void setAppCachePath(String appCachePath) { throw new MustOverrideException(); } /** - * Sets the maximum size for the Application Caches content. + * Sets the maximum size for the Application Caches content. The default is + * {@link Long#MAX_VALUE}. * * @param appCacheMaxSize the maximum size in bytes */ @@ -976,7 +1015,9 @@ public abstract class WebSettings { } /** - * Sets whether the database storage API is enabled. + * Sets whether the database storage API is enabled. The default value is + * false. See also {@link #setDatabasePath} for how to correctly set up the + * database storage API. * * @param flag true if the WebView should use the database storage API */ @@ -985,7 +1026,7 @@ public abstract class WebSettings { } /** - * Sets whether the DOM storage API is enabled. + * Sets whether the DOM storage API is enabled. The default value is false. * * @param flag true if the WebView should use the DOM storage API */ @@ -997,15 +1038,16 @@ public abstract class WebSettings { * Gets whether the DOM Storage APIs are enabled. * * @return true if the DOM Storage APIs are enabled + * @see #setDomStorageEnabled */ public synchronized boolean getDomStorageEnabled() { throw new MustOverrideException(); } /** - * Gets the path to where database storage API databases are saved for - * the current WebView. + * Gets the path to where database storage API databases are saved. * * @return the String path to the database storage API databases + * @see #setDatabasePath */ public synchronized String getDatabasePath() { throw new MustOverrideException(); @@ -1015,13 +1057,16 @@ public abstract class WebSettings { * Gets whether the database storage API is enabled. * * @return true if the database storage API is enabled + * @see #setDatabaseEnabled */ public synchronized boolean getDatabaseEnabled() { throw new MustOverrideException(); } /** - * Sets whether Geolocation is enabled. + * Sets whether Geolocation is enabled. The default is true. See also + * {@link #setGeolocationDatabasePath} for how to correctly set up + * Geolocation. * * @param flag whether Geolocation should be enabled */ @@ -1064,6 +1109,7 @@ public abstract class WebSettings { * Gets whether plugins are enabled. * * @return true if plugins are enabled + * @see #setPluginsEnabled * @deprecated This method has been replaced by {@link #getPluginState} */ @Deprecated @@ -1072,9 +1118,10 @@ public abstract class WebSettings { } /** - * Gets the current plugin state. + * Gets the current state regarding whether plugins are enabled. * - * @return a value corresponding to the enum PluginState + * @return the plugin state as a {@link PluginState} value + * @see #setPluginState */ public synchronized PluginState getPluginState() { throw new MustOverrideException(); @@ -1135,8 +1182,8 @@ public abstract class WebSettings { } /** - * Sets the WebView's user-agent string. If the string "ua" is null or empty, - * it will use the system default user-agent string. + * Sets the WebView's user-agent string. If the string is null or empty, + * the system default value will be used. */ public synchronized void setUserAgentString(String ua) { throw new MustOverrideException(); @@ -1144,6 +1191,9 @@ public abstract class WebSettings { /** * Gets the WebView's user-agent string. + * + * @return the WebView's user-agent string + * @see #setUserAgentString */ public synchronized String getUserAgentString() { throw new MustOverrideException(); @@ -1151,7 +1201,8 @@ public abstract class WebSettings { /** * Tells the WebView whether it needs to set a node to have focus when - * {@link WebView#requestFocus(int, android.graphics.Rect)} is called. + * {@link WebView#requestFocus(int, android.graphics.Rect)} is called. The + * default value is true. * * @param flag whether the WebView needs to set a node */ @@ -1161,9 +1212,10 @@ public abstract class WebSettings { /** * Sets the priority of the Render thread. Unlike the other settings, this - * one only needs to be called once per process. The default is NORMAL. + * one only needs to be called once per process. The default value is + * {@link RenderPriority#NORMAL}. * - * @param priority a RenderPriority + * @param priority the priority */ public synchronized void setRenderPriority(RenderPriority priority) { throw new MustOverrideException(); @@ -1171,20 +1223,25 @@ public abstract class WebSettings { /** * Overrides the way the cache is used. The way the cache is used is based - * on the navigation option. For a normal page load, the cache is checked + * on the navigation type. For a normal page load, the cache is checked * and content is re-validated as needed. When navigating back, content is - * not revalidated, instead the content is just pulled from the cache. - * This function allows the client to override this behavior. + * not revalidated, instead the content is just retrieved from the cache. + * This method allows the client to override this behavior by specifying + * one of {@link #LOAD_DEFAULT}, {@link #LOAD_NORMAL}, + * {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or + * {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}. * - * @param mode one of the LOAD_ values + * @param mode the mode to use */ public void setCacheMode(int mode) { throw new MustOverrideException(); } /** - * Gets the current setting for overriding the cache mode. For a full - * description, see the {@link #setCacheMode(int)} function. + * Gets the current setting for overriding the cache mode. + * + * @return the current setting for overriding the cache mode + * @see #setCacheMode */ public int getCacheMode() { throw new MustOverrideException(); diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java index 38b5e5c..d3ec603 100644 --- a/core/java/android/webkit/WebSyncManager.java +++ b/core/java/android/webkit/WebSyncManager.java @@ -37,9 +37,6 @@ abstract class WebSyncManager implements Runnable { // handler of the sync thread protected Handler mHandler; // database for the persistent storage - // Note that this remains uninitialised as it is unused. We cannot remove - // the member as it leaked into the public API via CookieSyncManager. - // TODO: hide this member, ditto for mHandler. protected WebViewDatabase mDataBase; // Ref count for calls to start/stop sync private int mStartSyncRefCount; @@ -65,6 +62,7 @@ abstract class WebSyncManager implements Runnable { protected WebSyncManager(Context context, String name) { mThreadName = name; if (context != null) { + mDataBase = WebViewDatabase.getInstance(context); mSyncThread = new Thread(this); mSyncThread.setName(mThreadName); mSyncThread.start(); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 119fcd3..2545cd8 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -35,8 +35,8 @@ import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewDebug; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -44,6 +44,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.AbsoluteLayout; +import java.io.BufferedWriter; import java.io.File; import java.util.Map; @@ -263,7 +264,7 @@ import java.util.Map; @Widget public class WebView extends AbsoluteLayout implements ViewTreeObserver.OnGlobalFocusChangeListener, - ViewGroup.OnHierarchyChangeListener { + ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler { private static final String LOGTAG = "webview_proxy"; @@ -1004,6 +1005,7 @@ public class WebView extends AbsoluteLayout * * @return the current scale */ + @ViewDebug.ExportedProperty(category = "webview") public float getScale() { checkThread(); return mProvider.getScale(); @@ -1094,6 +1096,7 @@ public class WebView extends AbsoluteLayout * * @return the URL for the current page */ + @ViewDebug.ExportedProperty(category = "webview") public String getUrl() { checkThread(); return mProvider.getUrl(); @@ -1108,6 +1111,7 @@ public class WebView extends AbsoluteLayout * * @return the URL that was originally requested for the current page */ + @ViewDebug.ExportedProperty(category = "webview") public String getOriginalUrl() { checkThread(); return mProvider.getOriginalUrl(); @@ -1119,6 +1123,7 @@ public class WebView extends AbsoluteLayout * * @return the title for the current page */ + @ViewDebug.ExportedProperty(category = "webview") public String getTitle() { checkThread(); return mProvider.getTitle(); @@ -1161,6 +1166,7 @@ public class WebView extends AbsoluteLayout * * @return the height of the HTML content */ + @ViewDebug.ExportedProperty(category = "webview") public int getContentHeight() { checkThread(); return mProvider.getContentHeight(); @@ -1172,6 +1178,7 @@ public class WebView extends AbsoluteLayout * @return the width of the HTML content * @hide */ + @ViewDebug.ExportedProperty(category = "webview") public int getContentWidth() { return mProvider.getContentWidth(); } @@ -1652,6 +1659,24 @@ public class WebView extends AbsoluteLayout mProvider.debugDump(); } + /** + * See {@link ViewDebug.HierarchyHandler#dumpViewHierarchyWithProperties(BufferedWriter, int)} + * @hide + */ + @Override + public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) { + mProvider.dumpViewHierarchyWithProperties(out, level); + } + + /** + * See {@link ViewDebug.HierarchyHandler#findHierarchyView(String, int)} + * @hide + */ + @Override + public View findHierarchyView(String className, int hashCode) { + return mProvider.findHierarchyView(className, hashCode); + } + //------------------------------------------------------------------------- // Interface for WebView providers //------------------------------------------------------------------------- diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 84a6129..5eefbe1 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -117,6 +117,8 @@ import android.widget.Toast; import junit.framework.Assert; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -132,6 +134,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; +import java.util.concurrent.CountDownLatch; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -679,6 +682,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // after resize. static private final int EDIT_RECT_BUFFER = 10; + static private final long SELECTION_HANDLE_ANIMATION_MS = 150; + // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK private boolean mAutoRedraw; @@ -945,21 +950,20 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; private Drawable mSelectHandleCenter; - private Point mSelectHandleLeftOffset; - private Point mSelectHandleRightOffset; - private Point mSelectHandleCenterOffset; - private Point mSelectCursorLeft = new Point(); - private int mSelectCursorLeftLayerId; - private QuadF mSelectCursorLeftTextQuad = new QuadF(); - private Point mSelectCursorRight = new Point(); - private int mSelectCursorRightLayerId; - private QuadF mSelectCursorRightTextQuad = new QuadF(); + private Point mSelectOffset; + private Point mSelectCursorBase = new Point(); + private Rect mSelectHandleBaseBounds = new Rect(); + private int mSelectCursorBaseLayerId; + private QuadF mSelectCursorBaseTextQuad = new QuadF(); + private Point mSelectCursorExtent = new Point(); + private Rect mSelectHandleExtentBounds = new Rect(); + private int mSelectCursorExtentLayerId; + private QuadF mSelectCursorExtentTextQuad = new QuadF(); private Point mSelectDraggingCursor; - private Point mSelectDraggingOffset; private QuadF mSelectDraggingTextQuad; private boolean mIsCaretSelection; - static final int HANDLE_ID_LEFT = 0; - static final int HANDLE_ID_RIGHT = 1; + static final int HANDLE_ID_BASE = 0; + static final int HANDLE_ID_EXTENT = 1; // the color used to highlight the touch rectangles static final int HIGHLIGHT_COLOR = 0x6633b5e5; @@ -1049,6 +1053,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc static final int EDIT_TEXT_SIZE_CHANGED = 150; static final int SHOW_CARET_HANDLE = 151; static final int UPDATE_CONTENT_BOUNDS = 152; + static final int SCROLL_HANDLE_INTO_VIEW = 153; private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT; @@ -2196,7 +2201,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } // We grab a copy of the back/forward list because a client of WebView // may have invalidated the history list by calling clearHistory. - WebBackForwardList list = copyBackForwardList(); + WebBackForwardListClassic list = copyBackForwardList(); final int currentIndex = list.getCurrentIndex(); final int size = list.getSize(); // We should fail saving the state if the list is empty or the index is @@ -2210,7 +2215,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // arrays. ArrayList<byte[]> history = new ArrayList<byte[]>(size); for (int i = 0; i < size; i++) { - WebHistoryItem item = list.getItemAtIndex(i); + WebHistoryItemClassic item = list.getItemAtIndex(i); if (null == item) { // FIXME: this shouldn't happen // need to determine how item got set to null @@ -2409,7 +2414,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ @Override public WebBackForwardList restoreState(Bundle inState) { - WebBackForwardList returnList = null; + WebBackForwardListClassic returnList = null; if (inState == null) { return returnList; } @@ -2417,7 +2422,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mCertificate = SslCertificate.restoreState( inState.getBundle("certificate")); - final WebBackForwardList list = mCallbackProxy.getBackForwardList(); + final WebBackForwardListClassic list = mCallbackProxy.getBackForwardList(); final int index = inState.getInt("index"); // We can't use a clone of the list because we need to modify the // shared copy, so synchronize instead to prevent concurrent @@ -2438,7 +2443,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // the item and thus our history list cannot be rebuilt. return null; } - WebHistoryItem item = new WebHistoryItem(data); + WebHistoryItem item = new WebHistoryItemClassic(data); list.addHistoryItem(item); } // Grab the most recent copy to return to the caller. @@ -2611,7 +2616,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ @Override public boolean canGoBack() { - WebBackForwardList l = mCallbackProxy.getBackForwardList(); + WebBackForwardListClassic l = mCallbackProxy.getBackForwardList(); synchronized (l) { if (l.getClearPending()) { return false; @@ -2634,7 +2639,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ @Override public boolean canGoForward() { - WebBackForwardList l = mCallbackProxy.getBackForwardList(); + WebBackForwardListClassic l = mCallbackProxy.getBackForwardList(); synchronized (l) { if (l.getClearPending()) { return false; @@ -2657,7 +2662,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ @Override public boolean canGoBackOrForward(int steps) { - WebBackForwardList l = mCallbackProxy.getBackForwardList(); + WebBackForwardListClassic l = mCallbackProxy.getBackForwardList(); synchronized (l) { if (l.getClearPending()) { return false; @@ -3305,6 +3310,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } if (mTouchMode == TOUCH_DRAG_LAYER_MODE) { scrollLayerTo(scrollX, scrollY); + animateHandles(); return; } mInOverScrollMode = false; @@ -3325,6 +3331,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mWebViewPrivate.super_scrollTo(scrollX, scrollY); + animateHandles(); + if (mOverScrollGlow != null) { mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY); } @@ -3371,7 +3379,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ @Override public String getTouchIconUrl() { - WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem(); + WebHistoryItemClassic h = mCallbackProxy.getBackForwardList().getCurrentItem(); return h != null ? h.getTouchIconUrl() : null; } @@ -3535,7 +3543,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc * See {@link WebView#copyBackForwardList()} */ @Override - public WebBackForwardList copyBackForwardList() { + public WebBackForwardListClassic copyBackForwardList() { return mCallbackProxy.getBackForwardList().clone(); } @@ -3819,17 +3827,14 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc return; } if (mSelectingText) { - if (mSelectCursorLeftLayerId == mCurrentScrollingLayerId) { - mSelectCursorLeft.offset(dx, dy); - mSelectCursorLeftTextQuad.offset(dx, dy); + if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) { + mSelectCursorBase.offset(dx, dy); + mSelectCursorBaseTextQuad.offset(dx, dy); } - if (mSelectCursorRightLayerId == mCurrentScrollingLayerId) { - mSelectCursorRight.offset(dx, dy); - mSelectCursorRightTextQuad.offset(dx, dy); + if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) { + mSelectCursorExtent.offset(dx, dy); + mSelectCursorExtentTextQuad.offset(dx, dy); } - } else if (mHandleAlpha.getAlpha() > 0) { - // stop fading as we're not going to move with the layer. - mHandleAlphaAnimator.end(); } if (mAutoCompletePopup != null && mCurrentScrollingLayerId == mEditTextLayerId) { @@ -4445,9 +4450,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } private void onZoomAnimationStart() { - if (!mSelectingText && mHandleAlpha.getAlpha() > 0) { - mHandleAlphaAnimator.end(); - } } private void onZoomAnimationEnd() { @@ -4480,34 +4482,63 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private class SelectionHandleAlpha { private int mAlpha = 0; + private int mTargetAlpha = 0; + public void setAlpha(int alpha) { mAlpha = alpha; - if (mSelectHandleCenter != null) { - mSelectHandleCenter.setAlpha(alpha); - mSelectHandleLeft.setAlpha(alpha); - mSelectHandleRight.setAlpha(alpha); - // TODO: Use partial invalidate - invalidate(); - } + // TODO: Use partial invalidate + invalidate(); } public int getAlpha() { return mAlpha; } + public void setTargetAlpha(int alpha) { + mTargetAlpha = alpha; + } + + public int getTargetAlpha() { + return mTargetAlpha; + } + } private void startSelectingText() { mSelectingText = true; mShowTextSelectionExtra = true; - mHandleAlphaAnimator.setIntValues(255); - mHandleAlphaAnimator.start(); + animateHandles(); + } + + private void animateHandle(boolean canShow, ObjectAnimator animator, + Point selectionPoint, int selectionLayerId, + SelectionHandleAlpha alpha) { + boolean isVisible = canShow && mSelectingText + && ((mSelectionStarted && mSelectDraggingCursor == selectionPoint) + || isHandleVisible(selectionPoint, selectionLayerId)); + int targetValue = isVisible ? 255 : 0; + if (targetValue != alpha.getTargetAlpha()) { + alpha.setTargetAlpha(targetValue); + animator.setIntValues(targetValue); + animator.setDuration(SELECTION_HANDLE_ANIMATION_MS); + animator.start(); + } + } + + private void animateHandles() { + boolean canShowBase = mSelectingText; + boolean canShowExtent = mSelectingText && !mIsCaretSelection; + animateHandle(canShowBase, mBaseHandleAlphaAnimator, mSelectCursorBase, + mSelectCursorBaseLayerId, mBaseAlpha); + animateHandle(canShowExtent, mExtentHandleAlphaAnimator, + mSelectCursorExtent, mSelectCursorExtentLayerId, + mExtentAlpha); } + private void endSelectingText() { mSelectingText = false; mShowTextSelectionExtra = false; - mHandleAlphaAnimator.setIntValues(0); - mHandleAlphaAnimator.start(); + animateHandles(); } private void ensureSelectionHandles() { @@ -4518,66 +4549,87 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc com.android.internal.R.drawable.text_select_handle_left); mSelectHandleRight = mContext.getResources().getDrawable( com.android.internal.R.drawable.text_select_handle_right); - mHandleAlpha.setAlpha(mHandleAlpha.getAlpha()); - mSelectHandleCenterOffset = new Point(0, - -mSelectHandleCenter.getIntrinsicHeight()); - mSelectHandleLeftOffset = new Point(0, + // All handles have the same height, so we can save effort with + // this assumption. + mSelectOffset = new Point(0, -mSelectHandleLeft.getIntrinsicHeight()); - mSelectHandleRightOffset = new Point( - -mSelectHandleLeft.getIntrinsicWidth() / 2, - -mSelectHandleRight.getIntrinsicHeight()); } } + private void drawHandle(Point point, int handleId, Rect bounds, + int alpha, Canvas canvas) { + int offset; + int width; + int height; + Drawable drawable; + boolean isLeft = nativeIsHandleLeft(mNativeClass, handleId); + if (isLeft) { + drawable = mSelectHandleLeft; + width = mSelectHandleLeft.getIntrinsicWidth(); + height = mSelectHandleLeft.getIntrinsicHeight(); + // Magic formula copied from TextView + offset = (width * 3) / 4; + } else { + drawable = mSelectHandleRight; + width = mSelectHandleRight.getIntrinsicWidth(); + height = mSelectHandleRight.getIntrinsicHeight(); + // Magic formula copied from TextView + offset = width / 4; + } + int x = contentToViewDimension(point.x); + int y = contentToViewDimension(point.y); + bounds.set(x - offset, y, x - offset + width, y + height); + drawable.setBounds(bounds); + drawable.setAlpha(alpha); + drawable.draw(canvas); + } + private void drawTextSelectionHandles(Canvas canvas) { - if (mHandleAlpha.getAlpha() == 0) { + if (mBaseAlpha.getAlpha() == 0 && mExtentAlpha.getAlpha() == 0) { return; } ensureSelectionHandles(); - if (mSelectingText) { - int[] handles = new int[4]; - getSelectionHandles(handles); - int start_x = contentToViewDimension(handles[0]); - int start_y = contentToViewDimension(handles[1]); - int end_x = contentToViewDimension(handles[2]); - int end_y = contentToViewDimension(handles[3]); - - if (mIsCaretSelection) { - // Caret handle is centered - start_x -= (mSelectHandleCenter.getIntrinsicWidth() / 2); - mSelectHandleCenter.setBounds(start_x, start_y, - start_x + mSelectHandleCenter.getIntrinsicWidth(), - start_y + mSelectHandleCenter.getIntrinsicHeight()); - } else { - // Magic formula copied from TextView - start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4; - mSelectHandleLeft.setBounds(start_x, start_y, - start_x + mSelectHandleLeft.getIntrinsicWidth(), - start_y + mSelectHandleLeft.getIntrinsicHeight()); - end_x -= mSelectHandleRight.getIntrinsicWidth() / 4; - mSelectHandleRight.setBounds(end_x, end_y, - end_x + mSelectHandleRight.getIntrinsicWidth(), - end_y + mSelectHandleRight.getIntrinsicHeight()); - } - } - if (mIsCaretSelection) { + // Caret handle is centered + int x = contentToViewDimension(mSelectCursorBase.x) - + (mSelectHandleCenter.getIntrinsicWidth() / 2); + int y = contentToViewDimension(mSelectCursorBase.y); + mSelectHandleBaseBounds.set(x, y, + x + mSelectHandleCenter.getIntrinsicWidth(), + y + mSelectHandleCenter.getIntrinsicHeight()); + mSelectHandleCenter.setBounds(mSelectHandleBaseBounds); + mSelectHandleCenter.setAlpha(mBaseAlpha.getAlpha()); mSelectHandleCenter.draw(canvas); } else { - mSelectHandleLeft.draw(canvas); - mSelectHandleRight.draw(canvas); + drawHandle(mSelectCursorBase, HANDLE_ID_BASE, + mSelectHandleBaseBounds, mBaseAlpha.getAlpha(), canvas); + drawHandle(mSelectCursorExtent, HANDLE_ID_EXTENT, + mSelectHandleExtentBounds, mExtentAlpha.getAlpha(), canvas); } } + private boolean isHandleVisible(Point selectionPoint, int layerId) { + boolean isVisible = true; + if (mIsEditingText) { + isVisible = mEditTextContentBounds.contains(selectionPoint.x, + selectionPoint.y); + } + if (isVisible) { + isVisible = nativeIsPointVisible(mNativeClass, layerId, + selectionPoint.x, selectionPoint.y); + } + return isVisible; + } + /** * Takes an int[4] array as an output param with the values being * startX, startY, endX, endY */ private void getSelectionHandles(int[] handles) { - handles[0] = mSelectCursorLeft.x; - handles[1] = mSelectCursorLeft.y; - handles[2] = mSelectCursorRight.x; - handles[3] = mSelectCursorRight.y; + handles[0] = mSelectCursorBase.x; + handles[1] = mSelectCursorBase.y; + handles[2] = mSelectCursorExtent.x; + handles[3] = mSelectCursorExtent.y; } // draw history @@ -4812,6 +4864,43 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } /** + * Sets use of the Geolocation mock client. Also resets that client. Called + * by DRT on UI thread, need to proxy to WebCore thread. + * + * debug only + */ + public void setUseMockGeolocation() { + mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_GEOLOCATION); + } + + /** + * Called by DRT on WebCore thread. + * + * debug only + */ + public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) { + mWebViewCore.setMockGeolocationPosition(latitude, longitude, accuracy); + } + + /** + * Called by DRT on WebCore thread. + * + * debug only + */ + public void setMockGeolocationError(int code, String message) { + mWebViewCore.setMockGeolocationError(code, message); + } + + /** + * Called by DRT on WebCore thread. + * + * debug only + */ + public void setMockGeolocationPermission(boolean allow) { + mWebViewCore.setMockGeolocationPermission(allow); + } + + /** * Called by DRT on WebCore thread. * * debug only @@ -5044,8 +5133,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc ClipboardManager cm = (ClipboardManager)(mContext .getSystemService(Context.CLIPBOARD_SERVICE)); if (cm.hasPrimaryClip()) { - Point cursorPoint = new Point(contentToViewX(mSelectCursorLeft.x), - contentToViewY(mSelectCursorLeft.y)); + Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x), + contentToViewY(mSelectCursorBase.y)); Point cursorTop = calculateCaretTop(); cursorTop.set(contentToViewX(cursorTop.x), contentToViewY(cursorTop.y)); @@ -5095,12 +5184,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc * calculates the top of a caret. */ private Point calculateCaretTop() { - float scale = scaleAlongSegment(mSelectCursorLeft.x, mSelectCursorLeft.y, - mSelectCursorLeftTextQuad.p4, mSelectCursorLeftTextQuad.p3); + float scale = scaleAlongSegment(mSelectCursorBase.x, mSelectCursorBase.y, + mSelectCursorBaseTextQuad.p4, mSelectCursorBaseTextQuad.p3); int x = Math.round(scaleCoordinate(scale, - mSelectCursorLeftTextQuad.p1.x, mSelectCursorLeftTextQuad.p2.x)); + mSelectCursorBaseTextQuad.p1.x, mSelectCursorBaseTextQuad.p2.x)); int y = Math.round(scaleCoordinate(scale, - mSelectCursorLeftTextQuad.p1.y, mSelectCursorLeftTextQuad.p2.y)); + mSelectCursorBaseTextQuad.p1.y, mSelectCursorBaseTextQuad.p2.y)); return new Point(x, y); } @@ -5111,50 +5200,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } private void syncSelectionCursors() { - mSelectCursorLeftLayerId = - nativeGetHandleLayerId(mNativeClass, HANDLE_ID_LEFT, - mSelectCursorLeft, mSelectCursorLeftTextQuad); - mSelectCursorRightLayerId = - nativeGetHandleLayerId(mNativeClass, HANDLE_ID_RIGHT, - mSelectCursorRight, mSelectCursorRightTextQuad); - } - - private void adjustSelectionCursors() { - if (mIsCaretSelection) { - syncSelectionCursors(); - return; // no need to swap left and right handles. - } - - boolean wasDraggingLeft = (mSelectDraggingCursor == mSelectCursorLeft); - int oldX = mSelectDraggingCursor.x; - int oldY = mSelectDraggingCursor.y; - int oldLeftX = mSelectCursorLeft.x; - int oldLeftY = mSelectCursorLeft.y; - int oldRightX = mSelectCursorRight.x; - int oldRightY = mSelectCursorRight.y; - syncSelectionCursors(); - - boolean rightChanged = (oldRightX != mSelectCursorRight.x - || oldRightY != mSelectCursorRight.y); - boolean leftChanged = (oldLeftX != mSelectCursorLeft.x - || oldLeftY != mSelectCursorLeft.y); - if (leftChanged && rightChanged) { - // Left and right switched places, so swap dragging cursor - boolean draggingLeft = !wasDraggingLeft; - mSelectDraggingCursor = (draggingLeft - ? mSelectCursorLeft : mSelectCursorRight); - mSelectDraggingTextQuad = (draggingLeft - ? mSelectCursorLeftTextQuad : mSelectCursorRightTextQuad); - mSelectDraggingOffset = (draggingLeft - ? mSelectHandleLeftOffset : mSelectHandleRightOffset); - } - mSelectDraggingCursor.set(oldX, oldY); - } - - private float distanceSquared(int x, int y, Point p) { - float dx = p.x - x; - float dy = p.y - y; - return (dx * dx) + (dy * dy); + mSelectCursorBaseLayerId = + nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, + mSelectCursorBase, mSelectCursorBaseTextQuad); + mSelectCursorExtentLayerId = + nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, + mSelectCursorExtent, mSelectCursorExtentTextQuad); } private boolean setupWebkitSelect() { @@ -5169,18 +5220,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } private void updateWebkitSelection() { - int[] handles = null; - if (mIsCaretSelection) { - mSelectCursorRight.set(mSelectCursorLeft.x, mSelectCursorLeft.y); - } - if (mSelectingText) { - handles = new int[4]; - getSelectionHandles(handles); - } else { - nativeSetTextSelection(mNativeClass, 0); - } + int handleId = (mSelectDraggingCursor == mSelectCursorBase) + ? HANDLE_ID_BASE : HANDLE_ID_EXTENT; mWebViewCore.removeMessages(EventHub.SELECT_TEXT); - mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles); + mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, + mSelectDraggingCursor.x, mSelectDraggingCursor.y, (Integer)handleId); } private void resetCaretTimer() { @@ -5543,21 +5587,21 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc Point caretTop = calculateCaretTop(); if (visibleRect.width() < mEditTextContentBounds.width()) { // The whole edit won't fit in the width, so use the caret rect - if (mSelectCursorLeft.x < caretTop.x) { - showRect.left = Math.max(0, mSelectCursorLeft.x - buffer); + if (mSelectCursorBase.x < caretTop.x) { + showRect.left = Math.max(0, mSelectCursorBase.x - buffer); showRect.right = caretTop.x + buffer; } else { showRect.left = Math.max(0, caretTop.x - buffer); - showRect.right = mSelectCursorLeft.x + buffer; + showRect.right = mSelectCursorBase.x + buffer; } } if (visibleRect.height() < mEditTextContentBounds.height()) { // The whole edit won't fit in the height, so use the caret rect - if (mSelectCursorLeft.y > caretTop.y) { + if (mSelectCursorBase.y > caretTop.y) { showRect.top = Math.max(0, caretTop.y - buffer); - showRect.bottom = mSelectCursorLeft.y + buffer; + showRect.bottom = mSelectCursorBase.y + buffer; } else { - showRect.top = Math.max(0, mSelectCursorLeft.y - buffer); + showRect.top = Math.max(0, mSelectCursorBase.y - buffer); showRect.bottom = caretTop.y + buffer; } } @@ -5801,28 +5845,19 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc ensureSelectionHandles(); int shiftedY = y - getTitleHeight() + getScrollY(); int shiftedX = x + getScrollX(); - if (mSelectHandleCenter != null && mSelectHandleCenter.getBounds() - .contains(shiftedX, shiftedY)) { + if (mSelectHandleBaseBounds.contains(shiftedX, shiftedY)) { mSelectionStarted = true; - mSelectDraggingCursor = mSelectCursorLeft; - mSelectDraggingOffset = mSelectHandleCenterOffset; - mSelectDraggingTextQuad = mSelectCursorLeftTextQuad; - mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE); - hidePasteButton(); - } else if (mSelectHandleLeft != null - && mSelectHandleLeft.getBounds() - .contains(shiftedX, shiftedY)) { - mSelectionStarted = true; - mSelectDraggingOffset = mSelectHandleLeftOffset; - mSelectDraggingCursor = mSelectCursorLeft; - mSelectDraggingTextQuad = mSelectCursorLeftTextQuad; - } else if (mSelectHandleRight != null - && mSelectHandleRight.getBounds() + mSelectDraggingCursor = mSelectCursorBase; + mSelectDraggingTextQuad = mSelectCursorBaseTextQuad; + if (mIsCaretSelection) { + mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE); + hidePasteButton(); + } + } else if (mSelectHandleExtentBounds .contains(shiftedX, shiftedY)) { mSelectionStarted = true; - mSelectDraggingOffset = mSelectHandleRightOffset; - mSelectDraggingCursor = mSelectCursorRight; - mSelectDraggingTextQuad = mSelectCursorRightTextQuad; + mSelectDraggingCursor = mSelectCursorExtent; + mSelectDraggingTextQuad = mSelectCursorExtentTextQuad; } else if (mIsCaretSelection) { selectionDone(); } @@ -5867,9 +5902,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } if (deltaX != 0 || deltaY != 0) { int handleX = contentX + - viewToContentDimension(mSelectDraggingOffset.x); + viewToContentDimension(mSelectOffset.x); int handleY = contentY + - viewToContentDimension(mSelectDraggingOffset.y); + viewToContentDimension(mSelectOffset.y); mSelectDraggingCursor.set(handleX, handleY); boolean inCursorText = mSelectDraggingTextQuad.containsPoint(handleX, handleY); @@ -6006,12 +6041,15 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc break; } case MotionEvent.ACTION_UP: { - endScrollEdit(); - if (!mConfirmMove && mIsEditingText && mSelectionStarted && - mIsCaretSelection) { - showPasteWindow(); - stopTouch(); - break; + if (mIsEditingText && mSelectionStarted) { + endScrollEdit(); + mPrivateHandler.sendEmptyMessageDelayed(SCROLL_HANDLE_INTO_VIEW, + TEXT_SCROLL_FIRST_SCROLL_MS); + if (!mConfirmMove && mIsCaretSelection) { + showPasteWindow(); + stopTouch(); + break; + } } mLastTouchUpTime = eventTime; if (mSentAutoScrollMessage) { @@ -6118,6 +6156,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } } + private static int getSelectionCoordinate(int coordinate, int min, int max) { + return Math.max(Math.min(coordinate, max), min); + } + private void beginScrollEdit() { if (mLastEditScroll == 0) { mLastEditScroll = SystemClock.uptimeMillis() - @@ -6126,10 +6168,37 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } } + private void scrollDraggedSelectionHandleIntoView() { + if (mSelectDraggingCursor == null) { + return; + } + int x = mSelectDraggingCursor.x; + int y = mSelectDraggingCursor.y; + if (!mEditTextContentBounds.contains(x,y)) { + int left = Math.min(0, x - mEditTextContentBounds.left - EDIT_RECT_BUFFER); + int right = Math.max(0, x - mEditTextContentBounds.right + EDIT_RECT_BUFFER); + int deltaX = left + right; + int above = Math.min(0, y - mEditTextContentBounds.top - EDIT_RECT_BUFFER); + int below = Math.max(0, y - mEditTextContentBounds.bottom + EDIT_RECT_BUFFER); + int deltaY = above + below; + if (deltaX != 0 || deltaY != 0) { + int scrollX = getTextScrollX() + deltaX; + int scrollY = getTextScrollY() + deltaY; + scrollX = clampBetween(scrollX, 0, getMaxTextScrollX()); + scrollY = clampBetween(scrollY, 0, getMaxTextScrollY()); + scrollEditText(scrollX, scrollY); + } + } + } + private void endScrollEdit() { mLastEditScroll = 0; } + private static int clampBetween(int value, int min, int max) { + return Math.max(min, Math.min(value, max)); + } + private static int getTextScrollDelta(float speed, long deltaT) { float distance = speed * deltaT; int intDistance = (int)Math.floor(distance); @@ -6145,10 +6214,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ private void scrollEditWithCursor() { if (mLastEditScroll != 0) { - int x = viewToContentX(mLastTouchX + getScrollX() + mSelectDraggingOffset.x); + int x = viewToContentX(mLastTouchX + getScrollX() + mSelectOffset.x); float scrollSpeedX = getTextScrollSpeed(x, mEditTextContentBounds.left, mEditTextContentBounds.right); - int y = viewToContentY(mLastTouchY + getScrollY() + mSelectDraggingOffset.y); + int y = viewToContentY(mLastTouchY + getScrollY() + mSelectOffset.y); float scrollSpeedY = getTextScrollSpeed(y, mEditTextContentBounds.top, mEditTextContentBounds.bottom); if (scrollSpeedX == 0.0f && scrollSpeedY == 0.0f) { @@ -6158,24 +6227,27 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc long timeSinceLastUpdate = currentTime - mLastEditScroll; int deltaX = getTextScrollDelta(scrollSpeedX, timeSinceLastUpdate); int deltaY = getTextScrollDelta(scrollSpeedY, timeSinceLastUpdate); + int scrollX = getTextScrollX() + deltaX; + scrollX = clampBetween(scrollX, 0, getMaxTextScrollX()); + int scrollY = getTextScrollY() + deltaY; + scrollY = clampBetween(scrollY, 0, getMaxTextScrollY()); + mLastEditScroll = currentTime; - if (deltaX == 0 && deltaY == 0) { + if (scrollX == getTextScrollX() && scrollY == getTextScrollY()) { // By probability no text scroll this time. Try again later. mPrivateHandler.sendEmptyMessageDelayed(SCROLL_EDIT_TEXT, TEXT_SCROLL_FIRST_SCROLL_MS); } else { - int scrollX = getTextScrollX() + deltaX; - scrollX = Math.min(getMaxTextScrollX(), scrollX); - scrollX = Math.max(0, scrollX); - int scrollY = getTextScrollY() + deltaY; - scrollY = Math.min(getMaxTextScrollY(), scrollY); - scrollY = Math.max(0, scrollY); - scrollEditText(scrollX, scrollY); - int cursorX = mSelectDraggingCursor.x; - int cursorY = mSelectDraggingCursor.y; - mSelectDraggingCursor.set(x - deltaX, y - deltaY); + int selectionX = getSelectionCoordinate(x, + mEditTextContentBounds.left, mEditTextContentBounds.right); + int selectionY = getSelectionCoordinate(y, + mEditTextContentBounds.top, mEditTextContentBounds.bottom); + int oldX = mSelectDraggingCursor.x; + int oldY = mSelectDraggingCursor.y; + mSelectDraggingCursor.set(selectionX, selectionY); updateWebkitSelection(); - mSelectDraggingCursor.set(cursorX, cursorY); + scrollEditText(scrollX, scrollY); + mSelectDraggingCursor.set(oldX, oldY); } } } @@ -6231,10 +6303,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // scrolling. The rectangle is in document coordinates. final int maxX = mScrollingLayerRect.right; final int maxY = mScrollingLayerRect.bottom; - final int resultX = Math.max(0, - Math.min(mScrollingLayerRect.left + contentX, maxX)); - final int resultY = Math.max(0, - Math.min(mScrollingLayerRect.top + contentY, maxY)); + final int resultX = clampBetween(maxX, 0, + mScrollingLayerRect.left + contentX); + final int resultY = clampBetween(maxY, 0, + mScrollingLayerRect.top + contentY); if (resultX != mScrollingLayerRect.left || resultY != mScrollingLayerRect.top @@ -6335,10 +6407,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc int x = Math.round(newX); int y = Math.round(newY); if (mIsEditingText) { - x = Math.max(mEditTextContentBounds.left, - Math.min(mEditTextContentBounds.right, x)); - y = Math.max(mEditTextContentBounds.top, - Math.min(mEditTextContentBounds.bottom, y)); + x = clampBetween(x, mEditTextContentBounds.left, + mEditTextContentBounds.right); + y = clampBetween(y, mEditTextContentBounds.top, + mEditTextContentBounds.bottom); } mSelectDraggingCursor.set(x, y); } @@ -6401,9 +6473,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private long mTrackballUpTime = 0; private long mLastCursorTime = 0; private Rect mLastCursorBounds; - private SelectionHandleAlpha mHandleAlpha = new SelectionHandleAlpha(); - private ObjectAnimator mHandleAlphaAnimator = - ObjectAnimator.ofInt(mHandleAlpha, "alpha", 0); + private SelectionHandleAlpha mBaseAlpha = new SelectionHandleAlpha(); + private SelectionHandleAlpha mExtentAlpha = new SelectionHandleAlpha(); + private ObjectAnimator mBaseHandleAlphaAnimator = + ObjectAnimator.ofInt(mBaseAlpha, "alpha", 0); + private ObjectAnimator mExtentHandleAlphaAnimator = + ObjectAnimator.ofInt(mExtentAlpha, "alpha", 0); // Set by default; BrowserActivity clears to interpret trackball data // directly for movement. Currently, the framework only passes @@ -7437,7 +7512,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mEditTextLayerId = initData.mNodeLayerId; nativeMapLayerRect(mNativeClass, mEditTextLayerId, mEditTextContentBounds); - mEditTextContent.set(initData.mContentRect); + mEditTextContent.set(initData.mClientRect); relocateAutoCompletePopup(); } break; @@ -7518,6 +7593,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc scrollEditWithCursor(); break; + case SCROLL_HANDLE_INTO_VIEW: + scrollDraggedSelectionHandleIntoView(); + break; + default: super.handleMessage(msg); break; @@ -7553,8 +7632,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc .contains(x, y); } else { isPressingHandle = - mSelectHandleLeft.getBounds().contains(x, y) - || mSelectHandleRight.getBounds().contains(x, y); + mSelectHandleBaseBounds.contains(x, y) + || mSelectHandleExtentBounds.contains(x, y); } return isPressingHandle; } @@ -7901,8 +7980,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc if (data.mSelectTextPtr != 0 && (data.mStart != data.mEnd || - (mFieldPointer == nodePointer && mFieldPointer != 0))) { - mIsCaretSelection = (data.mStart == data.mEnd); + (mFieldPointer == nodePointer && mFieldPointer != 0) || + (nodePointer == 0 && data.mStart == 0 && data.mEnd == 0))) { + mIsEditingText = (mFieldPointer == nodePointer) && nodePointer != 0; + mIsCaretSelection = (data.mStart == data.mEnd && nodePointer != 0); if (mIsCaretSelection && (mInputConnection == null || mInputConnection.getEditable().length() == 0)) { @@ -7911,11 +7992,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } else { if (!mSelectingText) { setupWebkitSelect(); - } else if (!mSelectionStarted) { - syncSelectionCursors(); } else { - adjustSelectionCursors(); + syncSelectionCursors(); } + animateHandles(); if (mIsCaretSelection) { resetCaretTimer(); } @@ -7931,8 +8011,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc float maxScrollX = getMaxTextScrollX(); float scrollPercentX = ((float)scrollX)/maxScrollX; mEditTextContent.offsetTo(-scrollX, -scrollY); - mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SCROLL_TEXT_INPUT, 0, + mWebViewCore.removeMessages(EventHub.SCROLL_TEXT_INPUT); + mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0, scrollY, (Float)scrollPercentX); + animateHandles(); } private void beginTextBatch() { @@ -8518,6 +8600,54 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc WebViewCore.setShouldMonitorWebCoreThread(); } + @Override + public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) { + int layer = getBaseLayer(); + if (layer != 0) { + try { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + ViewStateSerializer.dumpLayerHierarchy(layer, stream, level); + stream.close(); + byte[] buf = stream.toByteArray(); + out.write(new String(buf, "ascii")); + } catch (IOException e) {} + } + } + + @Override + public View findHierarchyView(String className, int hashCode) { + if (mNativeClass == 0) return null; + Picture pic = new Picture(); + if (!nativeDumpLayerContentToPicture(mNativeClass, className, hashCode, pic)) { + return null; + } + return new PictureWrapperView(getContext(), pic, mWebView); + } + + private static class PictureWrapperView extends View { + Picture mPicture; + WebView mWebView; + + public PictureWrapperView(Context context, Picture picture, WebView parent) { + super(context); + mPicture = picture; + mWebView = parent; + setWillNotDraw(false); + setRight(mPicture.getWidth()); + setBottom(mPicture.getHeight()); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawPicture(mPicture); + } + + @Override + public boolean post(Runnable action) { + return mWebView.post(action); + } + } + private native void nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx); private native void nativeDebugDump(); private static native void nativeDestroy(int ptr); @@ -8538,6 +8668,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc int scrollingLayer); private native int nativeGetBaseLayer(int nativeInstance); private native void nativeCopyBaseContentToPicture(Picture pict); + private native boolean nativeDumpLayerContentToPicture(int nativeInstance, + String className, int layerId, Picture pict); private native boolean nativeHasContent(); private native void nativeStopGL(int ptr); private native void nativeDiscardAllTextures(); @@ -8581,4 +8713,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated); private static native void nativeFindMaxVisibleRect(int instance, int layerId, Rect visibleContentRect); + private static native boolean nativeIsHandleLeft(int instance, int handleId); + private static native boolean nativeIsPointVisible(int instance, + int layerId, int contentX, int contentY); } diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 6aff10a..64a5918 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -204,35 +204,6 @@ public class WebViewClient { } /** - * Notify the host application that an SSL error occurred while loading a - * resource, but the WebView chose to proceed anyway based on a - * decision retained from a previous response to onReceivedSslError(). - * @hide - */ - public void onProceededAfterSslError(WebView view, SslError error) { - } - - /** - * Notify the host application to handle a SSL client certificate - * request (display the request to the user and ask whether to - * proceed with a client certificate or not). The host application - * has to call either handler.cancel() or handler.proceed() as the - * connection is suspended and waiting for the response. The - * default behavior is to cancel, returning no client certificate. - * - * @param view The WebView that is initiating the callback. - * @param handler A ClientCertRequestHandler object that will - * handle the user's response. - * @param host_and_port The host and port of the requesting server. - * - * @hide - */ - public void onReceivedClientCertRequest(WebView view, - ClientCertRequestHandler handler, String host_and_port) { - handler.cancel(); - } - - /** * Notify the host application to handle an authentication request. The * default behavior is to cancel the request. * diff --git a/core/java/android/webkit/WebViewClientClassicExt.java b/core/java/android/webkit/WebViewClientClassicExt.java new file mode 100644 index 0000000..a873585 --- /dev/null +++ b/core/java/android/webkit/WebViewClientClassicExt.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.net.http.SslError; + +/** + * Adds WebViewClassic specific extension methods to the WebViewClient callback class. + * These are not part of the public WebView API, so the class is hidden. + * @hide + */ +public class WebViewClientClassicExt extends WebViewClient { + + /** + * Notify the host application that an SSL error occurred while loading a + * resource, but the WebView chose to proceed anyway based on a + * decision retained from a previous response to onReceivedSslError(). + */ + public void onProceededAfterSslError(WebView view, SslError error) { + } + + /** + * Notify the host application to handle a SSL client certificate + * request (display the request to the user and ask whether to + * proceed with a client certificate or not). The host application + * has to call either handler.cancel() or handler.proceed() as the + * connection is suspended and waiting for the response. The + * default behavior is to cancel, returning no client certificate. + * + * @param view The WebView that is initiating the callback. + * @param handler A ClientCertRequestHandler object that will + * handle the user's response. + * @param host_and_port The host and port of the requesting server. + */ + public void onReceivedClientCertRequest(WebView view, + ClientCertRequestHandler handler, String host_and_port) { + handler.cancel(); + } +} diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 728ddbf..905647d 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -132,6 +132,8 @@ public final class WebViewCore { private int mRestoredX = 0; private int mRestoredY = 0; + private MockGeolocation mMockGeolocation = new MockGeolocation(this); + private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager = new DeviceMotionAndOrientationManager(this); private DeviceMotionService mDeviceMotionService; @@ -560,13 +562,6 @@ public final class WebViewCore { } /** - * Notify the webview that this is an installable web app. - */ - protected void setInstallableWebApp() { - mCallbackProxy.setInstallableWebApp(); - } - - /** * Notify the webview that we want to display the video layer fullscreen. */ protected void enterFullscreenForVideoLayer(int layerId, String url) { @@ -619,8 +614,6 @@ public final class WebViewCore { */ private native void nativeNotifyAnimationStarted(int nativeClass); - private native boolean nativeFocusBoundsChanged(int nativeClass); - private native boolean nativeKey(int nativeClass, int keyCode, int unichar, int repeatCount, boolean isShift, boolean isAlt, boolean isSym, boolean isDown); @@ -960,7 +953,7 @@ public final class WebViewCore { public int mMaxLength; public Rect mContentBounds; public int mNodeLayerId; - public Rect mContentRect; + public Rect mClientRect; } // mAction of TouchEventData can be MotionEvent.getAction() which uses the @@ -1199,6 +1192,7 @@ public final class WebViewCore { static final int SET_INITIAL_FOCUS = 224; static final int SAVE_VIEW_STATE = 225; + static final int SET_USE_MOCK_GEOLOCATION = 226; // Private handler for WebCore messages. private Handler mHandler; @@ -1306,13 +1300,8 @@ public final class WebViewCore { } else { xPercent = ((Float) msg.obj).floatValue(); } - Rect contentBounds = new Rect(); nativeScrollFocusedTextInput(mNativeClass, xPercent, - msg.arg2, contentBounds); - Message.obtain( - mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_CONTENT_BOUNDS, - contentBounds).sendToTarget(); + msg.arg2); break; case LOAD_URL: { @@ -1660,6 +1649,10 @@ public final class WebViewCore { (Set<String>) msg.obj); break; + case SET_USE_MOCK_GEOLOCATION: + setUseMockGeolocation(); + break; + case SET_USE_MOCK_DEVICE_ORIENTATION: setUseMockDeviceOrientation(); break; @@ -1708,13 +1701,9 @@ public final class WebViewCore { nativeInsertText(mNativeClass, (String) msg.obj); break; case SELECT_TEXT: { - int[] args = (int[]) msg.obj; - if (args == null) { - nativeClearTextSelection(mNativeClass); - } else { - nativeSelectText(mNativeClass, args[0], - args[1], args[2], args[3]); - } + int handleId = (Integer) msg.obj; + nativeSelectText(mNativeClass, handleId, + msg.arg1, msg.arg2); break; } case SELECT_WORD_AT: { @@ -2192,7 +2181,6 @@ public final class WebViewCore { // only non-null if it is for the first picture set after the first layout ViewState mViewState; boolean mFirstLayoutForNonStandardLoad; - boolean mFocusSizeChanged; } DrawData mLastDrawData = null; @@ -2247,7 +2235,6 @@ public final class WebViewCore { private void webkitDraw(DrawData draw) { if (mWebViewClassic != null) { - draw.mFocusSizeChanged = nativeFocusBoundsChanged(mNativeClass); draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight); if (mSettings.getUseWideViewPort()) { draw.mMinPrefWidth = Math.max( @@ -2330,7 +2317,6 @@ public final class WebViewCore { Log.w(LOGTAG, "Cannot pauseUpdatePicture, core destroyed or not initialized!"); return; } - core.nativeSetIsPaused(core.mNativeClass, true); core.mDrawIsPaused = true; } } @@ -2348,7 +2334,6 @@ public final class WebViewCore { Log.w(LOGTAG, "Cannot resumeUpdatePicture, core destroyed!"); return; } - core.nativeSetIsPaused(core.mNativeClass, false); core.mDrawIsPaused = false; // always redraw on resume to reenable gif animations core.mDrawIsScheduled = false; @@ -2363,13 +2348,13 @@ public final class WebViewCore { ////////////////////////////////////////////////////////////////////////// private void restoreState(int index) { - WebBackForwardList list = mCallbackProxy.getBackForwardList(); + WebBackForwardListClassic list = mCallbackProxy.getBackForwardList(); int size = list.getSize(); for (int i = 0; i < size; i++) { list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); } mBrowserFrame.mLoadInitFromJava = true; - list.restoreIndex(mBrowserFrame.mNativeFrame, index); + WebBackForwardListClassic.restoreIndex(mBrowserFrame.mNativeFrame, index); mBrowserFrame.mLoadInitFromJava = false; } @@ -2787,14 +2772,11 @@ public final class WebViewCore { } // called by JNI - private void updateTextfield(int ptr, boolean changeToPassword, - String text, int textGeneration) { + private void updateTextfield(int ptr, String text, int textGeneration) { if (mWebViewClassic != null) { - Message msg = Message.obtain(mWebViewClassic.mPrivateHandler, + Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, - textGeneration, text); - msg.getData().putBoolean("password", changeToPassword); - msg.sendToTarget(); + textGeneration, text).sendToTarget(); } } @@ -2855,7 +2837,7 @@ public final class WebViewCore { * Scroll the focused textfield to (xPercent, y) in document space */ private native void nativeScrollFocusedTextInput(int nativeClass, - float xPercent, int y, Rect contentBounds); + float xPercent, int y); // these must be in document space (i.e. not scaled/zoomed). private native void nativeSetScrollOffset(int nativeClass, @@ -3063,6 +3045,22 @@ public final class WebViewCore { mDeviceMotionAndOrientationManager.setUseMock(); } + private void setUseMockGeolocation() { + mMockGeolocation.setUseMock(); + } + + public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) { + mMockGeolocation.setPosition(latitude, longitude, accuracy); + } + + public void setMockGeolocationError(int code, String message) { + mMockGeolocation.setError(code, message); + } + + public void setMockGeolocationPermission(boolean allow) { + mMockGeolocation.setPermission(allow); + } + public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha, @@ -3089,7 +3087,6 @@ public final class WebViewCore { sShouldMonitorWebCoreThread = true; } - private native void nativeSetIsPaused(int nativeClass, boolean isPaused); private native void nativePause(int nativeClass); private native void nativeResume(int nativeClass); private native void nativeFreeMemory(int nativeClass); @@ -3135,7 +3132,7 @@ public final class WebViewCore { private native String nativeGetText(int nativeClass, int startX, int startY, int endX, int endY); private native void nativeSelectText(int nativeClass, - int startX, int startY, int endX, int endY); + int handleId, int x, int y); private native void nativeClearTextSelection(int nativeClass); private native boolean nativeSelectWordAt(int nativeClass, int x, int y); private native void nativeSelectAll(int nativeClass); diff --git a/core/java/android/webkit/WebViewDatabaseClassic.java b/core/java/android/webkit/WebViewDatabaseClassic.java index 9b1d4cb..c804b90 100644 --- a/core/java/android/webkit/WebViewDatabaseClassic.java +++ b/core/java/android/webkit/WebViewDatabaseClassic.java @@ -100,6 +100,7 @@ final class WebViewDatabaseClassic extends WebViewDatabase { private boolean mInitialized = false; WebViewDatabaseClassic(final Context context) { + JniUtil.setContext(context); new Thread() { @Override public void run() { diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index 867ee54..ad28b17 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -37,6 +37,7 @@ import android.view.inputmethod.InputConnection; import android.webkit.WebView.HitTestResult; import android.webkit.WebView.PictureListener; +import java.io.BufferedWriter; import java.io.File; import java.util.Map; @@ -238,6 +239,10 @@ public interface WebViewProvider { public void debugDump(); + public void dumpViewHierarchyWithProperties(BufferedWriter out, int level); + + public View findHierarchyView(String className, int hashCode); + //------------------------------------------------------------------------- // Provider glue methods //------------------------------------------------------------------------- diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 0a71c5a..421a324 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -225,26 +225,53 @@ public abstract class CompoundButton extends Button implements Checkable { } @Override + public int getCompoundPaddingLeft() { + int padding = super.getCompoundPaddingLeft(); + if (!isLayoutRtl()) { + final Drawable buttonDrawable = mButtonDrawable; + if (buttonDrawable != null) { + padding += buttonDrawable.getIntrinsicWidth(); + } + } + return padding; + } + + @Override + public int getCompoundPaddingRight() { + int padding = super.getCompoundPaddingRight(); + if (isLayoutRtl()) { + final Drawable buttonDrawable = mButtonDrawable; + if (buttonDrawable != null) { + padding += buttonDrawable.getIntrinsicWidth(); + } + } + return padding; + } + + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); final Drawable buttonDrawable = mButtonDrawable; if (buttonDrawable != null) { final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; - final int height = buttonDrawable.getIntrinsicHeight(); - - int y = 0; + final int drawableHeight = buttonDrawable.getIntrinsicHeight(); + final int drawableWidth = buttonDrawable.getIntrinsicWidth(); + int top = 0; switch (verticalGravity) { case Gravity.BOTTOM: - y = getHeight() - height; + top = getHeight() - drawableHeight; break; case Gravity.CENTER_VERTICAL: - y = (getHeight() - height) / 2; + top = (getHeight() - drawableHeight) / 2; break; } + int bottom = top + drawableHeight; + int left = isLayoutRtl() ? getWidth() - drawableWidth : 0; + int right = isLayoutRtl() ? getWidth() : drawableWidth; - buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height); + buttonDrawable.setBounds(left, top, right, bottom); buttonDrawable.draw(canvas); } } diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index d019d8c..1893620 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -603,6 +603,12 @@ public class FrameLayout extends ViewGroup { */ public int gravity = -1; + @Override + protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { + width = a.getLayoutDimension(widthAttr, MATCH_PARENT); + height = a.getLayoutDimension(heightAttr, MATCH_PARENT); + } + /** * {@inheritDoc} */ diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index ada7dd1..f23e1aa 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -94,7 +94,7 @@ public class GridView extends AbsListView { private View mReferenceView = null; private View mReferenceViewInSelectedRow = null; - private int mGravity = Gravity.LEFT; + private int mGravity = Gravity.START; private final Rect mTempRect = new Rect(); @@ -300,9 +300,18 @@ public class GridView extends AbsListView { final int columnWidth = mColumnWidth; final int horizontalSpacing = mHorizontalSpacing; + final boolean isLayoutRtl = isLayoutRtl(); + int last; - int nextLeft = mListPadding.left + - ((mStretchMode == STRETCH_SPACING_UNIFORM) ? horizontalSpacing : 0); + int nextLeft; + + if (isLayoutRtl) { + nextLeft = getWidth() - mListPadding.right - columnWidth - + ((mStretchMode == STRETCH_SPACING_UNIFORM) ? horizontalSpacing : 0); + } else { + nextLeft = mListPadding.left + + ((mStretchMode == STRETCH_SPACING_UNIFORM) ? horizontalSpacing : 0); + } if (!mStackFromBottom) { last = Math.min(startPos + mNumColumns, mItemCount); @@ -311,7 +320,8 @@ public class GridView extends AbsListView { startPos = Math.max(0, startPos - mNumColumns + 1); if (last - startPos < mNumColumns) { - nextLeft += (mNumColumns - (last - startPos)) * (columnWidth + horizontalSpacing); + final int deltaLeft = (mNumColumns - (last - startPos)) * (columnWidth + horizontalSpacing); + nextLeft += (isLayoutRtl ? -1 : +1) * deltaLeft; } } @@ -330,7 +340,7 @@ public class GridView extends AbsListView { final int where = flow ? -1 : pos - startPos; child = makeAndAddView(pos, y, flow, nextLeft, selected, where); - nextLeft += columnWidth; + nextLeft += (isLayoutRtl ? -1 : +1) * columnWidth; if (pos < last - 1) { nextLeft += horizontalSpacing; } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index cf28da4..f259597 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -193,15 +193,6 @@ public class ImageView extends View { } } - /** - * @hide - */ - @Override - public int getResolvedLayoutDirection(Drawable dr) { - return (dr == mDrawable) ? - getResolvedLayoutDirection() : super.getResolvedLayoutDirection(dr); - } - @Override public boolean hasOverlappingRendering() { return (getBackground() != null); @@ -675,6 +666,7 @@ public class ImageView extends View { d.setState(getDrawableState()); } d.setLevel(mLevel); + d.setLayoutDirection(getLayoutDirection()); mDrawableWidth = d.getIntrinsicWidth(); mDrawableHeight = d.getIntrinsicHeight(); applyColorMod(); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index f3f18d5..1c6a406 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -478,6 +478,9 @@ public class ProgressBar extends View { d.setCallback(this); } mIndeterminateDrawable = d; + if (mIndeterminateDrawable != null) { + mIndeterminateDrawable.setLayoutDirection(getLayoutDirection()); + } if (mIndeterminate) { mCurrentDrawable = d; postInvalidate(); @@ -517,6 +520,7 @@ public class ProgressBar extends View { if (d != null) { d.setCallback(this); + d.setLayoutDirection(getLayoutDirection()); // Make sure the ProgressBar is always tall enough int drawableHeight = d.getMinimumHeight(); @@ -975,15 +979,6 @@ public class ProgressBar extends View { } } - /** - * @hide - */ - @Override - public int getResolvedLayoutDirection(Drawable who) { - return (who == mProgressDrawable || who == mIndeterminateDrawable) ? - getResolvedLayoutDirection() : super.getResolvedLayoutDirection(who); - } - @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { updateDrawableBounds(w, h); diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index f217c9c..338b8d6 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -297,33 +297,6 @@ public class RadioGroup extends LinearLayout { public LayoutParams(MarginLayoutParams source) { super(source); } - - /** - * <p>Fixes the child's width to - * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the child's - * height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} - * when not specified in the XML file.</p> - * - * @param a the styled attributes set - * @param widthAttr the width attribute to fetch - * @param heightAttr the height attribute to fetch - */ - @Override - protected void setBaseAttributes(TypedArray a, - int widthAttr, int heightAttr) { - - if (a.hasValue(widthAttr)) { - width = a.getLayoutDimension(widthAttr, "layout_width"); - } else { - width = WRAP_CONTENT; - } - - if (a.hasValue(heightAttr)) { - height = a.getLayoutDimension(heightAttr, "layout_height"); - } else { - height = WRAP_CONTENT; - } - } } /** diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index beb87cd..62063a1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1656,12 +1656,19 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling {@link + * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}. + * * @param viewId The id of the view whose text should change - * @param start The id of a drawable to place before the text (relative to the + * @param start The id of a drawable to place before the text (relative to the * layout direction), or 0 * @param top The id of a drawable to place above the text, or 0 * @param end The id of a drawable to place after the text, or 0 +<<<<<<< HEAD + * @param bottom The id of a drawable to place below the text, or 0 +======= * @param bottom The id of a drawable to place below the text, or 0 +>>>>>>> 0a43f67e */ public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) { addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom)); diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 36d1ee0..510a794 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -349,7 +349,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { public void setGravity(int gravity) { if (mGravity != gravity) { if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { - gravity |= Gravity.LEFT; + gravity |= Gravity.START; } mGravity = gravity; requestLayout(); @@ -453,7 +453,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { /** * Creates and positions all views for this Spinner. * - * @param delta Change in the selected position. +1 moves selection is moving to the right, + * @param delta Change in the selected position. +1 means selection is moving to the right, * so views are scrolling to the left. -1 means selection is moving to the left. */ @Override @@ -485,7 +485,9 @@ public class Spinner extends AbsSpinner implements OnClickListener { View sel = makeAndAddView(mSelectedPosition); int width = sel.getMeasuredWidth(); int selectedOffset = childrenLeft; - switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + final int layoutDirection = getResolvedLayoutDirection(); + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2); break; @@ -932,19 +934,18 @@ public class Spinner extends AbsSpinner implements OnClickListener { @Override public void show() { final Drawable background = getBackground(); - int bgOffset = 0; + int hOffset = 0; if (background != null) { background.getPadding(mTempRect); - bgOffset = -mTempRect.left; + hOffset = isLayoutRtl() ? mTempRect.right : -mTempRect.left; } else { mTempRect.left = mTempRect.right = 0; } final int spinnerPaddingLeft = Spinner.this.getPaddingLeft(); + final int spinnerPaddingRight = Spinner.this.getPaddingRight(); + final int spinnerWidth = Spinner.this.getWidth(); if (mDropDownWidth == WRAP_CONTENT) { - final int spinnerWidth = Spinner.this.getWidth(); - final int spinnerPaddingRight = Spinner.this.getPaddingRight(); - int contentWidth = measureContentWidth( (SpinnerAdapter) mAdapter, getBackground()); final int contentWidthLimit = mContext.getResources() @@ -952,17 +953,20 @@ public class Spinner extends AbsSpinner implements OnClickListener { if (contentWidth > contentWidthLimit) { contentWidth = contentWidthLimit; } - setContentWidth(Math.max( contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight)); } else if (mDropDownWidth == MATCH_PARENT) { - final int spinnerWidth = Spinner.this.getWidth(); - final int spinnerPaddingRight = Spinner.this.getPaddingRight(); setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight); } else { setContentWidth(mDropDownWidth); } - setHorizontalOffset(bgOffset + spinnerPaddingLeft); + + if (isLayoutRtl()) { + hOffset += spinnerWidth - spinnerPaddingRight - getWidth(); + } else { + hOffset += spinnerPaddingLeft; + } + setHorizontalOffset(hOffset); setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); super.show(); getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 471f259..8f5dc2c 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -469,12 +469,6 @@ public class Switch extends CompoundButton { @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - if (mOnLayout == null) { mOnLayout = makeLayout(mTextOn); } @@ -490,34 +484,6 @@ public class Switch extends CompoundButton { mThumbWidth = maxTextWidth + mThumbTextPadding * 2; - switch (widthMode) { - case MeasureSpec.AT_MOST: - widthSize = Math.min(widthSize, switchWidth); - break; - - case MeasureSpec.UNSPECIFIED: - widthSize = switchWidth; - break; - - case MeasureSpec.EXACTLY: - // Just use what we were given - break; - } - - switch (heightMode) { - case MeasureSpec.AT_MOST: - heightSize = Math.min(heightSize, switchHeight); - break; - - case MeasureSpec.UNSPECIFIED: - heightSize = switchHeight; - break; - - case MeasureSpec.EXACTLY: - // Just use what we were given - break; - } - mSwitchWidth = switchWidth; mSwitchHeight = switchHeight; @@ -651,7 +617,7 @@ public class Switch extends CompoundButton { mVelocityTracker.computeCurrentVelocity(1000); float xvel = mVelocityTracker.getXVelocity(); if (Math.abs(xvel) > mMinFlingVelocity) { - newState = xvel > 0; + newState = isLayoutRtl() ? (xvel < 0) : (xvel > 0); } else { newState = getTargetCheckedState(); } @@ -669,13 +635,25 @@ public class Switch extends CompoundButton { } private boolean getTargetCheckedState() { - return mThumbPosition >= getThumbScrollRange() / 2; + if (isLayoutRtl()) { + return mThumbPosition <= getThumbScrollRange() / 2; + } else { + return mThumbPosition >= getThumbScrollRange() / 2; + } + } + + private void setThumbPosition(boolean checked) { + if (isLayoutRtl()) { + mThumbPosition = checked ? 0 : getThumbScrollRange(); + } else { + mThumbPosition = checked ? getThumbScrollRange() : 0; + } } @Override public void setChecked(boolean checked) { super.setChecked(checked); - mThumbPosition = checked ? getThumbScrollRange() : 0; + setThumbPosition(checked); invalidate(); } @@ -683,10 +661,19 @@ public class Switch extends CompoundButton { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - mThumbPosition = isChecked() ? getThumbScrollRange() : 0; + setThumbPosition(isChecked()); + + int switchRight; + int switchLeft; + + if (isLayoutRtl()) { + switchLeft = getPaddingLeft(); + switchRight = switchLeft + mSwitchWidth; + } else { + switchRight = getWidth() - getPaddingRight(); + switchLeft = switchRight - mSwitchWidth; + } - int switchRight = getWidth() - getPaddingRight(); - int switchLeft = switchRight - mSwitchWidth; int switchTop = 0; int switchBottom = 0; switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) { @@ -761,7 +748,22 @@ public class Switch extends CompoundButton { } @Override + public int getCompoundPaddingLeft() { + if (!isLayoutRtl()) { + return super.getCompoundPaddingLeft(); + } + int padding = super.getCompoundPaddingLeft() + mSwitchWidth; + if (!TextUtils.isEmpty(getText())) { + padding += mSwitchPadding; + } + return padding; + } + + @Override public int getCompoundPaddingRight() { + if (isLayoutRtl()) { + return super.getCompoundPaddingRight(); + } int padding = super.getCompoundPaddingRight() + mSwitchWidth; if (!TextUtils.isEmpty(getText())) { padding += mSwitchPadding; diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java index 6331b6d..513f180 100644 --- a/core/java/android/widget/TableLayout.java +++ b/core/java/android/widget/TableLayout.java @@ -741,11 +741,7 @@ public class TableLayout extends LinearLayout { @Override protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { this.width = MATCH_PARENT; - if (a.hasValue(heightAttr)) { - this.height = a.getLayoutDimension(heightAttr, "layout_height"); - } else { - this.height = WRAP_CONTENT; - } + this.height = a.getLayoutDimension(heightAttr, WRAP_CONTENT); } } diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index 01c4c2c..fb548df 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -502,19 +502,8 @@ public class TableRow extends LinearLayout { @Override protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { - // We don't want to force users to specify a layout_width - if (a.hasValue(widthAttr)) { - width = a.getLayoutDimension(widthAttr, "layout_width"); - } else { - width = MATCH_PARENT; - } - - // We don't want to force users to specify a layout_height - if (a.hasValue(heightAttr)) { - height = a.getLayoutDimension(heightAttr, "layout_height"); - } else { - height = WRAP_CONTENT; - } + width = a.getLayoutDimension(widthAttr, MATCH_PARENT); + height = a.getLayoutDimension(heightAttr, WRAP_CONTENT); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 01617da..25f0131 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1542,7 +1542,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the start padding of the view, plus space for the start * Drawable if any. - * @hide */ public int getCompoundPaddingStart() { resolveDrawables(); @@ -1558,7 +1557,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the end padding of the view, plus space for the end * Drawable if any. - * @hide */ public int getCompoundPaddingEnd() { resolveDrawables(); @@ -1656,7 +1654,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the total start padding of the view, including the start * Drawable if any. - * @hide */ public int getTotalPaddingStart() { return getCompoundPaddingStart(); @@ -1665,7 +1662,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the total end padding of the view, including the end * Drawable if any. - * @hide */ public int getTotalPaddingEnd() { return getCompoundPaddingEnd(); @@ -1868,7 +1864,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom - * @hide */ public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) { @@ -1990,7 +1985,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom - * @hide */ @android.view.RemotableViewMethod public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end, @@ -2014,7 +2008,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom - * @hide */ public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, Drawable end, Drawable bottom) { @@ -2061,7 +2054,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom - * @hide */ public Drawable[] getCompoundDrawablesRelative() { final Drawables dr = mDrawables; @@ -4586,23 +4578,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** - * @hide - */ - @Override - public int getResolvedLayoutDirection(Drawable who) { - if (who == null) return View.LAYOUT_DIRECTION_LTR; - if (mDrawables != null) { - final Drawables drawables = mDrawables; - if (who == drawables.mDrawableLeft || who == drawables.mDrawableRight || - who == drawables.mDrawableTop || who == drawables.mDrawableBottom || - who == drawables.mDrawableStart || who == drawables.mDrawableEnd) { - return getResolvedLayoutDirection(); - } - } - return super.getResolvedLayoutDirection(who); - } - @Override public boolean hasOverlappingRendering() { return (getBackground() != null || mText instanceof Spannable || hasSelection()); @@ -5634,7 +5609,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener physicalWidth, false); } - /** @hide */ @Override public void onResolvedLayoutDirectionReset() { if (mLayoutAlignment != null) { @@ -8180,7 +8154,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mEditor.mInBatchEditControllers; } - /** @hide */ @Override public void onResolvedTextDirectionChanged() { if (hasPasswordTransformationMethod()) { @@ -8216,13 +8189,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** - * Subclasses will need to override this method to implement their own way of resolving - * drawables depending on the layout direction. - * - * A call to the super method will be required from the subclasses implementation. - */ - protected void resolveDrawables() { + @Override + public void onResolveDrawables(int layoutDirection) { // No need to resolve twice if (mResolvedDrawables) { return; @@ -8238,7 +8206,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } Drawables dr = mDrawables; - switch(getResolvedLayoutDirection()) { + switch(layoutDirection) { case LAYOUT_DIRECTION_RTL: if (dr.mDrawableStart != null) { dr.mDrawableRight = dr.mDrawableStart; @@ -8270,9 +8238,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } break; } + updateDrawablesLayoutDirection(dr, layoutDirection); mResolvedDrawables = true; } + private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) { + if (dr.mDrawableLeft != null) { + dr.mDrawableLeft.setLayoutDirection(layoutDirection); + } + if (dr.mDrawableRight != null) { + dr.mDrawableRight.setLayoutDirection(layoutDirection); + } + if (dr.mDrawableTop != null) { + dr.mDrawableTop.setLayoutDirection(layoutDirection); + } + if (dr.mDrawableBottom != null) { + dr.mDrawableBottom.setLayoutDirection(layoutDirection); + } + } + protected void resetResolvedDrawables() { mResolvedDrawables = false; } diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index fafc113..6fc0773 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -24,6 +24,7 @@ import android.graphics.PixelFormat; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.LocaleUtil; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -33,6 +34,8 @@ import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import java.util.Locale; + /** * A toast is a view containing a quick little message for the user. The toast class * helps you create and show those. @@ -371,7 +374,11 @@ public class Toast { handleHide(); mView = mNextView; mWM = WindowManagerImpl.getDefault(); - final int gravity = mGravity; + // We can resolve the Gravity here by using the Locale for getting + // the layout direction + final int layoutDirection = LocaleUtil.getLayoutDirectionFromLocale( + Locale.getDefault()); + final int gravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); mParams.gravity = gravity; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { mParams.horizontalWeight = 1.0f; diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 6a0cd36..1a76461f 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -58,14 +58,14 @@ interface IBatteryStats { void noteBluetoothOff(); void noteFullWifiLockAcquired(int uid); void noteFullWifiLockReleased(int uid); - void noteScanWifiLockAcquired(int uid); - void noteScanWifiLockReleased(int uid); + void noteWifiScanStarted(int uid); + void noteWifiScanStopped(int uid); void noteWifiMulticastEnabled(int uid); void noteWifiMulticastDisabled(int uid); void noteFullWifiLockAcquiredFromSource(in WorkSource ws); void noteFullWifiLockReleasedFromSource(in WorkSource ws); - void noteScanWifiLockAcquiredFromSource(in WorkSource ws); - void noteScanWifiLockReleasedFromSource(in WorkSource ws); + void noteWifiScanStartedFromSource(in WorkSource ws); + void noteWifiScanStoppedFromSource(in WorkSource ws); void noteWifiMulticastEnabledFromSource(in WorkSource ws); void noteWifiMulticastDisabledFromSource(in WorkSource ws); void noteNetworkInterfaceType(String iface, int type); diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl index 216d985..78b4466 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl @@ -24,6 +24,7 @@ import android.widget.RemoteViews; oneway interface IAppWidgetHost { void updateAppWidget(int appWidgetId, in RemoteViews views); void providerChanged(int appWidgetId, in AppWidgetProviderInfo info); + void providersChanged(); void viewDataChanged(int appWidgetId, int viewId); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 5157385..42bc14d 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -159,8 +159,8 @@ public final class BatteryStatsImpl extends BatteryStats { = new SparseArray<ArrayList<StopwatchTimer>>(); final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<StopwatchTimer>(); final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<StopwatchTimer>(); - final ArrayList<StopwatchTimer> mScanWifiLockTimers = new ArrayList<StopwatchTimer>(); final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<StopwatchTimer>(); + final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>(); // Last partial timers we use for distributing CPU usage. final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>(); @@ -320,6 +320,18 @@ public final class BatteryStatsImpl extends BatteryStats { Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime }; + private static final int[] WAKEUP_SOURCES_FORMAT = new int[] { + Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name + Process.PROC_TAB_TERM|Process.PROC_COMBINE| + Process.PROC_OUT_LONG, // 1: count + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE + |Process.PROC_OUT_LONG, // 6: totalTime + }; + private final String[] mProcWakelocksName = new String[3]; private final long[] mProcWakelocksData = new long[3]; @@ -1028,34 +1040,44 @@ public final class BatteryStatsImpl extends BatteryStats { private final Map<String, KernelWakelockStats> readKernelWakelockStats() { + FileInputStream is; byte[] buffer = new byte[8192]; int len; + boolean wakeup_sources = false; try { - FileInputStream is = new FileInputStream("/proc/wakelocks"); + try { + is = new FileInputStream("/proc/wakelocks"); + } catch (java.io.FileNotFoundException e) { + try { + is = new FileInputStream("/d/wakeup_sources"); + wakeup_sources = true; + } catch (java.io.FileNotFoundException e2) { + return null; + } + } + len = is.read(buffer); is.close(); + } catch (java.io.IOException e) { + return null; + } - if (len > 0) { - int i; - for (i=0; i<len; i++) { - if (buffer[i] == '\0') { - len = i; - break; - } + if (len > 0) { + int i; + for (i=0; i<len; i++) { + if (buffer[i] == '\0') { + len = i; + break; } } - } catch (java.io.FileNotFoundException e) { - return null; - } catch (java.io.IOException e) { - return null; } - return parseProcWakelocks(buffer, len); + return parseProcWakelocks(buffer, len, wakeup_sources); } private final Map<String, KernelWakelockStats> parseProcWakelocks( - byte[] wlBuffer, int len) { + byte[] wlBuffer, int len, boolean wakeup_sources) { String name; int count; long totalTime; @@ -1092,12 +1114,20 @@ public final class BatteryStatsImpl extends BatteryStats { if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?'; } boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex, - PROC_WAKELOCKS_FORMAT, nameStringArray, wlData, null); + wakeup_sources ? WAKEUP_SOURCES_FORMAT : + PROC_WAKELOCKS_FORMAT, + nameStringArray, wlData, null); name = nameStringArray[0]; count = (int) wlData[1]; - // convert nanoseconds to microseconds with rounding. - totalTime = (wlData[2] + 500) / 1000; + + if (wakeup_sources) { + // convert milliseconds to microseconds + totalTime = wlData[2] * 1000; + } else { + // convert nanoseconds to microseconds with rounding. + totalTime = (wlData[2] + 500) / 1000; + } if (parsed && name.length() > 0) { if (!m.containsKey(name)) { @@ -2174,28 +2204,28 @@ public final class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(); } - int mWifiScanLockNesting = 0; + int mWifiScanNesting = 0; - public void noteScanWifiLockAcquiredLocked(int uid) { - if (mWifiScanLockNesting == 0) { - mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG; - if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock on to: " + public void noteWifiScanStartedLocked(int uid) { + if (mWifiScanNesting == 0) { + mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(SystemClock.elapsedRealtime()); } - mWifiScanLockNesting++; - getUidStatsLocked(uid).noteScanWifiLockAcquiredLocked(); + mWifiScanNesting++; + getUidStatsLocked(uid).noteWifiScanStartedLocked(); } - public void noteScanWifiLockReleasedLocked(int uid) { - mWifiScanLockNesting--; - if (mWifiScanLockNesting == 0) { - mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG; - if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock off to: " + public void noteWifiScanStoppedLocked(int uid) { + mWifiScanNesting--; + if (mWifiScanNesting == 0) { + mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(SystemClock.elapsedRealtime()); } - getUidStatsLocked(uid).noteScanWifiLockReleasedLocked(); + getUidStatsLocked(uid).noteWifiScanStoppedLocked(); } int mWifiMulticastNesting = 0; @@ -2236,17 +2266,17 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteScanWifiLockAcquiredFromSourceLocked(WorkSource ws) { + public void noteWifiScanStartedFromSourceLocked(WorkSource ws) { int N = ws.size(); for (int i=0; i<N; i++) { - noteScanWifiLockAcquiredLocked(ws.get(i)); + noteWifiScanStartedLocked(ws.get(i)); } } - public void noteScanWifiLockReleasedFromSourceLocked(WorkSource ws) { + public void noteWifiScanStoppedFromSourceLocked(WorkSource ws) { int N = ws.size(); for (int i=0; i<N; i++) { - noteScanWifiLockReleasedLocked(ws.get(i)); + noteWifiScanStoppedLocked(ws.get(i)); } } @@ -2360,8 +2390,8 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mFullWifiLockOut; StopwatchTimer mFullWifiLockTimer; - boolean mScanWifiLockOut; - StopwatchTimer mScanWifiLockTimer; + boolean mWifiScanStarted; + StopwatchTimer mWifiScanTimer; boolean mWifiMulticastEnabled; StopwatchTimer mWifiMulticastTimer; @@ -2405,8 +2435,8 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiRunningTimers, mUnpluggables); mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK, mFullWifiLockTimers, mUnpluggables); - mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK, - mScanWifiLockTimers, mUnpluggables); + mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN, + mWifiScanTimers, mUnpluggables); mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, mWifiMulticastTimers, mUnpluggables); mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON, @@ -2518,22 +2548,22 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override - public void noteScanWifiLockAcquiredLocked() { - if (!mScanWifiLockOut) { - mScanWifiLockOut = true; - if (mScanWifiLockTimer == null) { - mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK, - mScanWifiLockTimers, mUnpluggables); + public void noteWifiScanStartedLocked() { + if (!mWifiScanStarted) { + mWifiScanStarted = true; + if (mWifiScanTimer == null) { + mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN, + mWifiScanTimers, mUnpluggables); } - mScanWifiLockTimer.startRunningLocked(BatteryStatsImpl.this); + mWifiScanTimer.startRunningLocked(BatteryStatsImpl.this); } } @Override - public void noteScanWifiLockReleasedLocked() { - if (mScanWifiLockOut) { - mScanWifiLockOut = false; - mScanWifiLockTimer.stopRunningLocked(BatteryStatsImpl.this); + public void noteWifiScanStoppedLocked() { + if (mWifiScanStarted) { + mWifiScanStarted = false; + mWifiScanTimer.stopRunningLocked(BatteryStatsImpl.this); } } @@ -2614,11 +2644,11 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override - public long getScanWifiLockTime(long batteryRealtime, int which) { - if (mScanWifiLockTimer == null) { + public long getWifiScanTime(long batteryRealtime, int which) { + if (mWifiScanTimer == null) { return 0; } - return mScanWifiLockTimer.getTotalTimeLocked(batteryRealtime, which); + return mWifiScanTimer.getTotalTimeLocked(batteryRealtime, which); } @Override @@ -2698,9 +2728,9 @@ public final class BatteryStatsImpl extends BatteryStats { active |= !mFullWifiLockTimer.reset(BatteryStatsImpl.this, false); active |= mFullWifiLockOut; } - if (mScanWifiLockTimer != null) { - active |= !mScanWifiLockTimer.reset(BatteryStatsImpl.this, false); - active |= mScanWifiLockOut; + if (mWifiScanTimer != null) { + active |= !mWifiScanTimer.reset(BatteryStatsImpl.this, false); + active |= mWifiScanStarted; } if (mWifiMulticastTimer != null) { active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false); @@ -2791,8 +2821,8 @@ public final class BatteryStatsImpl extends BatteryStats { if (mFullWifiLockTimer != null) { mFullWifiLockTimer.detach(); } - if (mScanWifiLockTimer != null) { - mScanWifiLockTimer.detach(); + if (mWifiScanTimer != null) { + mWifiScanTimer.detach(); } if (mWifiMulticastTimer != null) { mWifiMulticastTimer.detach(); @@ -2860,9 +2890,9 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } - if (mScanWifiLockTimer != null) { + if (mWifiScanTimer != null) { out.writeInt(1); - mScanWifiLockTimer.writeToParcel(out, batteryRealtime); + mWifiScanTimer.writeToParcel(out, batteryRealtime); } else { out.writeInt(0); } @@ -2954,12 +2984,12 @@ public final class BatteryStatsImpl extends BatteryStats { } else { mFullWifiLockTimer = null; } - mScanWifiLockOut = false; + mWifiScanStarted = false; if (in.readInt() != 0) { - mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK, - mScanWifiLockTimers, mUnpluggables, in); + mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN, + mWifiScanTimers, mUnpluggables, in); } else { - mScanWifiLockTimer = null; + mWifiScanTimer = null; } mWifiMulticastEnabled = false; if (in.readInt() != 0) { @@ -5118,9 +5148,9 @@ public final class BatteryStatsImpl extends BatteryStats { if (in.readInt() != 0) { u.mFullWifiLockTimer.readSummaryFromParcelLocked(in); } - u.mScanWifiLockOut = false; + u.mWifiScanStarted = false; if (in.readInt() != 0) { - u.mScanWifiLockTimer.readSummaryFromParcelLocked(in); + u.mWifiScanTimer.readSummaryFromParcelLocked(in); } u.mWifiMulticastEnabled = false; if (in.readInt() != 0) { @@ -5310,9 +5340,9 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } - if (u.mScanWifiLockTimer != null) { + if (u.mWifiScanTimer != null) { out.writeInt(1); - u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL); + u.mWifiScanTimer.writeSummaryFromParcelLocked(out, NOWREAL); } else { out.writeInt(0); } @@ -5537,7 +5567,7 @@ public final class BatteryStatsImpl extends BatteryStats { mWindowTimers.clear(); mWifiRunningTimers.clear(); mFullWifiLockTimers.clear(); - mScanWifiLockTimers.clear(); + mWifiScanTimers.clear(); mWifiMulticastTimers.clear(); sNumSpeedSteps = in.readInt(); diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index d1c2d2e..5093b4d 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -444,6 +444,16 @@ public class AsyncChannel { if ((mConnection != null) && (mSrcContext != null)) { mSrcContext.unbindService(mConnection); } + try { + // Send the DISCONNECTED, although it may not be received + // but its the best we can do. + Message msg = Message.obtain(); + msg.what = CMD_CHANNEL_DISCONNECTED; + msg.replyTo = mSrcMessenger; + mDstMessenger.send(msg); + } catch(Exception e) { + } + // Tell source we're disconnected. if (mSrcHandler != null) { replyDisconnected(STATUS_SUCCESSFUL); } diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index 1391ac3..0ea7b83 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -80,9 +80,9 @@ import java.util.Vector; * and invoke <code>halting</code>. Any message subsequently received by the state * machine will cause <code>haltedProcessMessage</code> to be invoked.</p> * - * <p>If it is desirable to completely stop the state machine call <code>quit</code>. This - * will exit the current state and its parent and then exit from the controlling thread - * and no further messages will be processed.</p> + * <p>If it is desirable to completely stop the state machine call <code>quit</code> or + * <code>abort</code>. These will call <code>exit</code> of the current state and its parents, call + * <code>onQuiting</code> and then exit Thread/Loopers.</p> * * <p>In addition to <code>processMessage</code> each <code>State</code> has * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p> @@ -362,7 +362,7 @@ class Hsm1 extends StateMachine { } @Override - void halting() { + void onHalting() { Log.d(TAG, "halting"); synchronized (this) { this.notifyAll(); @@ -423,10 +423,10 @@ public class StateMachine { private String mName; /** Message.what value when quitting */ - public static final int SM_QUIT_CMD = -1; + private static final int SM_QUIT_CMD = -1; /** Message.what value when initializing */ - public static final int SM_INIT_CMD = -2; + private static final int SM_INIT_CMD = -2; /** * Convenience constant that maybe returned by processMessage @@ -443,11 +443,10 @@ public class StateMachine { public static final boolean NOT_HANDLED = false; /** + * StateMachine logging record. * {@hide} - * - * The information maintained for a processed message. */ - public static class ProcessedMessageInfo { + public static class LogRec { private long mTime; private int mWhat; private String mInfo; @@ -456,12 +455,13 @@ public class StateMachine { /** * Constructor - * @param message + * + * @param msg * @param state that handled the message * @param orgState is the first state the received the message but * did not processes the message. */ - ProcessedMessageInfo(Message msg, String info, State state, State orgState) { + LogRec(Message msg, String info, State state, State orgState) { update(msg, info, state, orgState); } @@ -473,7 +473,7 @@ public class StateMachine { */ public void update(Message msg, String info, State state, State orgState) { mTime = System.currentTimeMillis(); - mWhat = msg.what; + mWhat = (msg != null) ? msg.what : 0; mInfo = info; mState = state; mOrgState = orgState; @@ -517,8 +517,7 @@ public class StateMachine { /** * @return as string */ - @Override - public String toString() { + public String toString(StateMachine sm) { StringBuilder sb = new StringBuilder(); sb.append("time="); Calendar c = Calendar.getInstance(); @@ -529,10 +528,15 @@ public class StateMachine { sb.append(" orgState="); sb.append(mOrgState == null ? "<null>" : mOrgState.getName()); sb.append(" what="); - sb.append(mWhat); - sb.append("(0x"); - sb.append(Integer.toHexString(mWhat)); - sb.append(")"); + String what = sm.getWhatToString(mWhat); + if (TextUtils.isEmpty(what)) { + sb.append(mWhat); + sb.append("(0x"); + sb.append(Integer.toHexString(mWhat)); + sb.append(")"); + } else { + sb.append(what); + } if ( ! TextUtils.isEmpty(mInfo)) { sb.append(" "); sb.append(mInfo); @@ -542,21 +546,21 @@ public class StateMachine { } /** - * A list of messages recently processed by the state machine. + * A list of log records including messages recently processed by the state machine. * - * The class maintains a list of messages that have been most + * The class maintains a list of log records including messages * recently processed. The list is finite and may be set in the * constructor or by calling setSize. The public interface also - * includes size which returns the number of recent messages, - * count which is the number of message processed since the - * the last setSize, get which returns a processed message and - * add which adds a processed messaged. + * includes size which returns the number of recent records, + * count which is the number of records processed since the + * the last setSize, get which returns a record and + * add which adds a record. */ - private static class ProcessedMessages { + private static class LogRecords { private static final int DEFAULT_SIZE = 20; - private Vector<ProcessedMessageInfo> mMessages = new Vector<ProcessedMessageInfo>(); + private Vector<LogRec> mLogRecords = new Vector<LogRec>(); private int mMaxSize = DEFAULT_SIZE; private int mOldestIndex = 0; private int mCount = 0; @@ -564,39 +568,39 @@ public class StateMachine { /** * private constructor use add */ - private ProcessedMessages() { + private LogRecords() { } /** - * Set size of messages to maintain and clears all current messages. + * Set size of messages to maintain and clears all current records. * - * @param maxSize number of messages to maintain at anyone time. + * @param maxSize number of records to maintain at anyone time. */ - void setSize(int maxSize) { + synchronized void setSize(int maxSize) { mMaxSize = maxSize; mCount = 0; - mMessages.clear(); + mLogRecords.clear(); } /** - * @return the number of recent messages. + * @return the number of recent records. */ - int size() { - return mMessages.size(); + synchronized int size() { + return mLogRecords.size(); } /** - * @return the total number of messages processed since size was set. + * @return the total number of records processed since size was set. */ - int count() { + synchronized int count() { return mCount; } /** - * Clear the list of Processed Message Info. + * Clear the list of records. */ - void cleanup() { - mMessages.clear(); + synchronized void cleanup() { + mLogRecords.clear(); } /** @@ -604,7 +608,7 @@ public class StateMachine { * record and size()-1 is the newest record. If the index is to * large null is returned. */ - ProcessedMessageInfo get(int index) { + synchronized LogRec get(int index) { int nextIndex = mOldestIndex + index; if (nextIndex >= mMaxSize) { nextIndex -= mMaxSize; @@ -612,7 +616,7 @@ public class StateMachine { if (nextIndex >= size()) { return null; } else { - return mMessages.get(nextIndex); + return mLogRecords.get(nextIndex); } } @@ -625,12 +629,12 @@ public class StateMachine { * @param orgState is the first state the received the message but * did not processes the message. */ - void add(Message msg, String messageInfo, State state, State orgState) { + synchronized void add(Message msg, String messageInfo, State state, State orgState) { mCount += 1; - if (mMessages.size() < mMaxSize) { - mMessages.add(new ProcessedMessageInfo(msg, messageInfo, state, orgState)); + if (mLogRecords.size() < mMaxSize) { + mLogRecords.add(new LogRec(msg, messageInfo, state, orgState)); } else { - ProcessedMessageInfo pmi = mMessages.get(mOldestIndex); + LogRec pmi = mLogRecords.get(mOldestIndex); mOldestIndex += 1; if (mOldestIndex >= mMaxSize) { mOldestIndex = 0; @@ -652,8 +656,8 @@ public class StateMachine { /** The current message */ private Message mMsg; - /** A list of messages that this state machine has processed */ - private ProcessedMessages mProcessedMessages = new ProcessedMessages(); + /** A list of log records including messages this state machine has processed */ + private LogRecords mLogRecords = new LogRecords(); /** true if construction of the state machine has not been completed */ private boolean mIsConstructionCompleted; @@ -814,15 +818,18 @@ public class StateMachine { */ if (destState != null) { if (destState == mQuittingState) { + /** + * Call onQuitting to let subclasses cleanup. + */ + mSm.onQuitting(); cleanupAfterQuitting(); - } else if (destState == mHaltingState) { /** - * Call halting() if we've transitioned to the halting + * Call onHalting() if we've transitioned to the halting * state. All subsequent messages will be processed in * in the halting state which invokes haltedProcessMessage(msg); */ - mSm.halting(); + mSm.onHalting(); } } } @@ -831,7 +838,6 @@ public class StateMachine { * Cleanup all the static variables and the looper after the SM has been quit. */ private final void cleanupAfterQuitting() { - mSm.quitting(); if (mSm.mSmThread != null) { // If we made the thread then quit looper which stops the thread. getLooper().quit(); @@ -841,7 +847,7 @@ public class StateMachine { mSm.mSmHandler = null; mSm = null; mMsg = null; - mProcessedMessages.cleanup(); + mLogRecords.cleanup(); mStateStack = null; mTempStateStack = null; mStateInfo.clear(); @@ -892,36 +898,38 @@ public class StateMachine { if (mDbg) { Log.d(TAG, "processMsg: " + curStateInfo.state.getName()); } - while (!curStateInfo.state.processMessage(msg)) { - /** - * Not processed - */ - curStateInfo = curStateInfo.parentStateInfo; - if (curStateInfo == null) { + + if (isQuit(msg)) { + transitionTo(mQuittingState); + } else { + while (!curStateInfo.state.processMessage(msg)) { /** - * No parents left so it's not handled + * Not processed */ - mSm.unhandledMessage(msg); - if (isQuit(msg)) { - transitionTo(mQuittingState); + curStateInfo = curStateInfo.parentStateInfo; + if (curStateInfo == null) { + /** + * No parents left so it's not handled + */ + mSm.unhandledMessage(msg); + break; + } + if (mDbg) { + Log.d(TAG, "processMsg: " + curStateInfo.state.getName()); } - break; - } - if (mDbg) { - Log.d(TAG, "processMsg: " + curStateInfo.state.getName()); } - } - /** - * Record that we processed the message - */ - if (mSm.recordProcessedMessage(msg)) { - if (curStateInfo != null) { - State orgState = mStateStack[mStateStackTopIndex].state; - mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state, - orgState); - } else { - mProcessedMessages.add(msg, mSm.getMessageInfo(msg), null, null); + /** + * Record that we processed the message + */ + if (mSm.recordLogRec(msg)) { + if (curStateInfo != null) { + State orgState = mStateStack[mStateStackTopIndex].state; + mLogRecords.add(msg, mSm.getLogRecString(msg), curStateInfo.state, + orgState); + } else { + mLogRecords.add(msg, mSm.getLogRecString(msg), null, null); + } } } } @@ -1141,13 +1149,19 @@ public class StateMachine { mDeferredMessages.add(newMsg); } - /** @see StateMachine#deferMessage(Message) */ + /** @see StateMachine#quit() */ private final void quit() { if (mDbg) Log.d(TAG, "quit:"); sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); } - /** @see StateMachine#isQuit(Message) */ + /** @see StateMachine#quitNow() */ + private final void quitNow() { + if (mDbg) Log.d(TAG, "abort:"); + sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); + } + + /** Validate that the message was sent by quit or abort. */ private final boolean isQuit(Message msg) { return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj); } @@ -1162,26 +1176,6 @@ public class StateMachine { mDbg = dbg; } - /** @see StateMachine#setProcessedMessagesSize(int) */ - private final void setProcessedMessagesSize(int maxSize) { - mProcessedMessages.setSize(maxSize); - } - - /** @see StateMachine#getProcessedMessagesSize() */ - private final int getProcessedMessagesSize() { - return mProcessedMessages.size(); - } - - /** @see StateMachine#getProcessedMessagesCount() */ - private final int getProcessedMessagesCount() { - return mProcessedMessages.count(); - } - - /** @see StateMachine#getProcessedMessageInfo(int) */ - private final ProcessedMessageInfo getProcessedMessageInfo(int index) { - return mProcessedMessages.get(index); - } - } private SmHandler mSmHandler; @@ -1282,8 +1276,8 @@ public class StateMachine { /** * transition to halt state. Upon returning * from processMessage we will exit all current - * states, execute the halting() method and then - * all subsequent messages haltedProcessMesage + * states, execute the onHalting() method and then + * for all subsequent messages haltedProcessMessage * will be called. */ protected final void transitionToHaltingState() { @@ -1303,7 +1297,6 @@ public class StateMachine { mSmHandler.deferMessage(msg); } - /** * Called when message wasn't handled * @@ -1325,7 +1318,7 @@ public class StateMachine { * transitionToHalting. All subsequent messages will invoke * {@link StateMachine#haltedProcessMessage(Message)} */ - protected void halting() { + protected void onHalting() { } /** @@ -1334,7 +1327,7 @@ public class StateMachine { * ignored. In addition, if this StateMachine created the thread, the thread will * be stopped after this method returns. */ - protected void quitting() { + protected void onQuitting() { } /** @@ -1345,33 +1338,77 @@ public class StateMachine { } /** - * Set size of messages to maintain and clears all current messages. + * Set number of log records to maintain and clears all current records. * * @param maxSize number of messages to maintain at anyone time. */ - public final void setProcessedMessagesSize(int maxSize) { - mSmHandler.setProcessedMessagesSize(maxSize); + public final void setLogRecSize(int maxSize) { + mSmHandler.mLogRecords.setSize(maxSize); + } + + /** + * @return number of log records + */ + public final int getLogRecSize() { + return mSmHandler.mLogRecords.size(); + } + + /** + * @return the total number of records processed + */ + public final int getLogRecCount() { + return mSmHandler.mLogRecords.count(); + } + + /** + * @return a log record + */ + public final LogRec getLogRec(int index) { + return mSmHandler.mLogRecords.get(index); + } + + /** + * Add the string to LogRecords. + * + * @param string + */ + protected void addLogRec(String string) { + mSmHandler.mLogRecords.add(null, string, null, null); } /** - * @return number of messages processed + * Add the string and state to LogRecords + * + * @param string + * @param state current state */ - public final int getProcessedMessagesSize() { - return mSmHandler.getProcessedMessagesSize(); + protected void addLogRec(String string, State state) { + mSmHandler.mLogRecords.add(null, string, state, null); } /** - * @return the total number of messages processed + * @return true if msg should be saved in the log, default is true. */ - public final int getProcessedMessagesCount() { - return mSmHandler.getProcessedMessagesCount(); + protected boolean recordLogRec(Message msg) { + return true; } /** - * @return a processed message information + * Return a string to be logged by LogRec, default + * is an empty string. Override if additional information is desired. + * + * @param msg that was processed + * @return information to be logged as a String */ - public final ProcessedMessageInfo getProcessedMessageInfo(int index) { - return mSmHandler.getProcessedMessageInfo(index); + protected String getLogRecString(Message msg) { + return ""; + } + + /** + * @return the string for msg.what + */ + protected String getWhatToString(int what) { + return null; } /** @@ -1548,43 +1585,23 @@ public class StateMachine { } /** - * Conditionally quit the looper and stop execution. - * - * This sends the SM_QUIT_MSG to the state machine and - * if not handled by any state's processMessage then the - * state machine will be stopped and no further messages - * will be processed. + * Quit the state machine after all currently queued up messages are processed. */ - public final void quit() { - // mSmHandler can be null if the state machine has quit. + protected final void quit() { + // mSmHandler can be null if the state machine is already stopped. if (mSmHandler == null) return; mSmHandler.quit(); } /** - * @return ture if msg is quit + * Quit the state machine immediately all currently queued messages will be discarded. */ - protected final boolean isQuit(Message msg) { - return mSmHandler.isQuit(msg); - } - - /** - * @return true if msg should be saved in ProcessedMessage, default is true. - */ - protected boolean recordProcessedMessage(Message msg) { - return true; - } + protected final void quitNow() { + // mSmHandler can be null if the state machine is already stopped. + if (mSmHandler == null) return; - /** - * Return message info to be logged by ProcessedMessageInfo, default - * is an empty string. Override if additional information is desired. - * - * @param msg that was processed - * @return information to be logged as a String - */ - protected String getMessageInfo(Message msg) { - return ""; + mSmHandler.quitNow(); } /** @@ -1629,9 +1646,9 @@ public class StateMachine { */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(getName() + ":"); - pw.println(" total messages=" + getProcessedMessagesCount()); - for (int i=0; i < getProcessedMessagesSize(); i++) { - pw.printf(" msg[%d]: %s\n", i, getProcessedMessageInfo(i)); + pw.println(" total records=" + getLogRecCount()); + for (int i=0; i < getLogRecSize(); i++) { + pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString(this)); pw.flush(); } pw.println("curState=" + getCurrentState().getName()); diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java index 24c7161..2fb81ac 100644 --- a/core/java/com/android/internal/widget/LockSettingsService.java +++ b/core/java/com/android/internal/widget/LockSettingsService.java @@ -24,6 +24,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Binder; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserId; import android.provider.Settings; import android.provider.Settings.Secure; @@ -303,12 +304,15 @@ public class LockSettingsService extends ILockSettings.Stub { } private void writeToDb(String key, String value, int userId) { + writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId); + } + + private void writeToDb(SQLiteDatabase db, String key, String value, int userId) { ContentValues cv = new ContentValues(); cv.put(COLUMN_KEY, key); cv.put(COLUMN_USERID, userId); cv.put(COLUMN_VALUE, value); - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); db.beginTransaction(); try { db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", @@ -359,6 +363,16 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void onCreate(SQLiteDatabase db) { createTable(db); + initializeDefaults(db); + } + + private void initializeDefaults(SQLiteDatabase db) { + // Get the lockscreen default from a system property, if available + boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default", + false); + if (lockScreenDisable) { + writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0); + } } @Override |
