diff options
Diffstat (limited to 'core')
125 files changed, 1644 insertions, 3471 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index a6658cc..d207a0a 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1471,6 +1471,24 @@ public class ActivityManager { } /** + * Returns the usage statistics of each installed package. + * + * @hide + */ + public PkgUsageStats[] getAllPackageUsageStats() { + try { + IUsageStats usageStatsService = IUsageStats.Stub.asInterface( + ServiceManager.getService("usagestats")); + if (usageStatsService != null) { + return usageStatsService.getAllPkgUsageStats(); + } + } catch (RemoteException e) { + Log.w(TAG, "Could not query usage stats", e); + } + return new PkgUsageStats[0]; + } + + /** * @param userid the user's id. Zero indicates the default user * @hide */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index eee14fb..ee04729 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4093,11 +4093,6 @@ public final class ActivityThread { }); } - private final void detach() - { - sThreadLocal.set(null); - } - public static final ActivityThread systemMain() { HardwareRenderer.disable(); ActivityThread thread = new ActivityThread(); @@ -4105,10 +4100,9 @@ public final class ActivityThread { return thread; } - public final void installSystemProviders(List providers) { + public final void installSystemProviders(List<ProviderInfo> providers) { if (providers != null) { - installContentProviders(mInitialApplication, - (List<ProviderInfo>)providers); + installContentProviders(mInitialApplication, providers); } } @@ -4147,14 +4141,6 @@ public final class ActivityThread { Looper.loop(); - if (Process.supportsProcesses()) { - throw new RuntimeException("Main thread loop unexpectedly exited"); - } - - thread.detach(); - String name = (thread.mInitialApplication != null) - ? thread.mInitialApplication.getPackageName() - : "<unknown>"; - Slog.i(TAG, "Main thread of " + name + " is now exiting"); + throw new RuntimeException("Main thread loop unexpectedly exited"); } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 8749d3e..d2323e7 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1163,9 +1163,6 @@ class ContextImpl extends Context { throw new IllegalArgumentException("permission is null"); } - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } try { return ActivityManagerNative.getDefault().checkPermission( permission, pid, uid); @@ -1180,9 +1177,6 @@ class ContextImpl extends Context { throw new IllegalArgumentException("permission is null"); } - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } int pid = Binder.getCallingPid(); if (pid != Process.myPid()) { return checkPermission(permission, pid, @@ -1263,9 +1257,6 @@ class ContextImpl extends Context { @Override public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } try { return ActivityManagerNative.getDefault().checkUriPermission( uri, pid, uid, modeFlags); @@ -1276,9 +1267,6 @@ class ContextImpl extends Context { @Override public int checkCallingUriPermission(Uri uri, int modeFlags) { - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } int pid = Binder.getCallingPid(); if (pid != Process.myPid()) { return checkUriPermission(uri, pid, diff --git a/core/java/android/app/backup/IRestoreSession.aidl b/core/java/android/app/backup/IRestoreSession.aidl index 1dddbb0..14731ee 100644 --- a/core/java/android/app/backup/IRestoreSession.aidl +++ b/core/java/android/app/backup/IRestoreSession.aidl @@ -52,6 +52,25 @@ interface IRestoreSession { int restoreAll(long token, IRestoreObserver observer); /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + */ + int restoreSome(long token, IRestoreObserver observer, in String[] packages); + + /** * Restore a single application from backup. The data will be restored from the * current backup dataset if the given package has stored data there, or from * the dataset used during the last full device setup operation if the current diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java index 24ddb99..7181c61 100644 --- a/core/java/android/app/backup/RestoreSession.java +++ b/core/java/android/app/backup/RestoreSession.java @@ -87,6 +87,40 @@ public class RestoreSession { } /** + * Restore select packages from the given set onto the device, replacing the + * current data of any app contained in the set with the data previously + * backed up. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @return Zero on success, nonzero on error. The observer will only receive + * progress callbacks if this method returned zero. + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + * @param packages The set of packages for which to attempt a restore. Regardless of + * the contents of the actual back-end dataset named by {@code token}, only + * applications mentioned in this list will have their data restored. + * + * @hide + */ + public int restoreSome(long token, RestoreObserver observer, String[] packages) { + int err = -1; + if (mObserver != null) { + Log.d(TAG, "restoreAll() called during active restore"); + return -1; + } + mObserver = new RestoreObserverWrapper(mContext, observer); + try { + err = mBinder.restoreSome(token, mObserver, packages); + } catch (RemoteException e) { + Log.d(TAG, "Can't contact server to restore packages"); + } + return err; + } + + /** * Restore a single application from backup. The data will be restored from the * current backup dataset if the given package has stored data there, or from * the dataset used during the last full device setup operation if the current diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java index 52efc07..0a01dcf 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.annotation.SdkConstant; import android.content.Context; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -67,9 +66,6 @@ public final class BluetoothHealth implements BluetoothProfile { */ public static final int CHANNEL_TYPE_ANY = 12; - private final ArrayList<BluetoothHealthAppConfiguration> mAppConfigs = - new ArrayList<BluetoothHealthAppConfiguration>(); - /** * Register an application configuration that acts as a Health SINK. * This is the configuration that will be used to communicate with health devices @@ -86,7 +82,7 @@ public final class BluetoothHealth implements BluetoothProfile { * @return If true, callback will be called. */ public boolean registerSinkAppConfiguration(String name, int dataType, - IBluetoothHealthCallback callback) { + BluetoothHealthCallback callback) { if (!isEnabled() || name == null) return false; if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); @@ -111,18 +107,18 @@ public final class BluetoothHealth implements BluetoothProfile { * @hide */ public boolean registerAppConfiguration(String name, int dataType, int role, - int channelType, IBluetoothHealthCallback callback) { + int channelType, BluetoothHealthCallback callback) { boolean result = false; if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; if (DBG) log("registerApplication(" + name + ":" + dataType + ")"); + BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback); BluetoothHealthAppConfiguration config = - new BluetoothHealthAppConfiguration(name, dataType, role, channelType, - callback); + new BluetoothHealthAppConfiguration(name, dataType, role, channelType); if (mService != null) { try { - result = mService.registerAppConfiguration(config); + result = mService.registerAppConfiguration(config, wrapper); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -130,8 +126,6 @@ public final class BluetoothHealth implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - - if (result) mAppConfigs.add(config); return result; } @@ -147,7 +141,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; - if (mService != null && isEnabled() && isValidAppConfig(config)) { + if (mService != null && isEnabled() && config != null) { try { result = mService.unregisterAppConfiguration(config); } catch (RemoteException e) { @@ -157,26 +151,26 @@ public final class BluetoothHealth implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - if (result) mAppConfigs.remove(config); + return result; } /** * Connect to a health device which has the {@link #SOURCE_ROLE}. - * This is an asynchrnous call. If this function returns true, the callback + * This is an asynchronous call. If this function returns true, the callback * associated with the application configuration will be called. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. */ public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.connectChannelToSource(device, config); } catch (RemoteException e) { @@ -197,15 +191,15 @@ public final class BluetoothHealth implements BluetoothProfile { *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. * @hide */ public boolean connectChannelToSink(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelType) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.connectChannelToSink(device, config, channelType); } catch (RemoteException e) { @@ -226,8 +220,8 @@ public final class BluetoothHealth implements BluetoothProfile { *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @param fd The file descriptor that was associated with the channel. * @return If true, the callback associated with the application config will be called. * @hide @@ -235,7 +229,7 @@ public final class BluetoothHealth implements BluetoothProfile { public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.disconnectChannel(device, config, fd); } catch (RemoteException e) { @@ -262,7 +256,7 @@ public final class BluetoothHealth implements BluetoothProfile { public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.getMainChannelFd(device, config); } catch (RemoteException e) { @@ -290,6 +284,7 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ + @Override public int getConnectionState(BluetoothDevice device) { if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -317,6 +312,7 @@ public final class BluetoothHealth implements BluetoothProfile { * local adapter. * @return List of devices. The list will be empty on error. */ + @Override public List<BluetoothDevice> getConnectedDevices() { if (mService != null && isEnabled()) { try { @@ -348,6 +344,7 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ + @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (mService != null && isEnabled()) { try { @@ -361,6 +358,27 @@ public final class BluetoothHealth implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } + private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub { + private BluetoothHealthCallback mCallback; + + public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) { + mCallback = callback; + } + + @Override + public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, + int status) { + mCallback.onHealthAppConfigurationStatusChange(config, status); + } + + @Override + public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, + BluetoothDevice device, int prevState, int newState, + ParcelFileDescriptor fd) { + mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd); + } + } + /** Health Channel Connection State - Disconnected */ public static final int STATE_CHANNEL_DISCONNECTED = 0; /** Health Channel Connection State - Connecting */ @@ -379,7 +397,6 @@ public final class BluetoothHealth implements BluetoothProfile { /** Health App Configuration un-registration failure */ public static final int APPLICATION_UNREGISTRATION_FAILURE = 3; - private Context mContext; private ServiceListener mServiceListener; private IBluetooth mService; BluetoothAdapter mAdapter; @@ -420,14 +437,8 @@ public final class BluetoothHealth implements BluetoothProfile { return false; } - private boolean isValidAppConfig(BluetoothHealthAppConfiguration config) { - if (!mAppConfigs.isEmpty() && mAppConfigs.contains(config)) return true; - log("Not a valid config: " + config); - return false; - } - private boolean checkAppParam(String name, int role, int channelType, - IBluetoothHealthCallback callback) { + BluetoothHealthCallback callback) { if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING && diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java index b87aea5..7020249 100644 --- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -17,7 +17,6 @@ package android.bluetooth; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -34,21 +33,18 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { private final int mDataType; private final int mRole; private final int mChannelType; - private final IBluetoothHealthCallback mCallback; /** * Constructor to register the SINK role * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device - * @param callback Callback associated with the application configuration. */ - BluetoothHealthAppConfiguration(String name, int dataType, IBluetoothHealthCallback callback) { + BluetoothHealthAppConfiguration(String name, int dataType) { mName = name; mDataType = dataType; mRole = BluetoothHealth.SINK_ROLE; mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY; - mCallback = callback; } /** @@ -56,17 +52,15 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device - * @param role {@link BluetoothHealth.SOURCE_ROLE} or - * {@link BluetoothHealth.SINK_ROLE} - * @param callback Callback associated with the application configuration. + * @param role {@link BluetoothHealth#SOURCE_ROLE} or + * {@link BluetoothHealth#SINK_ROLE} */ - BluetoothHealthAppConfiguration(String name, int dataType, int role, int channelType, - IBluetoothHealthCallback callback) { + BluetoothHealthAppConfiguration(String name, int dataType, int role, int + channelType) { mName = name; mDataType = dataType; mRole = role; mChannelType = channelType; - mCallback = callback; } @Override @@ -77,8 +71,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { return mName.equals(config.getName()) && mDataType == config.getDataType() && mRole == config.getRole() && - mChannelType == config.getChannelType() && - mCallback.equals(config.getCallback()); + mChannelType == config.getChannelType(); } return false; } @@ -90,7 +83,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { result = 31 * result + mDataType; result = 31 * result + mRole; result = 31 * result + mChannelType; - result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0); return result; } @@ -98,9 +90,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { public String toString() { return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " + - mChannelType + ",callback=" + mCallback +"]"; + mChannelType + "]"; } + @Override public int describeContents() { return 0; } @@ -144,37 +137,31 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { return mChannelType; } - /** - * Return the callback associated with this application configuration. - * - * @return IBluetoothHealthCallback - */ - public IBluetoothHealthCallback getCallback() { - return mCallback; - } - public static final Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR = new Parcelable.Creator<BluetoothHealthAppConfiguration>() { + @Override public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { String name = in.readString(); int type = in.readInt(); int role = in.readInt(); int channelType = in.readInt(); - IBluetoothHealthCallback callback = - IBluetoothHealthCallback.Stub.asInterface(in.readStrongBinder()); - return new BluetoothHealthAppConfiguration(name, type, role, channelType, - callback); + return new BluetoothHealthAppConfiguration(name, type, role, + channelType); } + + @Override public BluetoothHealthAppConfiguration[] newArray(int size) { return new BluetoothHealthAppConfiguration[size]; } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mName); out.writeInt(mDataType); out.writeInt(mRole); out.writeInt(mChannelType); - out.writeStrongInterface(mCallback); } + + } diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java new file mode 100644 index 0000000..0d11bb5 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHealthCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.bluetooth; + +import android.os.ParcelFileDescriptor; +import android.util.Log; + +/** + * This class is used for all the {@link BluetoothHealth} callbacks. + * @hide + */ +public abstract class BluetoothHealthCallback { + + private static final String TAG = "BluetoothHealthCallback"; + + public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, + int status) { + Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + " Status:" + status); + } + + public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, + BluetoothDevice device, int prevState, int newState, + ParcelFileDescriptor fd) { + Log.d(TAG, "onHealthChannelStateChange: " + config + " Device:" + device + + "PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd); + } +} diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 83e59e2..acce182 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -62,6 +62,7 @@ public final class BluetoothServerSocket implements Closeable { /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; + private final int mChannel; /** * Construct a socket for incoming connections. @@ -74,6 +75,7 @@ public final class BluetoothServerSocket implements Closeable { */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) throws IOException { + mChannel = port; mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); } @@ -125,4 +127,12 @@ public final class BluetoothServerSocket implements Closeable { mHandler = handler; mMessage = message; } + + /** + * Returns the channel on which this socket is bound. + * @hide + */ + public int getChannel() { + return mChannel; + } } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 6ca6c2e..183772d 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothHealthCallback; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealthAppConfiguration; import android.os.ParcelUuid; @@ -102,7 +103,8 @@ interface IBluetooth boolean disconnectPanDevice(in BluetoothDevice device); // HDP profile APIs - boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config); + boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, + in IBluetoothHealthCallback callback); boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 9d40c42..a4c66e4 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -43,17 +43,41 @@ public class NetworkStats implements Parcelable { /** {@link #tag} value for without tag. */ public static final int TAG_NONE = 0; + // TODO: move public fields to Entry accessors, then undeprecate + // TODO: refactor rx/tx to rxBytes/txBytes + /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. */ + @Deprecated public final long elapsedRealtime; + @Deprecated public int size; + @Deprecated public String[] iface; + @Deprecated public int[] uid; + @Deprecated public int[] tag; + @Deprecated public long[] rx; + @Deprecated + public long[] rxPackets; + @Deprecated public long[] tx; + @Deprecated + public long[] txPackets; + + public static class Entry { + public String iface; + public int uid; + public int tag; + public long rxBytes; + public long rxPackets; + public long txBytes; + public long txPackets; + } public NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; @@ -62,7 +86,9 @@ public class NetworkStats implements Parcelable { this.uid = new int[initialSize]; this.tag = new int[initialSize]; this.rx = new long[initialSize]; + this.rxPackets = new long[initialSize]; this.tx = new long[initialSize]; + this.txPackets = new long[initialSize]; } public NetworkStats(Parcel parcel) { @@ -72,38 +98,82 @@ public class NetworkStats implements Parcelable { uid = parcel.createIntArray(); tag = parcel.createIntArray(); rx = parcel.createLongArray(); + rxPackets = parcel.createLongArray(); tx = parcel.createLongArray(); + txPackets = parcel.createLongArray(); } /** * Add new stats entry with given values. */ public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { + final Entry entry = new Entry(); + entry.iface = iface; + entry.uid = uid; + entry.tag = tag; + entry.rxBytes = rx; + entry.txBytes = tx; + return addValues(entry); + } + + /** + * Add new stats entry, copying from given {@link Entry}. The {@link Entry} + * object can be recycled across multiple calls. + */ + public NetworkStats addValues(Entry entry) { if (size >= this.iface.length) { - final int newLength = Math.max(this.iface.length, 10) * 3 / 2; - this.iface = Arrays.copyOf(this.iface, newLength); - this.uid = Arrays.copyOf(this.uid, newLength); - this.tag = Arrays.copyOf(this.tag, newLength); - this.rx = Arrays.copyOf(this.rx, newLength); - this.tx = Arrays.copyOf(this.tx, newLength); + final int newLength = Math.max(iface.length, 10) * 3 / 2; + iface = Arrays.copyOf(iface, newLength); + uid = Arrays.copyOf(uid, newLength); + tag = Arrays.copyOf(tag, newLength); + rx = Arrays.copyOf(rx, newLength); + rxPackets = Arrays.copyOf(rxPackets, newLength); + tx = Arrays.copyOf(tx, newLength); + txPackets = Arrays.copyOf(txPackets, newLength); } - this.iface[size] = iface; - this.uid[size] = uid; - this.tag[size] = tag; - this.rx[size] = rx; - this.tx[size] = tx; + iface[size] = entry.iface; + uid[size] = entry.uid; + tag[size] = entry.tag; + rx[size] = entry.rxBytes; + rxPackets[size] = entry.rxPackets; + tx[size] = entry.txBytes; + txPackets[size] = entry.txPackets; size++; return this; } /** + * Return specific stats entry. + */ + public Entry getValues(int i, Entry recycle) { + final Entry entry = recycle != null ? recycle : new Entry(); + entry.iface = iface[i]; + entry.uid = uid[i]; + entry.tag = tag[i]; + entry.rxBytes = rx[i]; + entry.rxPackets = rxPackets[i]; + entry.txBytes = tx[i]; + entry.txPackets = txPackets[i]; + return entry; + } + + public long getElapsedRealtime() { + return elapsedRealtime; + } + + public int size() { + return size; + } + + /** * Combine given values with an existing row, or create a new row if * {@link #findIndex(String, int, int)} is unable to find match. Can also be * used to subtract values from existing rows. */ public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) { + // TODO: extent to accept rxPackets/txPackets final int i = findIndex(iface, uid, tag); if (i == -1) { // only create new entry when positive contribution @@ -199,30 +269,41 @@ public class NetworkStats implements Parcelable { } // result will have our rows, and elapsed time between snapshots + final Entry entry = new Entry(); final NetworkStats result = new NetworkStats(deltaRealtime, size); for (int i = 0; i < size; i++) { - final String iface = this.iface[i]; - final int uid = this.uid[i]; - final int tag = this.tag[i]; + entry.iface = iface[i]; + entry.uid = uid[i]; + entry.tag = tag[i]; // find remote row that matches, and subtract - final int j = value.findIndex(iface, uid, tag); + final int j = value.findIndex(entry.iface, entry.uid, entry.tag); if (j == -1) { // newly appearing row, return entire value - result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]); + entry.rxBytes = rx[i]; + entry.rxPackets = rxPackets[i]; + entry.txBytes = tx[i]; + entry.txPackets = txPackets[i]; } else { // existing row, subtract remote value - long rx = this.rx[i] - value.rx[j]; - long tx = this.tx[i] - value.tx[j]; - if (enforceMonotonic && (rx < 0 || tx < 0)) { + entry.rxBytes = rx[i] - value.rx[j]; + entry.rxPackets = rxPackets[i] - value.rxPackets[j]; + entry.txBytes = tx[i] - value.tx[j]; + entry.txPackets = txPackets[i] - value.txPackets[j]; + if (enforceMonotonic + && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 + || entry.txPackets < 0)) { throw new IllegalArgumentException("found non-monotonic values"); } if (clampNegative) { - rx = Math.max(0, rx); - tx = Math.max(0, tx); + entry.rxBytes = Math.max(0, entry.rxBytes); + entry.rxPackets = Math.max(0, entry.rxPackets); + entry.txBytes = Math.max(0, entry.txBytes); + entry.txPackets = Math.max(0, entry.txPackets); } - result.addEntry(iface, uid, tag, rx, tx); } + + result.addValues(entry); } return result; @@ -235,13 +316,15 @@ public class NetworkStats implements Parcelable { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); - for (int i = 0; i < iface.length; i++) { + for (int i = 0; i < size; i++) { pw.print(prefix); pw.print(" iface="); pw.print(iface[i]); pw.print(" uid="); pw.print(uid[i]); pw.print(" tag="); pw.print(tag[i]); - pw.print(" rx="); pw.print(rx[i]); - pw.print(" tx="); pw.println(tx[i]); + pw.print(" rxBytes="); pw.print(rx[i]); + pw.print(" rxPackets="); pw.print(rxPackets[i]); + pw.print(" txBytes="); pw.print(tx[i]); + pw.print(" txPackets="); pw.println(txPackets[i]); } } @@ -265,7 +348,9 @@ public class NetworkStats implements Parcelable { dest.writeIntArray(uid); dest.writeIntArray(tag); dest.writeLongArray(rx); + dest.writeLongArray(rxPackets); dest.writeLongArray(tx); + dest.writeLongArray(txPackets); } public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 8a678d6..76534ef 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -38,8 +38,22 @@ public class NetworkUtils { /** Bring the named network interface down. */ public native static int disableInterface(String interfaceName); - /** Reset any sockets that are connected via the named interface. */ - public native static int resetConnections(String interfaceName); + /** Setting bit 0 indicates reseting of IPv4 addresses required */ + public static final int RESET_IPV4_ADDRESSES = 0x01; + + /** Setting bit 1 indicates reseting of IPv4 addresses required */ + public static final int RESET_IPV6_ADDRESSES = 0x02; + + /** Reset all addresses */ + public static final int RESET_ALL_ADDRESSES = RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES; + + /** + * Reset IPv6 or IPv4 sockets that are connected via the named interface. + * + * @param interfaceName is the interface to reset + * @param mask {@see #RESET_IPV4_ADDRESSES} and {@see #RESET_IPV6_ADDRESSES} + */ + public native static int resetConnections(String interfaceName, int mask); /** * Start the DHCP client daemon, in order to have it request addresses diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 3edd692..c0be664 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -86,9 +86,7 @@ public class Looper { public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); - if (Process.supportsProcesses()) { - myLooper().mQueue.mQuitAllowed = false; - } + myLooper().mQueue.mQuitAllowed = false; } private synchronized static void setMainLooper(Looper looper) { diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 673b187..5b1f563 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -266,84 +266,29 @@ public class Process { * @param uid The user-id under which the process will run. * @param gid The group-id under which the process will run. * @param gids Additional group-ids associated with the process. - * @param enableDebugger True if debugging should be enabled for this process. + * @param debugFlags Additional flags. + * @param targetSdkVersion The target SDK version for the app. * @param zygoteArgs Additional arguments to supply to the zygote process. * - * @return int If > 0 the pid of the new process; if 0 the process is - * being emulated by a thread + * @return An object that describes the result of the attempt to start the process. * @throws RuntimeException on fatal start failure * * {@hide} */ - public static final int start(final String processClass, + public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, - int debugFlags, - String[] zygoteArgs) - { - if (supportsProcesses()) { - try { - return startViaZygote(processClass, niceName, uid, gid, gids, - debugFlags, zygoteArgs); - } catch (ZygoteStartFailedEx ex) { - Log.e(LOG_TAG, - "Starting VM process through Zygote failed"); - throw new RuntimeException( - "Starting VM process through Zygote failed", ex); - } - } else { - // Running in single-process mode - - Runnable runnable = new Runnable() { - public void run() { - Process.invokeStaticMain(processClass); - } - }; - - // Thread constructors must not be called with null names (see spec). - if (niceName != null) { - new Thread(runnable, niceName).start(); - } else { - new Thread(runnable).start(); - } - - return 0; - } - } - - /** - * Start a new process. Don't supply a custom nice name. - * {@hide} - */ - public static final int start(String processClass, int uid, int gid, - int[] gids, int debugFlags, String[] zygoteArgs) { - return start(processClass, "", uid, gid, gids, - debugFlags, zygoteArgs); - } - - private static void invokeStaticMain(String className) { - Class cl; - Object args[] = new Object[1]; - - args[0] = new String[0]; //this is argv - + int debugFlags, int targetSdkVersion, + String[] zygoteArgs) { try { - cl = Class.forName(className); - cl.getMethod("main", new Class[] { String[].class }) - .invoke(null, args); - } catch (Exception ex) { - // can be: ClassNotFoundException, - // NoSuchMethodException, SecurityException, - // IllegalAccessException, IllegalArgumentException - // InvocationTargetException - // or uncaught exception from main() - - Log.e(LOG_TAG, "Exception invoking static main on " - + className, ex); - - throw new RuntimeException(ex); + return startViaZygote(processClass, niceName, uid, gid, gids, + debugFlags, targetSdkVersion, zygoteArgs); + } catch (ZygoteStartFailedEx ex) { + Log.e(LOG_TAG, + "Starting VM process through Zygote failed"); + throw new RuntimeException( + "Starting VM process through Zygote failed", ex); } - } /** retry interval for opening a zygote socket */ @@ -430,14 +375,11 @@ public class Process { * and returns the child's pid. Please note: the present implementation * replaces newlines in the argument list with spaces. * @param args argument list - * @return PID of new child process + * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason */ - private static int zygoteSendArgsAndGetPid(ArrayList<String> args) + private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args) throws ZygoteStartFailedEx { - - int pid; - openZygoteSocketIfNeeded(); try { @@ -448,7 +390,8 @@ public class Process { * b) a number of newline-separated argument strings equal to count * * After the zygote process reads these it will write the pid of - * the child or -1 on failure. + * the child or -1 on failure, followed by boolean to + * indicate whether a wrapper process was used. */ sZygoteWriter.write(Integer.toString(args.size())); @@ -468,11 +411,13 @@ public class Process { sZygoteWriter.flush(); // Should there be a timeout on this? - pid = sZygoteInputStream.readInt(); - - if (pid < 0) { + ProcessStartResult result = new ProcessStartResult(); + result.pid = sZygoteInputStream.readInt(); + if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } + result.usingWrapper = sZygoteInputStream.readBoolean(); + return result; } catch (IOException ex) { try { if (sZygoteSocket != null) { @@ -487,8 +432,6 @@ public class Process { throw new ZygoteStartFailedEx(ex); } - - return pid; } /** @@ -500,20 +443,19 @@ public class Process { * @param gid a POSIX gid that the new process shuold setgid() to * @param gids null-ok; a list of supplementary group IDs that the * new process should setgroup() to. - * @param enableDebugger True if debugging should be enabled for this process. + * @param debugFlags Additional flags. + * @param targetSdkVersion The target SDK version for the app. * @param extraArgs Additional arguments to supply to the zygote process. - * @return PID + * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason */ - private static int startViaZygote(final String processClass, + private static ProcessStartResult startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, - int debugFlags, + int debugFlags, int targetSdkVersion, String[] extraArgs) throws ZygoteStartFailedEx { - int pid; - synchronized(Process.class) { ArrayList<String> argsForZygote = new ArrayList<String>(); @@ -537,6 +479,7 @@ public class Process { if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { argsForZygote.add("--enable-assert"); } + argsForZygote.add("--target-sdk-version=" + targetSdkVersion); //TODO optionally enable debuger //argsForZygote.add("--enable-debugger"); @@ -568,15 +511,9 @@ public class Process { argsForZygote.add(arg); } } - - pid = zygoteSendArgsAndGetPid(argsForZygote); - } - if (pid <= 0) { - throw new ZygoteStartFailedEx("zygote start failed:" + pid); + return zygoteSendArgsAndGetResult(argsForZygote); } - - return pid; } /** @@ -736,8 +673,13 @@ public class Process { * * @return Returns true if the system can run in multiple processes, else * false if everything is running in a single process. + * + * @deprecated This method always returns true. Do not use. */ - public static final native boolean supportsProcesses(); + @Deprecated + public static final boolean supportsProcesses() { + return true; + } /** * Set the out-of-memory badness adjustment for a process. @@ -855,4 +797,21 @@ public class Process { * @hide */ public static final native long getPss(int pid); + + /** + * Specifies the outcome of having started a process. + * @hide + */ + public static final class ProcessStartResult { + /** + * The PID of the newly started process. + * Always >= 0. (If the start failed, an exception will have been thrown instead.) + */ + public int pid; + + /** + * True if the process was started with a wrapper attached. + */ + public boolean usingWrapper; + } } diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index b721665..1af24f4 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -114,7 +114,7 @@ public final class ServiceManager { * @hide */ public static void initServiceCache(Map<String, IBinder> cache) { - if (sCache.size() != 0 && Process.supportsProcesses()) { + if (sCache.size() != 0) { throw new IllegalStateException("setServiceCache may only be called once"); } sCache.putAll(cache); diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 792e4c1..60900e1 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -34,6 +34,8 @@ public class StorageVolume implements Parcelable { private final int mMtpReserveSpace; private final boolean mAllowMassStorage; private int mStorageId; + // maximum file size for the storage, or zero for no limit + private final long mMaxFileSize; // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING, // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED, @@ -41,18 +43,20 @@ public class StorageVolume implements Parcelable { public static final String EXTRA_STORAGE_VOLUME = "storage_volume"; public StorageVolume(String path, String description, boolean removable, - boolean emulated, int mtpReserveSpace, boolean allowMassStorage) { + boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize) { mPath = path; mDescription = description; mRemovable = removable; mEmulated = emulated; mMtpReserveSpace = mtpReserveSpace; mAllowMassStorage = allowMassStorage; + mMaxFileSize = maxFileSize; } // for parcelling only private StorageVolume(String path, String description, boolean removable, - boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) { + boolean emulated, int mtpReserveSpace, int storageId, + boolean allowMassStorage, long maxFileSize) { mPath = path; mDescription = description; mRemovable = removable; @@ -60,6 +64,7 @@ public class StorageVolume implements Parcelable { mMtpReserveSpace = mtpReserveSpace; mAllowMassStorage = allowMassStorage; mStorageId = storageId; + mMaxFileSize = maxFileSize; } /** @@ -142,6 +147,15 @@ public class StorageVolume implements Parcelable { return mAllowMassStorage; } + /** + * Returns maximum file size for the volume, or zero if it is unbounded. + * + * @return maximum file size + */ + public long getMaxFileSize() { + return mMaxFileSize; + } + @Override public boolean equals(Object obj) { if (obj instanceof StorageVolume && mPath != null) { @@ -171,9 +185,10 @@ public class StorageVolume implements Parcelable { int storageId = in.readInt(); int mtpReserveSpace = in.readInt(); int allowMassStorage = in.readInt(); + long maxFileSize = in.readLong(); return new StorageVolume(path, description, - removable == 1, emulated == 1, - mtpReserveSpace, storageId, allowMassStorage == 1); + removable == 1, emulated == 1, mtpReserveSpace, + storageId, allowMassStorage == 1, maxFileSize); } public StorageVolume[] newArray(int size) { @@ -193,5 +208,6 @@ public class StorageVolume implements Parcelable { parcel.writeInt(mStorageId); parcel.writeInt(mMtpReserveSpace); parcel.writeInt(mAllowMassStorage ? 1 : 0); + parcel.writeLong(mMaxFileSize); } } diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java deleted file mode 100644 index 128b697..0000000 --- a/core/java/android/pim/EventRecurrence.java +++ /dev/null @@ -1,892 +0,0 @@ -/* - * Copyright (C) 2006 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.pim; - -import android.text.TextUtils; -import android.text.format.Time; -import android.util.Log; -import android.util.TimeFormatException; - -import java.util.Calendar; -import java.util.HashMap; - -/** - * Event recurrence utility functions. - */ -public class EventRecurrence { - private static String TAG = "EventRecur"; - - public static final int SECONDLY = 1; - public static final int MINUTELY = 2; - public static final int HOURLY = 3; - public static final int DAILY = 4; - public static final int WEEKLY = 5; - public static final int MONTHLY = 6; - public static final int YEARLY = 7; - - public static final int SU = 0x00010000; - public static final int MO = 0x00020000; - public static final int TU = 0x00040000; - public static final int WE = 0x00080000; - public static final int TH = 0x00100000; - public static final int FR = 0x00200000; - public static final int SA = 0x00400000; - - public Time startDate; // set by setStartDate(), not parse() - - public int freq; // SECONDLY, MINUTELY, etc. - public String until; - public int count; - public int interval; - public int wkst; // SU, MO, TU, etc. - - /* lists with zero entries may be null references */ - public int[] bysecond; - public int bysecondCount; - public int[] byminute; - public int byminuteCount; - public int[] byhour; - public int byhourCount; - public int[] byday; - public int[] bydayNum; - public int bydayCount; - public int[] bymonthday; - public int bymonthdayCount; - public int[] byyearday; - public int byyeardayCount; - public int[] byweekno; - public int byweeknoCount; - public int[] bymonth; - public int bymonthCount; - public int[] bysetpos; - public int bysetposCount; - - /** maps a part string to a parser object */ - private static HashMap<String,PartParser> sParsePartMap; - static { - sParsePartMap = new HashMap<String,PartParser>(); - sParsePartMap.put("FREQ", new ParseFreq()); - sParsePartMap.put("UNTIL", new ParseUntil()); - sParsePartMap.put("COUNT", new ParseCount()); - sParsePartMap.put("INTERVAL", new ParseInterval()); - sParsePartMap.put("BYSECOND", new ParseBySecond()); - sParsePartMap.put("BYMINUTE", new ParseByMinute()); - sParsePartMap.put("BYHOUR", new ParseByHour()); - sParsePartMap.put("BYDAY", new ParseByDay()); - sParsePartMap.put("BYMONTHDAY", new ParseByMonthDay()); - sParsePartMap.put("BYYEARDAY", new ParseByYearDay()); - sParsePartMap.put("BYWEEKNO", new ParseByWeekNo()); - sParsePartMap.put("BYMONTH", new ParseByMonth()); - sParsePartMap.put("BYSETPOS", new ParseBySetPos()); - sParsePartMap.put("WKST", new ParseWkst()); - } - - /* values for bit vector that keeps track of what we have already seen */ - private static final int PARSED_FREQ = 1 << 0; - private static final int PARSED_UNTIL = 1 << 1; - private static final int PARSED_COUNT = 1 << 2; - private static final int PARSED_INTERVAL = 1 << 3; - private static final int PARSED_BYSECOND = 1 << 4; - private static final int PARSED_BYMINUTE = 1 << 5; - private static final int PARSED_BYHOUR = 1 << 6; - private static final int PARSED_BYDAY = 1 << 7; - private static final int PARSED_BYMONTHDAY = 1 << 8; - private static final int PARSED_BYYEARDAY = 1 << 9; - private static final int PARSED_BYWEEKNO = 1 << 10; - private static final int PARSED_BYMONTH = 1 << 11; - private static final int PARSED_BYSETPOS = 1 << 12; - private static final int PARSED_WKST = 1 << 13; - - /** maps a FREQ value to an integer constant */ - private static final HashMap<String,Integer> sParseFreqMap = new HashMap<String,Integer>(); - static { - sParseFreqMap.put("SECONDLY", SECONDLY); - sParseFreqMap.put("MINUTELY", MINUTELY); - sParseFreqMap.put("HOURLY", HOURLY); - sParseFreqMap.put("DAILY", DAILY); - sParseFreqMap.put("WEEKLY", WEEKLY); - sParseFreqMap.put("MONTHLY", MONTHLY); - sParseFreqMap.put("YEARLY", YEARLY); - } - - /** maps a two-character weekday string to an integer constant */ - private static final HashMap<String,Integer> sParseWeekdayMap = new HashMap<String,Integer>(); - static { - sParseWeekdayMap.put("SU", SU); - sParseWeekdayMap.put("MO", MO); - sParseWeekdayMap.put("TU", TU); - sParseWeekdayMap.put("WE", WE); - sParseWeekdayMap.put("TH", TH); - sParseWeekdayMap.put("FR", FR); - sParseWeekdayMap.put("SA", SA); - } - - /** If set, allow lower-case recurrence rule strings. Minor performance impact. */ - private static final boolean ALLOW_LOWER_CASE = false; - - /** If set, validate the value of UNTIL parts. Minor performance impact. */ - private static final boolean VALIDATE_UNTIL = false; - - /** If set, require that only one of {UNTIL,COUNT} is present. Breaks compat w/ old parser. */ - private static final boolean ONLY_ONE_UNTIL_COUNT = false; - - - /** - * Thrown when a recurrence string provided can not be parsed according - * to RFC2445. - */ - public static class InvalidFormatException extends RuntimeException { - InvalidFormatException(String s) { - super(s); - } - } - - - public void setStartDate(Time date) { - startDate = date; - } - - /** - * Converts one of the Calendar.SUNDAY constants to the SU, MO, etc. - * constants. btw, I think we should switch to those here too, to - * get rid of this function, if possible. - */ - public static int calendarDay2Day(int day) - { - switch (day) - { - case Calendar.SUNDAY: - return SU; - case Calendar.MONDAY: - return MO; - case Calendar.TUESDAY: - return TU; - case Calendar.WEDNESDAY: - return WE; - case Calendar.THURSDAY: - return TH; - case Calendar.FRIDAY: - return FR; - case Calendar.SATURDAY: - return SA; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - - public static int timeDay2Day(int day) - { - switch (day) - { - case Time.SUNDAY: - return SU; - case Time.MONDAY: - return MO; - case Time.TUESDAY: - return TU; - case Time.WEDNESDAY: - return WE; - case Time.THURSDAY: - return TH; - case Time.FRIDAY: - return FR; - case Time.SATURDAY: - return SA; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - public static int day2TimeDay(int day) - { - switch (day) - { - case SU: - return Time.SUNDAY; - case MO: - return Time.MONDAY; - case TU: - return Time.TUESDAY; - case WE: - return Time.WEDNESDAY; - case TH: - return Time.THURSDAY; - case FR: - return Time.FRIDAY; - case SA: - return Time.SATURDAY; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - - /** - * Converts one of the SU, MO, etc. constants to the Calendar.SUNDAY - * constants. btw, I think we should switch to those here too, to - * get rid of this function, if possible. - */ - public static int day2CalendarDay(int day) - { - switch (day) - { - case SU: - return Calendar.SUNDAY; - case MO: - return Calendar.MONDAY; - case TU: - return Calendar.TUESDAY; - case WE: - return Calendar.WEDNESDAY; - case TH: - return Calendar.THURSDAY; - case FR: - return Calendar.FRIDAY; - case SA: - return Calendar.SATURDAY; - default: - throw new RuntimeException("bad day of week: " + day); - } - } - - /** - * Converts one of the internal day constants (SU, MO, etc.) to the - * two-letter string representing that constant. - * - * @param day one the internal constants SU, MO, etc. - * @return the two-letter string for the day ("SU", "MO", etc.) - * - * @throws IllegalArgumentException Thrown if the day argument is not one of - * the defined day constants. - */ - private static String day2String(int day) { - switch (day) { - case SU: - return "SU"; - case MO: - return "MO"; - case TU: - return "TU"; - case WE: - return "WE"; - case TH: - return "TH"; - case FR: - return "FR"; - case SA: - return "SA"; - default: - throw new IllegalArgumentException("bad day argument: " + day); - } - } - - private static void appendNumbers(StringBuilder s, String label, - int count, int[] values) - { - if (count > 0) { - s.append(label); - count--; - for (int i=0; i<count; i++) { - s.append(values[i]); - s.append(","); - } - s.append(values[count]); - } - } - - private void appendByDay(StringBuilder s, int i) - { - int n = this.bydayNum[i]; - if (n != 0) { - s.append(n); - } - - String str = day2String(this.byday[i]); - s.append(str); - } - - @Override - public String toString() - { - StringBuilder s = new StringBuilder(); - - s.append("FREQ="); - switch (this.freq) - { - case SECONDLY: - s.append("SECONDLY"); - break; - case MINUTELY: - s.append("MINUTELY"); - break; - case HOURLY: - s.append("HOURLY"); - break; - case DAILY: - s.append("DAILY"); - break; - case WEEKLY: - s.append("WEEKLY"); - break; - case MONTHLY: - s.append("MONTHLY"); - break; - case YEARLY: - s.append("YEARLY"); - break; - } - - if (!TextUtils.isEmpty(this.until)) { - s.append(";UNTIL="); - s.append(until); - } - - if (this.count != 0) { - s.append(";COUNT="); - s.append(this.count); - } - - if (this.interval != 0) { - s.append(";INTERVAL="); - s.append(this.interval); - } - - if (this.wkst != 0) { - s.append(";WKST="); - s.append(day2String(this.wkst)); - } - - appendNumbers(s, ";BYSECOND=", this.bysecondCount, this.bysecond); - appendNumbers(s, ";BYMINUTE=", this.byminuteCount, this.byminute); - appendNumbers(s, ";BYSECOND=", this.byhourCount, this.byhour); - - // day - int count = this.bydayCount; - if (count > 0) { - s.append(";BYDAY="); - count--; - for (int i=0; i<count; i++) { - appendByDay(s, i); - s.append(","); - } - appendByDay(s, count); - } - - appendNumbers(s, ";BYMONTHDAY=", this.bymonthdayCount, this.bymonthday); - appendNumbers(s, ";BYYEARDAY=", this.byyeardayCount, this.byyearday); - appendNumbers(s, ";BYWEEKNO=", this.byweeknoCount, this.byweekno); - appendNumbers(s, ";BYMONTH=", this.bymonthCount, this.bymonth); - appendNumbers(s, ";BYSETPOS=", this.bysetposCount, this.bysetpos); - - return s.toString(); - } - - public boolean repeatsOnEveryWeekDay() { - if (this.freq != WEEKLY) { - return false; - } - - int count = this.bydayCount; - if (count != 5) { - return false; - } - - for (int i = 0 ; i < count ; i++) { - int day = byday[i]; - if (day == SU || day == SA) { - return false; - } - } - - return true; - } - - /** - * Determines whether this rule specifies a simple monthly rule by weekday, such as - * "FREQ=MONTHLY;BYDAY=3TU" (the 3rd Tuesday of every month). - * <p> - * Negative days, e.g. "FREQ=MONTHLY;BYDAY=-1TU" (the last Tuesday of every month), - * will cause "false" to be returned. - * <p> - * Rules that fire every week, such as "FREQ=MONTHLY;BYDAY=TU" (every Tuesday of every - * month) will cause "false" to be returned. (Note these are usually expressed as - * WEEKLY rules, and hence are uncommon.) - * - * @return true if this rule is of the appropriate form - */ - public boolean repeatsMonthlyOnDayCount() { - if (this.freq != MONTHLY) { - return false; - } - - if (bydayCount != 1 || bymonthdayCount != 0) { - return false; - } - - if (bydayNum[0] <= 0) { - return false; - } - - return true; - } - - /** - * Determines whether two integer arrays contain identical elements. - * <p> - * The native implementation over-allocated the arrays (and may have stuff left over from - * a previous run), so we can't just check the arrays -- the separately-maintained count - * field also matters. We assume that a null array will have a count of zero, and that the - * array can hold as many elements as the associated count indicates. - * <p> - * TODO: replace this with Arrays.equals() when the old parser goes away. - */ - private static boolean arraysEqual(int[] array1, int count1, int[] array2, int count2) { - if (count1 != count2) { - return false; - } - - for (int i = 0; i < count1; i++) { - if (array1[i] != array2[i]) - return false; - } - - return true; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof EventRecurrence)) { - return false; - } - - EventRecurrence er = (EventRecurrence) obj; - return (startDate == null ? - er.startDate == null : Time.compare(startDate, er.startDate) == 0) && - freq == er.freq && - (until == null ? er.until == null : until.equals(er.until)) && - count == er.count && - interval == er.interval && - wkst == er.wkst && - arraysEqual(bysecond, bysecondCount, er.bysecond, er.bysecondCount) && - arraysEqual(byminute, byminuteCount, er.byminute, er.byminuteCount) && - arraysEqual(byhour, byhourCount, er.byhour, er.byhourCount) && - arraysEqual(byday, bydayCount, er.byday, er.bydayCount) && - arraysEqual(bydayNum, bydayCount, er.bydayNum, er.bydayCount) && - arraysEqual(bymonthday, bymonthdayCount, er.bymonthday, er.bymonthdayCount) && - arraysEqual(byyearday, byyeardayCount, er.byyearday, er.byyeardayCount) && - arraysEqual(byweekno, byweeknoCount, er.byweekno, er.byweeknoCount) && - arraysEqual(bymonth, bymonthCount, er.bymonth, er.bymonthCount) && - arraysEqual(bysetpos, bysetposCount, er.bysetpos, er.bysetposCount); - } - - @Override public int hashCode() { - // We overrode equals, so we must override hashCode(). Nobody seems to need this though. - throw new UnsupportedOperationException(); - } - - /** - * Resets parser-modified fields to their initial state. Does not alter startDate. - * <p> - * The original parser always set all of the "count" fields, "wkst", and "until", - * essentially allowing the same object to be used multiple times by calling parse(). - * It's unclear whether this behavior was intentional. For now, be paranoid and - * preserve the existing behavior by resetting the fields. - * <p> - * We don't need to touch the integer arrays; they will either be ignored or - * overwritten. The "startDate" field is not set by the parser, so we ignore it here. - */ - private void resetFields() { - until = null; - freq = count = interval = bysecondCount = byminuteCount = byhourCount = - bydayCount = bymonthdayCount = byyeardayCount = byweeknoCount = bymonthCount = - bysetposCount = 0; - } - - /** - * Parses an rfc2445 recurrence rule string into its component pieces. Attempting to parse - * malformed input will result in an EventRecurrence.InvalidFormatException. - * - * @param recur The recurrence rule to parse (in un-folded form). - */ - public void parse(String recur) { - /* - * From RFC 2445 section 4.3.10: - * - * recur = "FREQ"=freq *( - * ; either UNTIL or COUNT may appear in a 'recur', - * ; but UNTIL and COUNT MUST NOT occur in the same 'recur' - * - * ( ";" "UNTIL" "=" enddate ) / - * ( ";" "COUNT" "=" 1*DIGIT ) / - * - * ; the rest of these keywords are optional, - * ; but MUST NOT occur more than once - * - * ( ";" "INTERVAL" "=" 1*DIGIT ) / - * ( ";" "BYSECOND" "=" byseclist ) / - * ( ";" "BYMINUTE" "=" byminlist ) / - * ( ";" "BYHOUR" "=" byhrlist ) / - * ( ";" "BYDAY" "=" bywdaylist ) / - * ( ";" "BYMONTHDAY" "=" bymodaylist ) / - * ( ";" "BYYEARDAY" "=" byyrdaylist ) / - * ( ";" "BYWEEKNO" "=" bywknolist ) / - * ( ";" "BYMONTH" "=" bymolist ) / - * ( ";" "BYSETPOS" "=" bysplist ) / - * ( ";" "WKST" "=" weekday ) / - * ( ";" x-name "=" text ) - * ) - * - * Examples: - * FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU - * FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8 - * - * Strategy: - * (1) Split the string at ';' boundaries to get an array of rule "parts". - * (2) For each part, find substrings for left/right sides of '=' (name/value). - * (3) Call a <name>-specific parsing function to parse the <value> into an - * output field. - * - * By keeping track of which names we've seen in a bit vector, we can verify the - * constraints indicated above (FREQ appears first, none of them appear more than once -- - * though x-[name] would require special treatment), and we have either UNTIL or COUNT - * but not both. - * - * In general, RFC 2445 property names (e.g. "FREQ") and enumerations ("TU") must - * be handled in a case-insensitive fashion, but case may be significant for other - * properties. We don't have any case-sensitive values in RRULE, except possibly - * for the custom "X-" properties, but we ignore those anyway. Thus, we can trivially - * convert the entire string to upper case and then use simple comparisons. - * - * Differences from previous version: - * - allows lower-case property and enumeration values [optional] - * - enforces that FREQ appears first - * - enforces that only one of UNTIL and COUNT may be specified - * - allows (but ignores) X-* parts - * - improved validation on various values (e.g. UNTIL timestamps) - * - error messages are more specific - */ - - /* TODO: replace with "if (freq != 0) throw" if nothing requires this */ - resetFields(); - - int parseFlags = 0; - String[] parts; - if (ALLOW_LOWER_CASE) { - parts = recur.toUpperCase().split(";"); - } else { - parts = recur.split(";"); - } - for (String part : parts) { - int equalIndex = part.indexOf('='); - if (equalIndex <= 0) { - /* no '=' or no LHS */ - throw new InvalidFormatException("Missing LHS in " + part); - } - - String lhs = part.substring(0, equalIndex); - String rhs = part.substring(equalIndex + 1); - if (rhs.length() == 0) { - throw new InvalidFormatException("Missing RHS in " + part); - } - - /* - * In lieu of a "switch" statement that allows string arguments, we use a - * map from strings to parsing functions. - */ - PartParser parser = sParsePartMap.get(lhs); - if (parser == null) { - if (lhs.startsWith("X-")) { - //Log.d(TAG, "Ignoring custom part " + lhs); - continue; - } - throw new InvalidFormatException("Couldn't find parser for " + lhs); - } else { - int flag = parser.parsePart(rhs, this); - if ((parseFlags & flag) != 0) { - throw new InvalidFormatException("Part " + lhs + " was specified twice"); - } - if (parseFlags == 0 && flag != PARSED_FREQ) { - throw new InvalidFormatException("FREQ must be specified first"); - } - parseFlags |= flag; - } - } - - // If not specified, week starts on Monday. - if ((parseFlags & PARSED_WKST) == 0) { - wkst = MO; - } - - // FREQ is mandatory. - if ((parseFlags & PARSED_FREQ) == 0) { - throw new InvalidFormatException("Must specify a FREQ value"); - } - - // Can't have both UNTIL and COUNT. - if ((parseFlags & (PARSED_UNTIL | PARSED_COUNT)) == (PARSED_UNTIL | PARSED_COUNT)) { - if (ONLY_ONE_UNTIL_COUNT) { - throw new InvalidFormatException("Must not specify both UNTIL and COUNT: " + recur); - } else { - Log.w(TAG, "Warning: rrule has both UNTIL and COUNT: " + recur); - } - } - } - - /** - * Base class for the RRULE part parsers. - */ - abstract static class PartParser { - /** - * Parses a single part. - * - * @param value The right-hand-side of the part. - * @param er The EventRecurrence into which the result is stored. - * @return A bit value indicating which part was parsed. - */ - public abstract int parsePart(String value, EventRecurrence er); - - /** - * Parses an integer, with range-checking. - * - * @param str The string to parse. - * @param minVal Minimum allowed value. - * @param maxVal Maximum allowed value. - * @param allowZero Is 0 allowed? - * @return The parsed value. - */ - public static int parseIntRange(String str, int minVal, int maxVal, boolean allowZero) { - try { - if (str.charAt(0) == '+') { - // Integer.parseInt does not allow a leading '+', so skip it manually. - str = str.substring(1); - } - int val = Integer.parseInt(str); - if (val < minVal || val > maxVal || (val == 0 && !allowZero)) { - throw new InvalidFormatException("Integer value out of range: " + str); - } - return val; - } catch (NumberFormatException nfe) { - throw new InvalidFormatException("Invalid integer value: " + str); - } - } - - /** - * Parses a comma-separated list of integers, with range-checking. - * - * @param listStr The string to parse. - * @param minVal Minimum allowed value. - * @param maxVal Maximum allowed value. - * @param allowZero Is 0 allowed? - * @return A new array with values, sized to hold the exact number of elements. - */ - public static int[] parseNumberList(String listStr, int minVal, int maxVal, - boolean allowZero) { - int[] values; - - if (listStr.indexOf(",") < 0) { - // Common case: only one entry, skip split() overhead. - values = new int[1]; - values[0] = parseIntRange(listStr, minVal, maxVal, allowZero); - } else { - String[] valueStrs = listStr.split(","); - int len = valueStrs.length; - values = new int[len]; - for (int i = 0; i < len; i++) { - values[i] = parseIntRange(valueStrs[i], minVal, maxVal, allowZero); - } - } - return values; - } - } - - /** parses FREQ={SECONDLY,MINUTELY,...} */ - private static class ParseFreq extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - Integer freq = sParseFreqMap.get(value); - if (freq == null) { - throw new InvalidFormatException("Invalid FREQ value: " + value); - } - er.freq = freq; - return PARSED_FREQ; - } - } - /** parses UNTIL=enddate, e.g. "19970829T021400" */ - private static class ParseUntil extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - if (VALIDATE_UNTIL) { - try { - // Parse the time to validate it. The result isn't retained. - Time until = new Time(); - until.parse(value); - } catch (TimeFormatException tfe) { - throw new InvalidFormatException("Invalid UNTIL value: " + value); - } - } - er.until = value; - return PARSED_UNTIL; - } - } - /** parses COUNT=[non-negative-integer] */ - private static class ParseCount extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - er.count = parseIntRange(value, 0, Integer.MAX_VALUE, true); - return PARSED_COUNT; - } - } - /** parses INTERVAL=[non-negative-integer] */ - private static class ParseInterval extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - er.interval = parseIntRange(value, 1, Integer.MAX_VALUE, false); - return PARSED_INTERVAL; - } - } - /** parses BYSECOND=byseclist */ - private static class ParseBySecond extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bysecond = parseNumberList(value, 0, 59, true); - er.bysecond = bysecond; - er.bysecondCount = bysecond.length; - return PARSED_BYSECOND; - } - } - /** parses BYMINUTE=byminlist */ - private static class ParseByMinute extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byminute = parseNumberList(value, 0, 59, true); - er.byminute = byminute; - er.byminuteCount = byminute.length; - return PARSED_BYMINUTE; - } - } - /** parses BYHOUR=byhrlist */ - private static class ParseByHour extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byhour = parseNumberList(value, 0, 23, true); - er.byhour = byhour; - er.byhourCount = byhour.length; - return PARSED_BYHOUR; - } - } - /** parses BYDAY=bywdaylist, e.g. "1SU,-1SU" */ - private static class ParseByDay extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byday; - int[] bydayNum; - int bydayCount; - - if (value.indexOf(",") < 0) { - /* only one entry, skip split() overhead */ - bydayCount = 1; - byday = new int[1]; - bydayNum = new int[1]; - parseWday(value, byday, bydayNum, 0); - } else { - String[] wdays = value.split(","); - int len = wdays.length; - bydayCount = len; - byday = new int[len]; - bydayNum = new int[len]; - for (int i = 0; i < len; i++) { - parseWday(wdays[i], byday, bydayNum, i); - } - } - er.byday = byday; - er.bydayNum = bydayNum; - er.bydayCount = bydayCount; - return PARSED_BYDAY; - } - - /** parses [int]weekday, putting the pieces into parallel array entries */ - private static void parseWday(String str, int[] byday, int[] bydayNum, int index) { - int wdayStrStart = str.length() - 2; - String wdayStr; - - if (wdayStrStart > 0) { - /* number is included; parse it out and advance to weekday */ - String numPart = str.substring(0, wdayStrStart); - int num = parseIntRange(numPart, -53, 53, false); - bydayNum[index] = num; - wdayStr = str.substring(wdayStrStart); - } else { - /* just the weekday string */ - wdayStr = str; - } - Integer wday = sParseWeekdayMap.get(wdayStr); - if (wday == null) { - throw new InvalidFormatException("Invalid BYDAY value: " + str); - } - byday[index] = wday; - } - } - /** parses BYMONTHDAY=bymodaylist */ - private static class ParseByMonthDay extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bymonthday = parseNumberList(value, -31, 31, false); - er.bymonthday = bymonthday; - er.bymonthdayCount = bymonthday.length; - return PARSED_BYMONTHDAY; - } - } - /** parses BYYEARDAY=byyrdaylist */ - private static class ParseByYearDay extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byyearday = parseNumberList(value, -366, 366, false); - er.byyearday = byyearday; - er.byyeardayCount = byyearday.length; - return PARSED_BYYEARDAY; - } - } - /** parses BYWEEKNO=bywknolist */ - private static class ParseByWeekNo extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] byweekno = parseNumberList(value, -53, 53, false); - er.byweekno = byweekno; - er.byweeknoCount = byweekno.length; - return PARSED_BYWEEKNO; - } - } - /** parses BYMONTH=bymolist */ - private static class ParseByMonth extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bymonth = parseNumberList(value, 1, 12, false); - er.bymonth = bymonth; - er.bymonthCount = bymonth.length; - return PARSED_BYMONTH; - } - } - /** parses BYSETPOS=bysplist */ - private static class ParseBySetPos extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - int[] bysetpos = parseNumberList(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true); - er.bysetpos = bysetpos; - er.bysetposCount = bysetpos.length; - return PARSED_BYSETPOS; - } - } - /** parses WKST={SU,MO,...} */ - private static class ParseWkst extends PartParser { - @Override public int parsePart(String value, EventRecurrence er) { - Integer wkst = sParseWeekdayMap.get(value); - if (wkst == null) { - throw new InvalidFormatException("Invalid WKST value: " + value); - } - er.wkst = wkst; - return PARSED_WKST; - } - } -} diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java deleted file mode 100644 index 58c5c63..0000000 --- a/core/java/android/pim/ICalendar.java +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2007 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.pim; - -import android.util.Log; - -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.ArrayList; - -/** - * Parses RFC 2445 iCalendar objects. - */ -public class ICalendar { - - private static final String TAG = "Sync"; - - // TODO: keep track of VEVENT, VTODO, VJOURNAL, VFREEBUSY, VTIMEZONE, VALARM - // components, by type field or by subclass? subclass would allow us to - // enforce grammars. - - /** - * Exception thrown when an iCalendar object has invalid syntax. - */ - public static class FormatException extends Exception { - public FormatException() { - super(); - } - - public FormatException(String msg) { - super(msg); - } - - public FormatException(String msg, Throwable cause) { - super(msg, cause); - } - } - - /** - * A component within an iCalendar (VEVENT, VTODO, VJOURNAL, VFEEBUSY, - * VTIMEZONE, VALARM). - */ - public static class Component { - - // components - private static final String BEGIN = "BEGIN"; - private static final String END = "END"; - private static final String NEWLINE = "\n"; - public static final String VCALENDAR = "VCALENDAR"; - public static final String VEVENT = "VEVENT"; - public static final String VTODO = "VTODO"; - public static final String VJOURNAL = "VJOURNAL"; - public static final String VFREEBUSY = "VFREEBUSY"; - public static final String VTIMEZONE = "VTIMEZONE"; - public static final String VALARM = "VALARM"; - - private final String mName; - private final Component mParent; // see if we can get rid of this - private LinkedList<Component> mChildren = null; - private final LinkedHashMap<String, ArrayList<Property>> mPropsMap = - new LinkedHashMap<String, ArrayList<Property>>(); - - /** - * Creates a new component with the provided name. - * @param name The name of the component. - */ - public Component(String name, Component parent) { - mName = name; - mParent = parent; - } - - /** - * Returns the name of the component. - * @return The name of the component. - */ - public String getName() { - return mName; - } - - /** - * Returns the parent of this component. - * @return The parent of this component. - */ - public Component getParent() { - return mParent; - } - - /** - * Helper that lazily gets/creates the list of children. - * @return The list of children. - */ - protected LinkedList<Component> getOrCreateChildren() { - if (mChildren == null) { - mChildren = new LinkedList<Component>(); - } - return mChildren; - } - - /** - * Adds a child component to this component. - * @param child The child component. - */ - public void addChild(Component child) { - getOrCreateChildren().add(child); - } - - /** - * Returns a list of the Component children of this component. May be - * null, if there are no children. - * - * @return A list of the children. - */ - public List<Component> getComponents() { - return mChildren; - } - - /** - * Adds a Property to this component. - * @param prop - */ - public void addProperty(Property prop) { - String name= prop.getName(); - ArrayList<Property> props = mPropsMap.get(name); - if (props == null) { - props = new ArrayList<Property>(); - mPropsMap.put(name, props); - } - props.add(prop); - } - - /** - * Returns a set of the property names within this component. - * @return A set of property names within this component. - */ - public Set<String> getPropertyNames() { - return mPropsMap.keySet(); - } - - /** - * Returns a list of properties with the specified name. Returns null - * if there are no such properties. - * @param name The name of the property that should be returned. - * @return A list of properties with the requested name. - */ - public List<Property> getProperties(String name) { - return mPropsMap.get(name); - } - - /** - * Returns the first property with the specified name. Returns null - * if there is no such property. - * @param name The name of the property that should be returned. - * @return The first property with the specified name. - */ - public Property getFirstProperty(String name) { - List<Property> props = mPropsMap.get(name); - if (props == null || props.size() == 0) { - return null; - } - return props.get(0); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - sb.append(NEWLINE); - return sb.toString(); - } - - /** - * Helper method that appends this component to a StringBuilder. The - * caller is responsible for appending a newline at the end of the - * component. - */ - public void toString(StringBuilder sb) { - sb.append(BEGIN); - sb.append(":"); - sb.append(mName); - sb.append(NEWLINE); - - // append the properties - for (String propertyName : getPropertyNames()) { - for (Property property : getProperties(propertyName)) { - property.toString(sb); - sb.append(NEWLINE); - } - } - - // append the sub-components - if (mChildren != null) { - for (Component component : mChildren) { - component.toString(sb); - sb.append(NEWLINE); - } - } - - sb.append(END); - sb.append(":"); - sb.append(mName); - } - } - - /** - * A property within an iCalendar component (e.g., DTSTART, DTEND, etc., - * within a VEVENT). - */ - public static class Property { - // properties - // TODO: do we want to list these here? the complete list is long. - public static final String DTSTART = "DTSTART"; - public static final String DTEND = "DTEND"; - public static final String DURATION = "DURATION"; - public static final String RRULE = "RRULE"; - public static final String RDATE = "RDATE"; - public static final String EXRULE = "EXRULE"; - public static final String EXDATE = "EXDATE"; - // ... need to add more. - - private final String mName; - private LinkedHashMap<String, ArrayList<Parameter>> mParamsMap = - new LinkedHashMap<String, ArrayList<Parameter>>(); - private String mValue; // TODO: make this final? - - /** - * Creates a new property with the provided name. - * @param name The name of the property. - */ - public Property(String name) { - mName = name; - } - - /** - * Creates a new property with the provided name and value. - * @param name The name of the property. - * @param value The value of the property. - */ - public Property(String name, String value) { - mName = name; - mValue = value; - } - - /** - * Returns the name of the property. - * @return The name of the property. - */ - public String getName() { - return mName; - } - - /** - * Returns the value of this property. - * @return The value of this property. - */ - public String getValue() { - return mValue; - } - - /** - * Sets the value of this property. - * @param value The desired value for this property. - */ - public void setValue(String value) { - mValue = value; - } - - /** - * Adds a {@link Parameter} to this property. - * @param param The parameter that should be added. - */ - public void addParameter(Parameter param) { - ArrayList<Parameter> params = mParamsMap.get(param.name); - if (params == null) { - params = new ArrayList<Parameter>(); - mParamsMap.put(param.name, params); - } - params.add(param); - } - - /** - * Returns the set of parameter names for this property. - * @return The set of parameter names for this property. - */ - public Set<String> getParameterNames() { - return mParamsMap.keySet(); - } - - /** - * Returns the list of parameters with the specified name. May return - * null if there are no such parameters. - * @param name The name of the parameters that should be returned. - * @return The list of parameters with the specified name. - */ - public List<Parameter> getParameters(String name) { - return mParamsMap.get(name); - } - - /** - * Returns the first parameter with the specified name. May return - * nll if there is no such parameter. - * @param name The name of the parameter that should be returned. - * @return The first parameter with the specified name. - */ - public Parameter getFirstParameter(String name) { - ArrayList<Parameter> params = mParamsMap.get(name); - if (params == null || params.size() == 0) { - return null; - } - return params.get(0); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - /** - * Helper method that appends this property to a StringBuilder. The - * caller is responsible for appending a newline after this property. - */ - public void toString(StringBuilder sb) { - sb.append(mName); - Set<String> parameterNames = getParameterNames(); - for (String parameterName : parameterNames) { - for (Parameter param : getParameters(parameterName)) { - sb.append(";"); - param.toString(sb); - } - } - sb.append(":"); - sb.append(mValue); - } - } - - /** - * A parameter defined for an iCalendar property. - */ - // TODO: make this a proper class rather than a struct? - public static class Parameter { - public String name; - public String value; - - /** - * Creates a new empty parameter. - */ - public Parameter() { - } - - /** - * Creates a new parameter with the specified name and value. - * @param name The name of the parameter. - * @param value The value of the parameter. - */ - public Parameter(String name, String value) { - this.name = name; - this.value = value; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - /** - * Helper method that appends this parameter to a StringBuilder. - */ - public void toString(StringBuilder sb) { - sb.append(name); - sb.append("="); - sb.append(value); - } - } - - private static final class ParserState { - // public int lineNumber = 0; - public String line; // TODO: just point to original text - public int index; - } - - // use factory method - private ICalendar() { - } - - // TODO: get rid of this -- handle all of the parsing in one pass through - // the text. - private static String normalizeText(String text) { - // it's supposed to be \r\n, but not everyone does that - text = text.replaceAll("\r\n", "\n"); - text = text.replaceAll("\r", "\n"); - - // we deal with line folding, by replacing all "\n " strings - // with nothing. The RFC specifies "\r\n " to be folded, but - // we handle "\n " and "\r " too because we can get those. - text = text.replaceAll("\n ", ""); - - return text; - } - - /** - * Parses text into an iCalendar component. Parses into the provided - * component, if not null, or parses into a new component. In the latter - * case, expects a BEGIN as the first line. Returns the provided or newly - * created top-level component. - */ - // TODO: use an index into the text, so we can make this a recursive - // function? - private static Component parseComponentImpl(Component component, - String text) - throws FormatException { - Component current = component; - ParserState state = new ParserState(); - state.index = 0; - - // split into lines - String[] lines = text.split("\n"); - - // each line is of the format: - // name *(";" param) ":" value - for (String line : lines) { - try { - current = parseLine(line, state, current); - // if the provided component was null, we will return the root - // NOTE: in this case, if the first line is not a BEGIN, a - // FormatException will get thrown. - if (component == null) { - component = current; - } - } catch (FormatException fe) { - if (false) { - Log.v(TAG, "Cannot parse " + line, fe); - } - // for now, we ignore the parse error. Google Calendar seems - // to be emitting some misformatted iCalendar objects. - } - continue; - } - return component; - } - - /** - * Parses a line into the provided component. Creates a new component if - * the line is a BEGIN, adding the newly created component to the provided - * parent. Returns whatever component is the current one (to which new - * properties will be added) in the parse. - */ - private static Component parseLine(String line, ParserState state, - Component component) - throws FormatException { - state.line = line; - int len = state.line.length(); - - // grab the name - char c = 0; - for (state.index = 0; state.index < len; ++state.index) { - c = line.charAt(state.index); - if (c == ';' || c == ':') { - break; - } - } - String name = line.substring(0, state.index); - - if (component == null) { - if (!Component.BEGIN.equals(name)) { - throw new FormatException("Expected BEGIN"); - } - } - - Property property; - if (Component.BEGIN.equals(name)) { - // start a new component - String componentName = extractValue(state); - Component child = new Component(componentName, component); - if (component != null) { - component.addChild(child); - } - return child; - } else if (Component.END.equals(name)) { - // finish the current component - String componentName = extractValue(state); - if (component == null || - !componentName.equals(component.getName())) { - throw new FormatException("Unexpected END " + componentName); - } - return component.getParent(); - } else { - property = new Property(name); - } - - if (c == ';') { - Parameter parameter = null; - while ((parameter = extractParameter(state)) != null) { - property.addParameter(parameter); - } - } - String value = extractValue(state); - property.setValue(value); - component.addProperty(property); - return component; - } - - /** - * Extracts the value ":..." on the current line. The first character must - * be a ':'. - */ - private static String extractValue(ParserState state) - throws FormatException { - String line = state.line; - if (state.index >= line.length() || line.charAt(state.index) != ':') { - throw new FormatException("Expected ':' before end of line in " - + line); - } - String value = line.substring(state.index + 1); - state.index = line.length() - 1; - return value; - } - - /** - * Extracts the next parameter from the line, if any. If there are no more - * parameters, returns null. - */ - private static Parameter extractParameter(ParserState state) - throws FormatException { - String text = state.line; - int len = text.length(); - Parameter parameter = null; - int startIndex = -1; - int equalIndex = -1; - while (state.index < len) { - char c = text.charAt(state.index); - if (c == ':') { - if (parameter != null) { - if (equalIndex == -1) { - throw new FormatException("Expected '=' within " - + "parameter in " + text); - } - parameter.value = text.substring(equalIndex + 1, - state.index); - } - return parameter; // may be null - } else if (c == ';') { - if (parameter != null) { - if (equalIndex == -1) { - throw new FormatException("Expected '=' within " - + "parameter in " + text); - } - parameter.value = text.substring(equalIndex + 1, - state.index); - return parameter; - } else { - parameter = new Parameter(); - startIndex = state.index; - } - } else if (c == '=') { - equalIndex = state.index; - if ((parameter == null) || (startIndex == -1)) { - throw new FormatException("Expected ';' before '=' in " - + text); - } - parameter.name = text.substring(startIndex + 1, equalIndex); - } else if (c == '"') { - if (parameter == null) { - throw new FormatException("Expected parameter before '\"' in " + text); - } - if (equalIndex == -1) { - throw new FormatException("Expected '=' within parameter in " + text); - } - if (state.index > equalIndex + 1) { - throw new FormatException("Parameter value cannot contain a '\"' in " + text); - } - final int endQuote = text.indexOf('"', state.index + 1); - if (endQuote < 0) { - throw new FormatException("Expected closing '\"' in " + text); - } - parameter.value = text.substring(state.index + 1, endQuote); - state.index = endQuote + 1; - return parameter; - } - ++state.index; - } - throw new FormatException("Expected ':' before end of line in " + text); - } - - /** - * Parses the provided text into an iCalendar object. The top-level - * component must be of type VCALENDAR. - * @param text The text to be parsed. - * @return The top-level VCALENDAR component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar VCALENDAR object. - */ - public static Component parseCalendar(String text) throws FormatException { - Component calendar = parseComponent(null, text); - if (calendar == null || !Component.VCALENDAR.equals(calendar.getName())) { - throw new FormatException("Expected " + Component.VCALENDAR); - } - return calendar; - } - - /** - * Parses the provided text into an iCalendar event. The top-level - * component must be of type VEVENT. - * @param text The text to be parsed. - * @return The top-level VEVENT component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar VEVENT. - */ - public static Component parseEvent(String text) throws FormatException { - Component event = parseComponent(null, text); - if (event == null || !Component.VEVENT.equals(event.getName())) { - throw new FormatException("Expected " + Component.VEVENT); - } - return event; - } - - /** - * Parses the provided text into an iCalendar component. - * @param text The text to be parsed. - * @return The top-level component. - * @throws FormatException Thrown if the text could not be parsed into an - * iCalendar component. - */ - public static Component parseComponent(String text) throws FormatException { - return parseComponent(null, text); - } - - /** - * Parses the provided text, adding to the provided component. - * @param component The component to which the parsed iCalendar data should - * be added. - * @param text The text to be parsed. - * @return The top-level component. - * @throws FormatException Thrown if the text could not be parsed as an - * iCalendar object. - */ - public static Component parseComponent(Component component, String text) - throws FormatException { - text = normalizeText(text); - return parseComponentImpl(component, text); - } -} diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java deleted file mode 100644 index b7fb320..0000000 --- a/core/java/android/pim/RecurrenceSet.java +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright (C) 2007 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.pim; - -import android.content.ContentValues; -import android.database.Cursor; -import android.provider.CalendarContract; -import android.text.TextUtils; -import android.text.format.Time; -import android.util.Log; - -import java.util.List; -import java.util.regex.Pattern; - -/** - * Basic information about a recurrence, following RFC 2445 Section 4.8.5. - * Contains the RRULEs, RDATE, EXRULEs, and EXDATE properties. - */ -public class RecurrenceSet { - - private final static String TAG = "CalendarProvider"; - - private final static String RULE_SEPARATOR = "\n"; - private final static String FOLDING_SEPARATOR = "\n "; - - // TODO: make these final? - public EventRecurrence[] rrules = null; - public long[] rdates = null; - public EventRecurrence[] exrules = null; - public long[] exdates = null; - - /** - * Creates a new RecurrenceSet from information stored in the - * events table in the CalendarProvider. - * @param values The values retrieved from the Events table. - */ - public RecurrenceSet(ContentValues values) - throws EventRecurrence.InvalidFormatException { - String rruleStr = values.getAsString(CalendarContract.Events.RRULE); - String rdateStr = values.getAsString(CalendarContract.Events.RDATE); - String exruleStr = values.getAsString(CalendarContract.Events.EXRULE); - String exdateStr = values.getAsString(CalendarContract.Events.EXDATE); - init(rruleStr, rdateStr, exruleStr, exdateStr); - } - - /** - * Creates a new RecurrenceSet from information stored in a database - * {@link Cursor} pointing to the events table in the - * CalendarProvider. The cursor must contain the RRULE, RDATE, EXRULE, - * and EXDATE columns. - * - * @param cursor The cursor containing the RRULE, RDATE, EXRULE, and EXDATE - * columns. - */ - public RecurrenceSet(Cursor cursor) - throws EventRecurrence.InvalidFormatException { - int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE); - int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE); - int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE); - int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE); - String rruleStr = cursor.getString(rruleColumn); - String rdateStr = cursor.getString(rdateColumn); - String exruleStr = cursor.getString(exruleColumn); - String exdateStr = cursor.getString(exdateColumn); - init(rruleStr, rdateStr, exruleStr, exdateStr); - } - - public RecurrenceSet(String rruleStr, String rdateStr, - String exruleStr, String exdateStr) - throws EventRecurrence.InvalidFormatException { - init(rruleStr, rdateStr, exruleStr, exdateStr); - } - - private void init(String rruleStr, String rdateStr, - String exruleStr, String exdateStr) - throws EventRecurrence.InvalidFormatException { - if (!TextUtils.isEmpty(rruleStr) || !TextUtils.isEmpty(rdateStr)) { - - if (!TextUtils.isEmpty(rruleStr)) { - String[] rruleStrs = rruleStr.split(RULE_SEPARATOR); - rrules = new EventRecurrence[rruleStrs.length]; - for (int i = 0; i < rruleStrs.length; ++i) { - EventRecurrence rrule = new EventRecurrence(); - rrule.parse(rruleStrs[i]); - rrules[i] = rrule; - } - } - - if (!TextUtils.isEmpty(rdateStr)) { - rdates = parseRecurrenceDates(rdateStr); - } - - if (!TextUtils.isEmpty(exruleStr)) { - String[] exruleStrs = exruleStr.split(RULE_SEPARATOR); - exrules = new EventRecurrence[exruleStrs.length]; - for (int i = 0; i < exruleStrs.length; ++i) { - EventRecurrence exrule = new EventRecurrence(); - exrule.parse(exruleStr); - exrules[i] = exrule; - } - } - - if (!TextUtils.isEmpty(exdateStr)) { - exdates = parseRecurrenceDates(exdateStr); - } - } - } - - /** - * Returns whether or not a recurrence is defined in this RecurrenceSet. - * @return Whether or not a recurrence is defined in this RecurrenceSet. - */ - public boolean hasRecurrence() { - return (rrules != null || rdates != null); - } - - /** - * Parses the provided RDATE or EXDATE string into an array of longs - * representing each date/time in the recurrence. - * @param recurrence The recurrence to be parsed. - * @return The list of date/times. - */ - public static long[] parseRecurrenceDates(String recurrence) { - // TODO: use "local" time as the default. will need to handle times - // that end in "z" (UTC time) explicitly at that point. - String tz = Time.TIMEZONE_UTC; - int tzidx = recurrence.indexOf(";"); - if (tzidx != -1) { - tz = recurrence.substring(0, tzidx); - recurrence = recurrence.substring(tzidx + 1); - } - Time time = new Time(tz); - String[] rawDates = recurrence.split(","); - int n = rawDates.length; - long[] dates = new long[n]; - for (int i = 0; i<n; ++i) { - // The timezone is updated to UTC if the time string specified 'Z'. - time.parse(rawDates[i]); - dates[i] = time.toMillis(false /* use isDst */); - time.timezone = tz; - } - return dates; - } - - /** - * Populates the database map of values with the appropriate RRULE, RDATE, - * EXRULE, and EXDATE values extracted from the parsed iCalendar component. - * @param component The iCalendar component containing the desired - * recurrence specification. - * @param values The db values that should be updated. - * @return true if the component contained the necessary information - * to specify a recurrence. The required fields are DTSTART, - * one of DTEND/DURATION, and one of RRULE/RDATE. Returns false if - * there was an error, including if the date is out of range. - */ - public static boolean populateContentValues(ICalendar.Component component, - ContentValues values) { - ICalendar.Property dtstartProperty = - component.getFirstProperty("DTSTART"); - String dtstart = dtstartProperty.getValue(); - ICalendar.Parameter tzidParam = - dtstartProperty.getFirstParameter("TZID"); - // NOTE: the timezone may be null, if this is a floating time. - String tzid = tzidParam == null ? null : tzidParam.value; - Time start = new Time(tzidParam == null ? Time.TIMEZONE_UTC : tzid); - boolean inUtc = start.parse(dtstart); - boolean allDay = start.allDay; - - // We force TimeZone to UTC for "all day recurring events" as the server is sending no - // TimeZone in DTSTART for them - if (inUtc || allDay) { - tzid = Time.TIMEZONE_UTC; - } - - String duration = computeDuration(start, component); - String rrule = flattenProperties(component, "RRULE"); - String rdate = extractDates(component.getFirstProperty("RDATE")); - String exrule = flattenProperties(component, "EXRULE"); - String exdate = extractDates(component.getFirstProperty("EXDATE")); - - if ((TextUtils.isEmpty(dtstart))|| - (TextUtils.isEmpty(duration))|| - ((TextUtils.isEmpty(rrule))&& - (TextUtils.isEmpty(rdate)))) { - if (false) { - Log.d(TAG, "Recurrence missing DTSTART, DTEND/DURATION, " - + "or RRULE/RDATE: " - + component.toString()); - } - return false; - } - - if (allDay) { - start.timezone = Time.TIMEZONE_UTC; - } - long millis = start.toMillis(false /* use isDst */); - values.put(CalendarContract.Events.DTSTART, millis); - if (millis == -1) { - if (false) { - Log.d(TAG, "DTSTART is out of range: " + component.toString()); - } - return false; - } - - values.put(CalendarContract.Events.RRULE, rrule); - values.put(CalendarContract.Events.RDATE, rdate); - values.put(CalendarContract.Events.EXRULE, exrule); - values.put(CalendarContract.Events.EXDATE, exdate); - values.put(CalendarContract.Events.EVENT_TIMEZONE, tzid); - values.put(CalendarContract.Events.DURATION, duration); - values.put(CalendarContract.Events.ALL_DAY, allDay ? 1 : 0); - return true; - } - - // This can be removed when the old CalendarSyncAdapter is removed. - public static boolean populateComponent(Cursor cursor, - ICalendar.Component component) { - - int dtstartColumn = cursor.getColumnIndex(CalendarContract.Events.DTSTART); - int durationColumn = cursor.getColumnIndex(CalendarContract.Events.DURATION); - int tzidColumn = cursor.getColumnIndex(CalendarContract.Events.EVENT_TIMEZONE); - int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE); - int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE); - int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE); - int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE); - int allDayColumn = cursor.getColumnIndex(CalendarContract.Events.ALL_DAY); - - - long dtstart = -1; - if (!cursor.isNull(dtstartColumn)) { - dtstart = cursor.getLong(dtstartColumn); - } - String duration = cursor.getString(durationColumn); - String tzid = cursor.getString(tzidColumn); - String rruleStr = cursor.getString(rruleColumn); - String rdateStr = cursor.getString(rdateColumn); - String exruleStr = cursor.getString(exruleColumn); - String exdateStr = cursor.getString(exdateColumn); - boolean allDay = cursor.getInt(allDayColumn) == 1; - - if ((dtstart == -1) || - (TextUtils.isEmpty(duration))|| - ((TextUtils.isEmpty(rruleStr))&& - (TextUtils.isEmpty(rdateStr)))) { - // no recurrence. - return false; - } - - ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART"); - Time dtstartTime = null; - if (!TextUtils.isEmpty(tzid)) { - if (!allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid)); - } - dtstartTime = new Time(tzid); - } else { - // use the "floating" timezone - dtstartTime = new Time(Time.TIMEZONE_UTC); - } - - dtstartTime.set(dtstart); - // make sure the time is printed just as a date, if all day. - // TODO: android.pim.Time really should take care of this for us. - if (allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE")); - dtstartTime.allDay = true; - dtstartTime.hour = 0; - dtstartTime.minute = 0; - dtstartTime.second = 0; - } - - dtstartProp.setValue(dtstartTime.format2445()); - component.addProperty(dtstartProp); - ICalendar.Property durationProp = new ICalendar.Property("DURATION"); - durationProp.setValue(duration); - component.addProperty(durationProp); - - addPropertiesForRuleStr(component, "RRULE", rruleStr); - addPropertyForDateStr(component, "RDATE", rdateStr); - addPropertiesForRuleStr(component, "EXRULE", exruleStr); - addPropertyForDateStr(component, "EXDATE", exdateStr); - return true; - } - -public static boolean populateComponent(ContentValues values, - ICalendar.Component component) { - long dtstart = -1; - if (values.containsKey(CalendarContract.Events.DTSTART)) { - dtstart = values.getAsLong(CalendarContract.Events.DTSTART); - } - String duration = values.getAsString(CalendarContract.Events.DURATION); - String tzid = values.getAsString(CalendarContract.Events.EVENT_TIMEZONE); - String rruleStr = values.getAsString(CalendarContract.Events.RRULE); - String rdateStr = values.getAsString(CalendarContract.Events.RDATE); - String exruleStr = values.getAsString(CalendarContract.Events.EXRULE); - String exdateStr = values.getAsString(CalendarContract.Events.EXDATE); - Integer allDayInteger = values.getAsInteger(CalendarContract.Events.ALL_DAY); - boolean allDay = (null != allDayInteger) ? (allDayInteger == 1) : false; - - if ((dtstart == -1) || - (TextUtils.isEmpty(duration))|| - ((TextUtils.isEmpty(rruleStr))&& - (TextUtils.isEmpty(rdateStr)))) { - // no recurrence. - return false; - } - - ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART"); - Time dtstartTime = null; - if (!TextUtils.isEmpty(tzid)) { - if (!allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid)); - } - dtstartTime = new Time(tzid); - } else { - // use the "floating" timezone - dtstartTime = new Time(Time.TIMEZONE_UTC); - } - - dtstartTime.set(dtstart); - // make sure the time is printed just as a date, if all day. - // TODO: android.pim.Time really should take care of this for us. - if (allDay) { - dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE")); - dtstartTime.allDay = true; - dtstartTime.hour = 0; - dtstartTime.minute = 0; - dtstartTime.second = 0; - } - - dtstartProp.setValue(dtstartTime.format2445()); - component.addProperty(dtstartProp); - ICalendar.Property durationProp = new ICalendar.Property("DURATION"); - durationProp.setValue(duration); - component.addProperty(durationProp); - - addPropertiesForRuleStr(component, "RRULE", rruleStr); - addPropertyForDateStr(component, "RDATE", rdateStr); - addPropertiesForRuleStr(component, "EXRULE", exruleStr); - addPropertyForDateStr(component, "EXDATE", exdateStr); - return true; - } - - private static void addPropertiesForRuleStr(ICalendar.Component component, - String propertyName, - String ruleStr) { - if (TextUtils.isEmpty(ruleStr)) { - return; - } - String[] rrules = getRuleStrings(ruleStr); - for (String rrule : rrules) { - ICalendar.Property prop = new ICalendar.Property(propertyName); - prop.setValue(rrule); - component.addProperty(prop); - } - } - - private static String[] getRuleStrings(String ruleStr) { - if (null == ruleStr) { - return new String[0]; - } - String unfoldedRuleStr = unfold(ruleStr); - String[] split = unfoldedRuleStr.split(RULE_SEPARATOR); - int count = split.length; - for (int n = 0; n < count; n++) { - split[n] = fold(split[n]); - } - return split; - } - - - private static final Pattern IGNORABLE_ICAL_WHITESPACE_RE = - Pattern.compile("(?:\\r\\n?|\\n)[ \t]"); - - private static final Pattern FOLD_RE = Pattern.compile(".{75}"); - - /** - * fold and unfolds ical content lines as per RFC 2445 section 4.1. - * - * <h3>4.1 Content Lines</h3> - * - * <p>The iCalendar object is organized into individual lines of text, called - * content lines. Content lines are delimited by a line break, which is a CRLF - * sequence (US-ASCII decimal 13, followed by US-ASCII decimal 10). - * - * <p>Lines of text SHOULD NOT be longer than 75 octets, excluding the line - * break. Long content lines SHOULD be split into a multiple line - * representations using a line "folding" technique. That is, a long line can - * be split between any two characters by inserting a CRLF immediately - * followed by a single linear white space character (i.e., SPACE, US-ASCII - * decimal 32 or HTAB, US-ASCII decimal 9). Any sequence of CRLF followed - * immediately by a single linear white space character is ignored (i.e., - * removed) when processing the content type. - */ - public static String fold(String unfoldedIcalContent) { - return FOLD_RE.matcher(unfoldedIcalContent).replaceAll("$0\r\n "); - } - - public static String unfold(String foldedIcalContent) { - return IGNORABLE_ICAL_WHITESPACE_RE.matcher( - foldedIcalContent).replaceAll(""); - } - - private static void addPropertyForDateStr(ICalendar.Component component, - String propertyName, - String dateStr) { - if (TextUtils.isEmpty(dateStr)) { - return; - } - - ICalendar.Property prop = new ICalendar.Property(propertyName); - String tz = null; - int tzidx = dateStr.indexOf(";"); - if (tzidx != -1) { - tz = dateStr.substring(0, tzidx); - dateStr = dateStr.substring(tzidx + 1); - } - if (!TextUtils.isEmpty(tz)) { - prop.addParameter(new ICalendar.Parameter("TZID", tz)); - } - prop.setValue(dateStr); - component.addProperty(prop); - } - - private static String computeDuration(Time start, - ICalendar.Component component) { - // see if a duration is defined - ICalendar.Property durationProperty = - component.getFirstProperty("DURATION"); - if (durationProperty != null) { - // just return the duration - return durationProperty.getValue(); - } - - // must compute a duration from the DTEND - ICalendar.Property dtendProperty = - component.getFirstProperty("DTEND"); - if (dtendProperty == null) { - // no DURATION, no DTEND: 0 second duration - return "+P0S"; - } - ICalendar.Parameter endTzidParameter = - dtendProperty.getFirstParameter("TZID"); - String endTzid = (endTzidParameter == null) - ? start.timezone : endTzidParameter.value; - - Time end = new Time(endTzid); - end.parse(dtendProperty.getValue()); - long durationMillis = end.toMillis(false /* use isDst */) - - start.toMillis(false /* use isDst */); - long durationSeconds = (durationMillis / 1000); - if (start.allDay && (durationSeconds % 86400) == 0) { - return "P" + (durationSeconds / 86400) + "D"; // Server wants this instead of P86400S - } else { - return "P" + durationSeconds + "S"; - } - } - - private static String flattenProperties(ICalendar.Component component, - String name) { - List<ICalendar.Property> properties = component.getProperties(name); - if (properties == null || properties.isEmpty()) { - return null; - } - - if (properties.size() == 1) { - return properties.get(0).getValue(); - } - - StringBuilder sb = new StringBuilder(); - - boolean first = true; - for (ICalendar.Property property : component.getProperties(name)) { - if (first) { - first = false; - } else { - // TODO: use commas. our RECUR parsing should handle that - // anyway. - sb.append(RULE_SEPARATOR); - } - sb.append(property.getValue()); - } - return sb.toString(); - } - - private static String extractDates(ICalendar.Property recurrence) { - if (recurrence == null) { - return null; - } - ICalendar.Parameter tzidParam = - recurrence.getFirstParameter("TZID"); - if (tzidParam != null) { - return tzidParam.value + ";" + recurrence.getValue(); - } - return recurrence.getValue(); - } -} diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index b492615..5b29103 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -17,6 +17,8 @@ package android.provider; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ContentProviderClient; @@ -83,7 +85,6 @@ import android.util.Log; * adapters</li> * </ul> * - * @hide */ public final class CalendarContract { private static final String TAG = "Calendar"; @@ -92,8 +93,8 @@ public final class CalendarContract { * Broadcast Action: This is the intent that gets fired when an alarm * notification needs to be posted for a reminder. * - * @SdkConstant */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER"; /** @@ -146,6 +147,11 @@ public final class CalendarContract { public static final String ACCOUNT_TYPE_LOCAL = "LOCAL"; /** + * This utility class cannot be instantiated + */ + private CalendarContract() {} + + /** * Generic columns for use by sync adapters. The specific functions of these * columns are private to the sync adapter. Other clients of the API should * not attempt to either read or write this column. These columns are @@ -384,7 +390,7 @@ public final class CalendarContract { * Class that represents a Calendar Entity. There is one entry per calendar. * This is a helper class to make batch operations easier. */ - public static class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns { + public static final class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns { /** * The default Uri used when creating a new calendar EntityIterator. @@ -394,6 +400,11 @@ public final class CalendarContract { "/calendar_entities"); /** + * This utility class cannot be instantiated + */ + private CalendarEntity() {} + + /** * Creates an entity iterator for the given cursor. It assumes the * cursor contains a calendars query. * @@ -566,7 +577,13 @@ public final class CalendarContract { * <li>{@link #CAL_SYNC10}</li> * </ul> */ - public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns { + public static final class Calendars implements BaseColumns, SyncColumns, CalendarColumns { + + /** + * This utility class cannot be instantiated + */ + private Calendars() {} + /** * The content:// style URL for accessing Calendars */ @@ -687,7 +704,7 @@ public final class CalendarContract { /** * Fields and helpers for interacting with Attendees. Each row of this table * represents a single attendee or guest of an event. Calling - * {@link #query(ContentResolver, long)} will return a list of attendees for + * {@link #query(ContentResolver, long, String[])} will return a list of attendees for * the event with the given eventId. Both apps and sync adapters may write * to this table. There are six writable fields and all of them except * {@link #ATTENDEE_NAME} must be included when inserting a new attendee. @@ -708,12 +725,12 @@ public final class CalendarContract { */ @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees"); + private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; + /** - * the projection used by the attendees query + * This utility class cannot be instantiated */ - public static final String[] PROJECTION = new String[] { - _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,}; - private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; + private Attendees() {} /** * Queries all attendees associated with the given event. This is a @@ -721,11 +738,12 @@ public final class CalendarContract { * * @param cr The content resolver to use for the query * @param eventId The id of the event to retrieve attendees for + * @param projection the columns to return in the cursor * @return A Cursor containing all attendees for the event */ - public static final Cursor query(ContentResolver cr, long eventId) { + public static final Cursor query(ContentResolver cr, long eventId, String[] projection) { String[] attArgs = {Long.toString(eventId)}; - return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */, + return cr.query(CONTENT_URI, projection, ATTENDEES_WHERE, attArgs /* selection args */, null /* sort order */); } } @@ -1068,6 +1086,11 @@ public final class CalendarContract { "/event_entities"); /** + * This utility class cannot be instantiated + */ + private EventsEntity() {} + + /** * Creates a new iterator for events * * @param cursor An event query @@ -1411,6 +1434,11 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/exception"); /** + * This utility class cannot be instantiated + */ + private Events() {} + + /** * The default sort order for this table */ private static final String DEFAULT_SORT_ORDER = ""; @@ -1484,6 +1512,11 @@ public final class CalendarContract { }; /** + * This utility class cannot be instantiated + */ + private Instances() {} + + /** * Performs a query to return all visible instances in the given range. * This is a blocking function and should not be done on the UI thread. * This will cause an expansion of recurring events to fill this time @@ -1636,7 +1669,7 @@ public final class CalendarContract { * time zone for the instances. These settings are stored using a key/value * scheme. A {@link #KEY} must be specified when updating these values. */ - public static class CalendarCache implements CalendarCacheColumns { + public static final class CalendarCache implements CalendarCacheColumns { /** * The URI to use for retrieving the properties from the Calendar db. */ @@ -1644,6 +1677,11 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/properties"); /** + * This utility class cannot be instantiated + */ + private CalendarCache() {} + + /** * They key for updating the use of auto/home time zones in Calendar. * Valid values are {@link #TIMEZONE_TYPE_AUTO} or * {@link #TIMEZONE_TYPE_HOME}. @@ -1724,6 +1762,11 @@ public final class CalendarContract { * @hide */ public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns { + + /** + * This utility class cannot be instantiated + */ + private CalendarMetaData() {} } protected interface EventDaysColumns { @@ -1746,14 +1789,12 @@ public final class CalendarContract { public static final class EventDays implements EventDaysColumns { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/instances/groupbyday"); + private static final String SELECTION = "selected=1"; /** - * The projection used by the EventDays query. + * This utility class cannot be instantiated */ - public static final String[] PROJECTION = { - STARTDAY, ENDDAY - }; - private static final String SELECTION = "selected=1"; + private EventDays() {} /** * Retrieves the days with events for the Julian days starting at @@ -1765,10 +1806,12 @@ public final class CalendarContract { * @param cr the ContentResolver * @param startDay the first Julian day in the range * @param numDays the number of days to load (must be at least 1) + * @param projection the columns to return in the cursor * @return a database cursor containing a list of start and end days for * events */ - public static final Cursor query(ContentResolver cr, int startDay, int numDays) { + public static final Cursor query(ContentResolver cr, int startDay, int numDays, + String[] projection) { if (numDays < 1) { return null; } @@ -1776,7 +1819,7 @@ public final class CalendarContract { Uri.Builder builder = CONTENT_URI.buildUpon(); ContentUris.appendId(builder, startDay); ContentUris.appendId(builder, endDay); - return cr.query(builder.build(), PROJECTION, SELECTION, + return cr.query(builder.build(), projection, SELECTION, null /* selection args */, STARTDAY); } } @@ -1821,7 +1864,7 @@ public final class CalendarContract { /** * Fields and helpers for accessing reminders for an event. Each row of this * table represents a single reminder for an event. Calling - * {@link #query(ContentResolver, long)} will return a list of reminders for + * {@link #query(ContentResolver, long, String[])} will return a list of reminders for * the event with the given eventId. Both apps and sync adapters may write * to this table. There are three writable fields and all of them must be * included when inserting a new reminder. They are: @@ -1833,25 +1876,26 @@ public final class CalendarContract { */ public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns { private static final String REMINDERS_WHERE = CalendarContract.Reminders.EVENT_ID + "=?"; - /** - * The projection used by the reminders query. - */ - public static final String[] PROJECTION = new String[] { - _ID, MINUTES, METHOD,}; @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders"); /** + * This utility class cannot be instantiated + */ + private Reminders() {} + + /** * Queries all reminders associated with the given event. This is a * blocking call and should not be done on the UI thread. * * @param cr The content resolver to use for the query * @param eventId The id of the event to retrieve reminders for + * @param projection the columns to return in the cursor * @return A Cursor containing all reminders for the event */ - public static final Cursor query(ContentResolver cr, long eventId) { + public static final Cursor query(ContentResolver cr, long eventId, String[] projection) { String[] remArgs = {Long.toString(eventId)}; - return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */, + return cr.query(CONTENT_URI, projection, REMINDERS_WHERE, remArgs /*selection args*/, null /* sort order */); } } @@ -1964,6 +2008,11 @@ public final class CalendarContract { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendar_alerts"); + /** + * This utility class cannot be instantiated + */ + private CalendarAlerts() {} + private static final String WHERE_ALARM_EXISTS = EVENT_ID + "=?" + " AND " + BEGIN + "=?" + " AND " + ALARM_TIME + "=?"; @@ -2134,7 +2183,7 @@ public final class CalendarContract { * given event id, begin time and alarm time. If one is found then this * alarm already exists and this method returns true. TODO Move to * provider - * + * * @param cr the ContentResolver * @param eventId the event id to match * @param begin the start time of the event in UTC millis @@ -2203,6 +2252,11 @@ public final class CalendarContract { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/extendedproperties"); + /** + * This utility class cannot be instantiated + */ + private ExtendedProperties() {} + // TODO: fill out this class when we actually start utilizing extendedproperties // in the calendar application. } @@ -2271,5 +2325,10 @@ public final class CalendarContract { * @hide */ public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns { + + /** + * This utility class cannot be instantiated + */ + private EventsRawTimes() {} } } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 61deea4..ec67683 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -135,14 +135,6 @@ public final class ContactsContract { public static final String ALLOW_PROFILE = "allow_profile"; /** - * A query parameter key used to specify the package that is requesting a query. - * This is used for restricting data based on package name. - * - * @hide - */ - public static final String REQUESTING_PACKAGE_PARAM_KEY = "requesting_package"; - - /** * Query parameter that should be used by the client to access a specific * {@link Directory}. The parameter value should be the _ID of the corresponding * directory, e.g. @@ -271,8 +263,6 @@ public final class ContactsContract { * <li>The URI authority is replaced with the corresponding {@link #DIRECTORY_AUTHORITY}.</li> * <li>The {@code accountName=} and {@code accountType=} parameters are added or * replaced using the corresponding {@link #ACCOUNT_TYPE} and {@link #ACCOUNT_NAME} values.</li> - * <li>If the URI is missing a ContactsContract.REQUESTING_PACKAGE_PARAM_KEY - * parameter, this parameter is added.</li> * </ul> * </p> * <p> @@ -1881,13 +1871,16 @@ public final class ContactsContract { public static final String CONTACT_ID = "contact_id"; /** - * Flag indicating that this {@link RawContacts} entry and its children have - * been restricted to specific platform apps. - * <P>Type: INTEGER (boolean)</P> + * The data set within the account that this row belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' data. * - * @hide until finalized in future platform release + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct data for + * the same account type and account name. + * <P>Type: TEXT</P> */ - public static final String IS_RESTRICTED = "is_restricted"; + public static final String DATA_SET = "data_set"; /** * The aggregation mode for this contact. @@ -2211,8 +2204,8 @@ public final class ContactsContract { * <td>The name of the account instance to which this row belongs, which when paired with * {@link #ACCOUNT_TYPE} identifies a specific account. * For example, this will be the Gmail address if it is a Google account. - * It should be set at the time - * the raw contact is inserted and never changed afterwards.</td> + * It should be set at the time the raw contact is inserted and never + * changed afterwards.</td> * </tr> * <tr> * <td>String</td> @@ -2222,8 +2215,8 @@ public final class ContactsContract { * <p> * The type of account to which this row belongs, which when paired with * {@link #ACCOUNT_NAME} identifies a specific account. - * It should be set at the time - * the raw contact is inserted and never changed afterwards. + * It should be set at the time the raw contact is inserted and never + * changed afterwards. * </p> * <p> * To ensure uniqueness, new account types should be chosen according to the @@ -2233,15 +2226,38 @@ public final class ContactsContract { * </tr> * <tr> * <td>String</td> + * <td>{@link #DATA_SET}</td> + * <td>read/write-once</td> + * <td> + * <p> + * The data set within the account that this row belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' data. The combination of {@link #ACCOUNT_TYPE}, + * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data + * that is associated with a single sync adapter. + * </p> + * <p> + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct data for + * the same account type and account name. + * </p> + * <p> + * It should be set at the time the raw contact is inserted and never + * changed afterwards. + * </p> + * </td> + * </tr> + * <tr> + * <td>String</td> * <td>{@link #SOURCE_ID}</td> * <td>read/write</td> * <td>String that uniquely identifies this row to its source account. * Typically it is set at the time the raw contact is inserted and never * changed afterwards. The one notable exception is a new raw contact: it - * will have an account name and type, but no source id. This - * indicates to the sync adapter that a new contact needs to be created - * server-side and its ID stored in the corresponding SOURCE_ID field on - * the phone. + * will have an account name and type (and possibly a data set), but no + * source id. This indicates to the sync adapter that a new contact needs + * to be created server-side and its ID stored in the corresponding + * SOURCE_ID field on the phone. * </td> * </tr> * <tr> @@ -2537,7 +2553,6 @@ public final class ContactsContract { DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DELETED); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, CONTACT_ID); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, STARRED); - DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, IS_RESTRICTED); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, NAME_VERIFIED); android.content.Entity contact = new android.content.Entity(cv); @@ -3814,27 +3829,6 @@ public final class ContactsContract { /** * <p> - * If {@link #FOR_EXPORT_ONLY} is explicitly set to "1", returned Cursor toward - * Data.CONTENT_URI contains only exportable data. - * </p> - * <p> - * This flag is useful (currently) only for vCard exporter in Contacts app, which - * needs to exclude "un-exportable" data from available data to export, while - * Contacts app itself has priviledge to access all data including "un-exportable" - * ones and providers return all of them regardless of the callers' intention. - * </p> - * <p> - * Type: INTEGER - * </p> - * - * @hide Maybe available only in Eclair and not really ready for public use. - * TODO: remove, or implement this feature completely. As of now (Eclair), - * we only use this flag in queryEntities(), not query(). - */ - public static final String FOR_EXPORT_ONLY = "for_export_only"; - - /** - * <p> * Build a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} * style {@link Uri} for the parent {@link android.provider.ContactsContract.Contacts} * entry of the given {@link ContactsContract.Data} entry. @@ -6213,6 +6207,18 @@ public final class ContactsContract { */ protected interface GroupsColumns { /** + * The data set within the account that this group belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' group data. + * + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct group data + * for the same account type and account name. + * <P>Type: TEXT</P> + */ + public static final String DATA_SET = "data_set"; + + /** * The display title of this group. * <p> * Type: TEXT @@ -6338,6 +6344,29 @@ public final class ContactsContract { * In other words, it would be a really bad idea to delete and reinsert a * group. A sync adapter should always do an update instead.</td> * </tr> + # <tr> + * <td>String</td> + * <td>{@link #DATA_SET}</td> + * <td>read/write-once</td> + * <td> + * <p> + * The data set within the account that this group belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' group data. The combination of {@link #ACCOUNT_TYPE}, + * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data + * that is associated with a single sync adapter. + * </p> + * <p> + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct data for + * the same account type and account name. + * </p> + * <p> + * It should be set at the time the group is inserted and never changed + * afterwards. + * </p> + * </td> + * </tr> * <tr> * <td>String</td> * <td>{@link #TITLE}</td> diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 65babc2..23b53ae 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3833,6 +3833,11 @@ public final class Settings { /** {@hide} */ public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history"; + /** Preferred NTP server. {@hide} */ + public static final String NTP_SERVER = "ntp_server"; + /** Timeout in milliseconds to wait for NTP server. {@hide} */ + public static final String NTP_TIMEOUT = "ntp_timeout"; + /** * @hide */ diff --git a/core/java/android/server/BluetoothHealthProfileHandler.java b/core/java/android/server/BluetoothHealthProfileHandler.java index 7f862e0..105ff33 100644 --- a/core/java/android/server/BluetoothHealthProfileHandler.java +++ b/core/java/android/server/BluetoothHealthProfileHandler.java @@ -20,15 +20,12 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealth; import android.bluetooth.BluetoothHealthAppConfiguration; -import android.bluetooth.BluetoothHealth; -import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.IBluetoothHealthCallback; import android.content.Context; -import android.content.Intent; import android.os.Handler; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.provider.Settings; import android.util.Log; import java.util.ArrayList; @@ -36,10 +33,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map.Entry; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; - /** * This handles all the operations on the Bluetooth Health profile. * All functions are called by BluetoothService, as Bluetooth Service @@ -58,6 +51,7 @@ final class BluetoothHealthProfileHandler { private ArrayList<HealthChannel> mHealthChannels; private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs; private HashMap <BluetoothDevice, Integer> mHealthDevices; + private HashMap <BluetoothHealthAppConfiguration, IBluetoothHealthCallback> mCallbacks; private static final int MESSAGE_REGISTER_APPLICATION = 0; private static final int MESSAGE_UNREGISTER_APPLICATION = 1; @@ -103,6 +97,7 @@ final class BluetoothHealthProfileHandler { } if (path == null) { + mCallbacks.remove(registerApp); callHealthApplicationStatusCallback(registerApp, BluetoothHealth.APPLICATION_REGISTRATION_FAILURE); } else { @@ -118,6 +113,7 @@ final class BluetoothHealthProfileHandler { boolean result = mBluetoothService.unregisterHealthApplicationNative( mHealthAppConfigs.get(unregisterApp)); if (result) { + mCallbacks.remove(unregisterApp); callHealthApplicationStatusCallback(unregisterApp, BluetoothHealth.APPLICATION_UNREGISTRATION_SUCCESS); } else { @@ -149,6 +145,7 @@ final class BluetoothHealthProfileHandler { mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>(); mHealthChannels = new ArrayList<HealthChannel>(); mHealthDevices = new HashMap<BluetoothDevice, Integer>(); + mCallbacks = new HashMap<BluetoothHealthAppConfiguration, IBluetoothHealthCallback>(); } static synchronized BluetoothHealthProfileHandler getInstance(Context context, @@ -157,10 +154,12 @@ final class BluetoothHealthProfileHandler { return sInstance; } - boolean registerAppConfiguration(BluetoothHealthAppConfiguration config) { + boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, + IBluetoothHealthCallback callback) { Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION); msg.obj = config; mHandler.sendMessage(msg); + mCallbacks.put(config, callback); return true; } @@ -442,11 +441,11 @@ final class BluetoothHealthProfileHandler { debugLog("Health Device Callback: " + device + " State Change: " + prevState + "->" + state); - try { - config.getCallback().onHealthChannelStateChange(config, device, prevState, - state, fd); - } catch (RemoteException e) { - errorLog("Error while making health channel state change callback: " + e); + IBluetoothHealthCallback callback = mCallbacks.get(config); + if (callback != null) { + try { + callback.onHealthChannelStateChange(config, device, prevState, state, fd); + } catch (RemoteException e) {} } } @@ -454,10 +453,11 @@ final class BluetoothHealthProfileHandler { BluetoothHealthAppConfiguration config, int status) { debugLog("Health Device Application: " + config + " State Change: status:" + status); - try { - config.getCallback().onHealthAppConfigurationStatusChange(config, status); - } catch (RemoteException e) { - errorLog("Error while making health app registration state change callback: " + e); + IBluetoothHealthCallback callback = mCallbacks.get(config); + if (callback != null) { + try { + callback.onHealthAppConfigurationStatusChange(config, status); + } catch (RemoteException e) {} } } @@ -526,19 +526,19 @@ final class BluetoothHealthProfileHandler { List<HealthChannel> chan; switch (currDeviceState) { case BluetoothHealth.STATE_DISCONNECTED: - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); break; case BluetoothHealth.STATE_CONNECTING: // Channel got connected. if (newDeviceState == BluetoothHealth.STATE_CONNECTED) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } else { // Channel got disconnected chan = findChannelByStates(device, new int [] { BluetoothHealth.STATE_CHANNEL_CONNECTING, BluetoothHealth.STATE_CHANNEL_DISCONNECTING}); if (chan.isEmpty()) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } } break; @@ -548,22 +548,23 @@ final class BluetoothHealthProfileHandler { BluetoothHealth.STATE_CHANNEL_CONNECTING, BluetoothHealth.STATE_CHANNEL_CONNECTED}); if (chan.isEmpty()) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } + break; case BluetoothHealth.STATE_DISCONNECTING: // Channel got disconnected. chan = findChannelByStates(device, new int [] { BluetoothHealth.STATE_CHANNEL_CONNECTING, BluetoothHealth.STATE_CHANNEL_DISCONNECTING}); if (chan.isEmpty()) { - updateAndsendIntent(device, currDeviceState, newDeviceState); + updateAndSendIntent(device, currDeviceState, newDeviceState); } break; } } } - private void updateAndsendIntent(BluetoothDevice device, int prevDeviceState, + private void updateAndSendIntent(BluetoothDevice device, int prevDeviceState, int newDeviceState) { mHealthDevices.put(device, newDeviceState); mBluetoothService.sendConnectionStateChange(device, prevDeviceState, newDeviceState); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 9839f76..b23e3ce 100644..100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -24,8 +24,6 @@ package android.server; -import com.android.internal.app.IBatteryStats; - import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -40,6 +38,7 @@ import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothHealthCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -58,6 +57,8 @@ import android.provider.Settings; import android.util.Log; import android.util.Pair; +import com.android.internal.app.IBatteryStats; + import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -2111,11 +2112,12 @@ public class BluetoothService extends IBluetooth.Stub { /**** Handlers for Health Device Profile ****/ // TODO: All these need to be converted to a state machine. - public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config) { + public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, + IBluetoothHealthCallback callback) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); synchronized (mBluetoothHealthProfileHandler) { - return mBluetoothHealthProfileHandler.registerAppConfiguration(config); + return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback); } } diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index c7603ee..8ef4295 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -384,11 +384,16 @@ class AudioPlaybackHandler { } count += written; } + + param.mLogger.onPlaybackStart(); } private void handleSynthesisDone(MessageParams msg) { final SynthesisMessageParams params = (SynthesisMessageParams) msg; handleSynthesisDone(params); + // This call is delayed more than it should be, but we are + // certain at this point that we have all the data we want. + params.mLogger.onWriteData(); } // Flush all remaining data to the audio track, stop it and release @@ -416,6 +421,8 @@ class AudioPlaybackHandler { final SynthesisMessageParams params = (SynthesisMessageParams) msg; if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")"); + params.mLogger.onPlaybackStart(); + // Channel config and bytes per frame are checked before // this message is sent. int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount); diff --git a/core/java/android/speech/tts/EventLogTags.logtags b/core/java/android/speech/tts/EventLogTags.logtags new file mode 100644 index 0000000..1a9f5fe --- /dev/null +++ b/core/java/android/speech/tts/EventLogTags.logtags @@ -0,0 +1,6 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package android.speech.tts; + +76001 tts_speak_success (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1),(engine_latency|2|3),(engine_total|2|3),(audio_latency|2|3) +76002 tts_speak_failure (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1) diff --git a/core/java/android/speech/tts/EventLogger.java b/core/java/android/speech/tts/EventLogger.java new file mode 100644 index 0000000..63b954b --- /dev/null +++ b/core/java/android/speech/tts/EventLogger.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package android.speech.tts; + +import android.os.SystemClock; +import android.text.TextUtils; + +/** + * Writes data about a given speech synthesis request to the event logs. + * The data that is logged includes the calling app, length of the utterance, + * speech rate / pitch and the latency and overall time taken. + * + * Note that {@link EventLogger#onStopped()} and {@link EventLogger#onError()} + * might be called from any thread, but on {@link EventLogger#onPlaybackStart()} and + * {@link EventLogger#onComplete()} must be called from a single thread + * (usually the audio playback thread} + */ +class EventLogger { + private final SynthesisRequest mRequest; + private final String mCallingApp; + private final String mServiceApp; + private final long mReceivedTime; + private long mPlaybackStartTime = -1; + private volatile long mRequestProcessingStartTime = -1; + private volatile long mEngineStartTime = -1; + private volatile long mEngineCompleteTime = -1; + + private volatile boolean mError = false; + private volatile boolean mStopped = false; + private boolean mLogWritten = false; + + EventLogger(SynthesisRequest request, String callingApp, + String serviceApp) { + mRequest = request; + mCallingApp = callingApp; + mServiceApp = serviceApp; + mReceivedTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that this request has been selected from + * the processing queue for processing. Engine latency / total time + * is measured from this baseline. + */ + public void onRequestProcessingStart() { + mRequestProcessingStartTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that a chunk of data has been received from + * the engine. Might be called multiple times. + */ + public void onEngineDataReceived() { + if (mEngineStartTime == -1) { + mEngineStartTime = SystemClock.elapsedRealtime(); + } + } + + /** + * Notifies the logger that the engine has finished processing data. + * Will be called exactly once. + */ + public void onEngineComplete() { + mEngineCompleteTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that audio playback has started for some section + * of the synthesis. This is normally some amount of time after the engine + * has synthesized data and varides depending on utterances and + * other audio currently in the queue. + */ + public void onPlaybackStart() { + // For now, keep track of only the first chunk of audio + // that was played. + if (mPlaybackStartTime == -1) { + mPlaybackStartTime = SystemClock.elapsedRealtime(); + } + } + + /** + * Notifies the logger that the current synthesis was stopped. + * Latency numbers are not reported for stopped syntheses. + */ + public void onStopped() { + mStopped = false; + } + + /** + * Notifies the logger that the current synthesis resulted in + * an error. This is logged using {@link EventLogTags#writeTtsSpeakFailure}. + */ + public void onError() { + mError = true; + } + + /** + * Notifies the logger that the current synthesis has completed. + * All available data is not logged. + */ + public void onWriteData() { + if (mLogWritten) { + return; + } else { + mLogWritten = true; + } + + long completionTime = SystemClock.elapsedRealtime(); + // onPlaybackStart() should normally always be called if an + // error does not occur. + if (mError || mPlaybackStartTime == -1 || mEngineCompleteTime == -1) { + EventLogTags.writeTtsSpeakFailure(mServiceApp, mCallingApp, + getUtteranceLength(), getLocaleString(), + mRequest.getSpeechRate(), mRequest.getPitch()); + return; + } + + // We don't report stopped syntheses because their overall + // total time spent will be innacurate (will not correlate with + // the length of the utterance). + if (mStopped) { + return; + } + + final long audioLatency = mPlaybackStartTime - mReceivedTime; + final long engineLatency = mEngineStartTime - mRequestProcessingStartTime; + final long engineTotal = mEngineCompleteTime - mRequestProcessingStartTime; + EventLogTags.writeTtsSpeakSuccess(mServiceApp, mCallingApp, + getUtteranceLength(), getLocaleString(), + mRequest.getSpeechRate(), mRequest.getPitch(), + engineLatency, engineTotal, audioLatency); + } + + /** + * @return the length of the utterance for the given synthesis, 0 + * if the utterance was {@code null}. + */ + private int getUtteranceLength() { + final String utterance = mRequest.getText(); + return utterance == null ? 0 : utterance.length(); + } + + /** + * Returns a formatted locale string from the synthesis params of the + * form lang-country-variant. + */ + private String getLocaleString() { + StringBuilder sb = new StringBuilder(mRequest.getLanguage()); + if (!TextUtils.isEmpty(mRequest.getCountry())) { + sb.append('-'); + sb.append(mRequest.getCountry()); + + if (!TextUtils.isEmpty(mRequest.getVariant())) { + sb.append('-'); + sb.append(mRequest.getVariant()); + } + } + + return sb.toString(); + } + +} diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index bdaa1b8..38030a6 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -65,29 +65,42 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { private final UtteranceCompletedDispatcher mDispatcher; private final String mCallingApp; + private final EventLogger mLogger; PlaybackSynthesisCallback(int streamType, float volume, float pan, AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher, - String callingApp) { + String callingApp, EventLogger logger) { mStreamType = streamType; mVolume = volume; mPan = pan; mAudioTrackHandler = audioTrackHandler; mDispatcher = dispatcher; mCallingApp = callingApp; + mLogger = logger; } @Override void stop() { if (DBG) Log.d(TAG, "stop()"); + // Note that mLogger.mError might be true too at this point. + mLogger.onStopped(); + synchronized (mStateLock) { - if (mToken == null || mStopped) { - Log.w(TAG, "stop() called twice, before start(), or after done()"); + if (mStopped) { + Log.w(TAG, "stop() called twice"); return; } - mAudioTrackHandler.stop(mToken); - mToken = null; + // mToken will be null if the engine encounters + // an error before it called start(). + if (mToken != null) { + mAudioTrackHandler.stop(mToken); + mToken = null; + } else { + // In all other cases, mAudioTrackHandler.stop() will + // result in onComplete being called. + mLogger.onWriteData(); + } mStopped = true; } } @@ -124,7 +137,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } SynthesisMessageParams params = new SynthesisMessageParams( mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, - mDispatcher, mCallingApp); + mDispatcher, mCallingApp, mLogger); mAudioTrackHandler.enqueueSynthesisStart(params); mToken = params; @@ -157,6 +170,8 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken); } + mLogger.onEngineDataReceived(); + return TextToSpeech.SUCCESS; } @@ -177,6 +192,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } mAudioTrackHandler.enqueueSynthesisDone(mToken); + mLogger.onEngineComplete(); } return TextToSpeech.SUCCESS; } @@ -184,6 +200,9 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { @Override public void error() { if (DBG) Log.d(TAG, "error() [will call stop]"); + // Currently, this call will not be logged if error( ) is called + // before start. + mLogger.onError(); stop(); } @@ -208,7 +227,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } SynthesisMessageParams params = new SynthesisMessageParams( mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, - mDispatcher, mCallingApp); + mDispatcher, mCallingApp, mLogger); params.addBuffer(buffer, offset, length); mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params); diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java index 51f3d2e..caf02ef 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -30,6 +30,7 @@ final class SynthesisMessageParams extends MessageParams { final int mChannelCount; final float mVolume; final float mPan; + final EventLogger mLogger; public volatile AudioTrack mAudioTrack; @@ -38,7 +39,7 @@ final class SynthesisMessageParams extends MessageParams { SynthesisMessageParams(int streamType, int sampleRate, int audioFormat, int channelCount, float volume, float pan, UtteranceCompletedDispatcher dispatcher, - String callingApp) { + String callingApp, EventLogger logger) { super(dispatcher, callingApp); mStreamType = streamType; @@ -47,6 +48,7 @@ final class SynthesisMessageParams extends MessageParams { mChannelCount = channelCount; mVolume = volume; mPan = pan; + mLogger = logger; // initially null. mAudioTrack = null; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 7ea9373..010c155 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -82,8 +82,7 @@ public abstract class TextToSpeechService extends Service { private AudioPlaybackHandler mAudioPlaybackHandler; private CallbackMap mCallbacks; - - private int mDefaultAvailability = TextToSpeech.LANG_NOT_SUPPORTED; + private String mPackageName; @Override public void onCreate() { @@ -99,9 +98,10 @@ public abstract class TextToSpeechService extends Service { mCallbacks = new CallbackMap(); + mPackageName = getApplicationInfo().packageName; + // Load default language - mDefaultAvailability = onLoadLanguage(getDefaultLanguage(), - getDefaultCountry(), getDefaultVariant()); + onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant()); } @Override @@ -457,12 +457,14 @@ public abstract class TextToSpeechService extends Service { // Non null after synthesis has started, and all accesses // guarded by 'this'. private AbstractSynthesisCallback mSynthesisCallback; + private final EventLogger mEventLogger; public SynthesisSpeechItem(String callingApp, Bundle params, String text) { super(callingApp, params); mText = text; mSynthesisRequest = new SynthesisRequest(mText, mParams); setRequestParams(mSynthesisRequest); + mEventLogger = new EventLogger(mSynthesisRequest, getCallingApp(), mPackageName); } public String getText() { @@ -485,6 +487,7 @@ public abstract class TextToSpeechService extends Service { @Override protected int playImpl() { AbstractSynthesisCallback synthesisCallback; + mEventLogger.onRequestProcessingStart(); synchronized (this) { mSynthesisCallback = createSynthesisCallback(); synthesisCallback = mSynthesisCallback; @@ -495,7 +498,7 @@ public abstract class TextToSpeechService extends Service { protected AbstractSynthesisCallback createSynthesisCallback() { return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(), - mAudioPlaybackHandler, this, getCallingApp()); + mAudioPlaybackHandler, this, getCallingApp(), mEventLogger); } private void setRequestParams(SynthesisRequest request) { diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index 240ad9b..555aac5 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -22,12 +22,22 @@ import android.os.Parcelable; import android.os.SystemClock; import android.text.ParcelableSpan; import android.text.TextUtils; +import android.widget.TextView; import java.util.Arrays; import java.util.Locale; /** - * Holds suggestion candidates of words under this span. + * Holds suggestion candidates for the text enclosed in this span. + * + * When such a span is edited in an EditText, double tapping on the text enclosed in this span will + * display a popup dialog listing suggestion replacement for that text. The user can then replace + * the original text by one of the suggestions. + * + * These spans should typically be created by the input method to privide correction and alternates + * for the text. + * + * @see TextView#setSuggestionsEnabled(boolean) */ public class SuggestionSpan implements ParcelableSpan { /** @@ -115,14 +125,14 @@ public class SuggestionSpan implements ParcelableSpan { } /** - * @return suggestions + * @return an array of suggestion texts for this span */ public String[] getSuggestions() { return mSuggestions; } /** - * @return locale of suggestions + * @return the locale of the suggestions */ public String getLocale() { return mLocaleString; diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 5b19ecd..2179ff3 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -16,41 +16,71 @@ package android.util; +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; import android.net.SntpClient; import android.os.SystemClock; +import android.provider.Settings; /** - * {@link TrustedTime} that connects with a remote NTP server as its remote - * trusted time source. + * {@link TrustedTime} that connects with a remote NTP server as its trusted + * time source. * * @hide */ public class NtpTrustedTime implements TrustedTime { - private String mNtpServer; - private long mNtpTimeout; + private static final String TAG = "NtpTrustedTime"; + private static final boolean LOGD = false; + + private static NtpTrustedTime sSingleton; + + private final String mServer; + private final long mTimeout; private boolean mHasCache; private long mCachedNtpTime; private long mCachedNtpElapsedRealtime; private long mCachedNtpCertainty; - public NtpTrustedTime() { + private NtpTrustedTime(String server, long timeout) { + if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server); + mServer = server; + mTimeout = timeout; } - public void setNtpServer(String server, long timeout) { - mNtpServer = server; - mNtpTimeout = timeout; + public static synchronized NtpTrustedTime getInstance(Context context) { + if (sSingleton == null) { + final Resources res = context.getResources(); + final ContentResolver resolver = context.getContentResolver(); + + final String defaultServer = res.getString( + com.android.internal.R.string.config_ntpServer); + final long defaultTimeout = res.getInteger( + com.android.internal.R.integer.config_ntpTimeout); + + final String secureServer = Settings.Secure.getString( + resolver, Settings.Secure.NTP_SERVER); + final long timeout = Settings.Secure.getLong( + resolver, Settings.Secure.NTP_TIMEOUT, defaultTimeout); + + final String server = secureServer != null ? secureServer : defaultServer; + sSingleton = new NtpTrustedTime(server, timeout); + } + + return sSingleton; } /** {@inheritDoc} */ public boolean forceRefresh() { - if (mNtpServer == null) { + if (mServer == null) { // missing server, so no trusted time available return false; } + if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); final SntpClient client = new SntpClient(); - if (client.requestTime(mNtpServer, (int) mNtpTimeout)) { + if (client.requestTime(mServer, (int) mTimeout)) { mHasCache = true; mCachedNtpTime = client.getNtpTime(); mCachedNtpElapsedRealtime = client.getNtpTimeReference(); @@ -89,9 +119,19 @@ public class NtpTrustedTime implements TrustedTime { if (!mHasCache) { throw new IllegalStateException("Missing authoritative time source"); } + if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit"); // current time is age after the last ntp cache; callers who // want fresh values will hit makeAuthoritative() first. return mCachedNtpTime + getCacheAge(); } + + public long getCachedNtpTime() { + if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit"); + return mCachedNtpTime; + } + + public long getCachedNtpTimeReference() { + return mCachedNtpElapsedRealtime; + } } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 188970c..011e44c 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -128,8 +128,23 @@ public abstract class HardwareRenderer { abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException; /** - * Setup the hardware renderer for drawing. This is called for every - * frame to draw. + * This method should be invoked whenever the current hardware renderer + * context should be reset. + */ + abstract void invalidate(); + + /** + * This method should be invoked to ensure the hardware renderer is in + * valid state (for instance, to ensure the correct EGL context is bound + * to the current thread.) + * + * @return true if the renderer is now valid, false otherwise + */ + abstract boolean validate(); + + /** + * Setup the hardware renderer for drawing. This is called whenever the + * size of the target surface changes or when the surface is first created. * * @param width Width of the drawing surface. * @param height Height of the drawing surface. @@ -289,9 +304,9 @@ public abstract class HardwareRenderer { static abstract class GlRenderer extends HardwareRenderer { // These values are not exposed in our EGL APIs static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - static final int EGL_SURFACE_TYPE = 0x3033; - static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; static final int EGL_OPENGL_ES2_BIT = 4; + static final int EGL_SURFACE_TYPE = 0x3033; + static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; private static final int SURFACE_STATE_ERROR = 0; private static final int SURFACE_STATE_SUCCESS = 1; @@ -311,8 +326,16 @@ public abstract class HardwareRenderer { int mFrameCount; Paint mDebugPaint; - boolean mDirtyRegions; - final boolean mDirtyRegionsRequested; + static boolean sDirtyRegions; + static final boolean sDirtyRegionsRequested; + static { + String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); + //noinspection PointlessBooleanExpression,ConstantConditions + sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); + sDirtyRegionsRequested = sDirtyRegions; + } + + boolean mDirtyRegionsEnabled; final boolean mVsyncDisabled; final int mGlVersion; @@ -326,11 +349,6 @@ public abstract class HardwareRenderer { mGlVersion = glVersion; mTranslucent = translucent; - final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); - //noinspection PointlessBooleanExpression,ConstantConditions - mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); - mDirtyRegionsRequested = mDirtyRegions; - final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty); if (mVsyncDisabled) { @@ -342,7 +360,7 @@ public abstract class HardwareRenderer { * Indicates whether this renderer instance can track and update dirty regions. */ boolean hasDirtyRegions() { - return mDirtyRegions; + return mDirtyRegionsEnabled; } /** @@ -479,8 +497,8 @@ public abstract class HardwareRenderer { sEglConfig = chooseEglConfig(); if (sEglConfig == null) { // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without - if (mDirtyRegions) { - mDirtyRegions = false; + if (sDirtyRegions) { + sDirtyRegions = false; sEglConfig = chooseEglConfig(); if (sEglConfig == null) { throw new RuntimeException("eglConfig not initialized"); @@ -500,7 +518,7 @@ public abstract class HardwareRenderer { private EGLConfig chooseEglConfig() { int[] configsCount = new int[1]; EGLConfig[] configs = new EGLConfig[1]; - int[] configSpec = getConfig(mDirtyRegions); + int[] configSpec = getConfig(sDirtyRegions); if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { throw new IllegalArgumentException("eglChooseConfig failed " + getEGLErrorString(sEgl.eglGetError())); @@ -566,18 +584,18 @@ public abstract class HardwareRenderer { // If mDirtyRegions is set, this means we have an EGL configuration // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set - if (mDirtyRegions) { - if (!GLES20Canvas.preserveBackBuffer()) { + if (sDirtyRegions) { + if (!(mDirtyRegionsEnabled = GLES20Canvas.preserveBackBuffer())) { Log.w(LOG_TAG, "Backbuffer cannot be preserved"); } - } else if (mDirtyRegionsRequested) { + } else if (sDirtyRegionsRequested) { // If mDirtyRegions is not set, our EGL configuration does not // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default // swap behavior might be EGL_BUFFER_PRESERVED, which means we // want to set mDirtyRegions. We try to do this only if dirty // regions were initially requested as part of the device // configuration (see RENDER_DIRTY_REGIONS) - mDirtyRegions = GLES20Canvas.isBackBufferPreserved(); + mDirtyRegionsEnabled = GLES20Canvas.isBackBufferPreserved(); } return sEglContext.getGL(); @@ -620,6 +638,19 @@ public abstract class HardwareRenderer { } @Override + void invalidate() { + // Cancels any existing buffer to ensure we'll get a buffer + // of the right size before we call eglSwapBuffers + sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + } + + @Override + boolean validate() { + return checkCurrent() != SURFACE_STATE_ERROR; + } + + @Override void setup(int width, int height) { mCanvas.setViewport(width, height); } @@ -645,7 +676,7 @@ public abstract class HardwareRenderer { attachInfo.mDrawingTime = SystemClock.uptimeMillis(); view.mPrivateFlags |= View.DRAWN; - + final int surfaceState = checkCurrent(); if (surfaceState != SURFACE_STATE_ERROR) { // We had to change the current surface and/or context, redraw everything @@ -706,10 +737,21 @@ public abstract class HardwareRenderer { } } } - + + /** + * Ensures the currnet EGL context is the one we expect. + * + * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, + * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or + * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one + */ private int checkCurrent() { - // TODO: Don't check the current context when we have one per UI thread - // TODO: Use a threadlocal flag to know whether the surface has changed + if (sEglThread != Thread.currentThread()) { + throw new IllegalStateException("Hardware acceleration can only be used with a " + + "single UI thread.\nOriginal thread: " + sEglThread + "\n" + + "Current thread: " + Thread.currentThread()); + } + if (!sEglContext.equals(sEgl.eglGetCurrentContext()) || !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1245898..74dc100 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4474,7 +4474,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit @RemotableViewMethod public void setLayoutDirection(int layoutDirection) { if (getLayoutDirection() != layoutDirection) { - resetLayoutDirectionResolution(); + resetResolvedLayoutDirection(); // Setting the flag will also request a layout. setFlags(layoutDirection, LAYOUT_DIRECTION_MASK); } @@ -9043,10 +9043,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH; } jumpDrawablesToCurrentState(); - resetLayoutDirectionResolution(); resolveLayoutDirectionIfNeeded(); resolvePadding(); - resetResolvedTextDirection(); resolveTextDirection(); if (isFocused()) { InputMethodManager imm = InputMethodManager.peekInstance(); @@ -9143,7 +9141,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * * @hide */ - protected void resetLayoutDirectionResolution() { + protected void resetResolvedLayoutDirection() { // Reset the current View resolution mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED; } @@ -9190,6 +9188,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } mCurrentAnimation = null; + + resetResolvedLayoutDirection(); + resetResolvedTextDirection(); } /** diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index d70c798..1dcbc26 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -901,6 +901,7 @@ public final class ViewAncestor extends Handler implements ViewParent, !mAttachInfo.mTurnOffWindowResizeAnim && mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled() && + mAttachInfo.mHardwareRenderer.validate() && lp != null && !PixelFormat.formatHasAlpha(lp.format)) { disposeResizeBuffer(); @@ -1315,6 +1316,9 @@ public final class ViewAncestor extends Handler implements ViewParent, mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled())) { mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight); + if (!hwInitialized) { + mAttachInfo.mHardwareRenderer.invalidate(); + } } if (!mStopped) { @@ -3845,10 +3849,6 @@ public final class ViewAncestor extends Handler implements ViewParent, } private static int checkCallingPermission(String permission) { - if (!Process.supportsProcesses()) { - return PackageManager.PERMISSION_GRANTED; - } - try { return ActivityManagerNative.getDefault().checkPermission( permission, Binder.getCallingPid(), Binder.getCallingUid()); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 41412de..752fd5a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -46,7 +46,6 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashSet; -import java.util.Locale; /** * <p> @@ -5000,15 +4999,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - protected void resetLayoutDirectionResolution() { - super.resetLayoutDirectionResolution(); + protected void resetResolvedLayoutDirection() { + super.resetResolvedLayoutDirection(); // Take care of resetting the children resolution too final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getLayoutDirection() == LAYOUT_DIRECTION_INHERIT) { - child.resetLayoutDirectionResolution(); + child.resetResolvedLayoutDirection(); } } } @@ -5019,7 +5018,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override protected void resolveTextDirection() { - int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + int resolvedTextDirection; switch(mTextDirection) { default: case TEXT_DIRECTION_INHERIT: diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java index 4c42cde..a1c6a53 100644 --- a/core/java/android/webkit/L10nUtils.java +++ b/core/java/android/webkit/L10nUtils.java @@ -74,7 +74,19 @@ public class L10nUtils { com.android.internal.R.string.autofill_country_code_re, // IDS_AUTOFILL_COUNTRY_CODE_RE com.android.internal.R.string.autofill_area_code_notext_re, // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE com.android.internal.R.string.autofill_phone_prefix_separator_re, // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE - com.android.internal.R.string.autofill_phone_suffix_separator_re // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE + com.android.internal.R.string.autofill_phone_suffix_separator_re, // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE + com.android.internal.R.string.autofill_province, // IDS_AUTOFILL_DIALOG_PROVINCE + com.android.internal.R.string.autofill_postal_code, // IDS_AUTOFILL_DIALOG_POSTAL_CODE + com.android.internal.R.string.autofill_state, // IDS_AUTOFILL_DIALOG_STATE + com.android.internal.R.string.autofill_zip_code, // IDS_AUTOFILL_DIALOG_ZIP_CODE + com.android.internal.R.string.autofill_county, // IDS_AUTOFILL_DIALOG_COUNTY + com.android.internal.R.string.autofill_island, // IDS_AUTOFILL_DIALOG_ISLAND + com.android.internal.R.string.autofill_district, // IDS_AUTOFILL_DIALOG_DISTRICT + com.android.internal.R.string.autofill_department, // IDS_AUTOFILL_DIALOG_DEPARTMENT + com.android.internal.R.string.autofill_prefecture, // IDS_AUTOFILL_DIALOG_PREFECTURE + com.android.internal.R.string.autofill_parish, // IDS_AUTOFILL_DIALOG_PARISH + com.android.internal.R.string.autofill_area, // IDS_AUTOFILL_DIALOG_AREA + com.android.internal.R.string.autofill_emirate // IDS_AUTOFILL_DIALOG_EMIRATE }; private static Context mApplicationContext; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 2145edd..c652e55 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -2240,6 +2240,10 @@ public final class WebViewCore { } private void setupViewport(boolean updateViewState) { + if (mWebView == null || mSettings == null) { + // We've been destroyed or are being destroyed, return early + return; + } // set the viewport settings from WebKit setViewportSettingsFromNative(); diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 252fc8f..49ea944 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -300,7 +300,7 @@ class ZoomManager { } public final float getDefaultScale() { - return mDefaultScale; + return mInitialScale > 0 ? mInitialScale : mDefaultScale; } public final float getReadingLevelScale() { @@ -344,6 +344,8 @@ class ZoomManager { public final void setInitialScaleInPercent(int scaleInPercent) { mInitialScale = scaleInPercent * 0.01f; + mActualScale = mInitialScale > 0 ? mInitialScale : mDefaultScale; + mInvActualScale = 1 / mActualScale; } public final float computeScaleWithLimits(float scale) { @@ -1087,6 +1089,7 @@ class ZoomManager { float scale; if (mInitialScale > 0) { scale = mInitialScale; + mTextWrapScale = scale; } else if (viewState.mViewScale > 0) { mTextWrapScale = viewState.mTextWrapScale; scale = viewState.mViewScale; @@ -1105,7 +1108,7 @@ class ZoomManager { } boolean reflowText = false; if (!viewState.mIsRestored) { - if (settings.getUseFixedViewport()) { + if (settings.getUseFixedViewport() && mInitialScale == 0) { // Override the scale only in case of fixed viewport. scale = Math.max(scale, overviewScale); mTextWrapScale = Math.max(mTextWrapScale, overviewScale); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 1e63e26..766b520 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5571,8 +5571,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - protected void resetLayoutDirectionResolution() { - super.resetLayoutDirectionResolution(); + protected void resetResolvedLayoutDirection() { + super.resetResolvedLayoutDirection(); if (mLayoutAlignment != null && (mTextAlign == TextAlign.VIEW_START || @@ -6212,6 +6212,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int scrollx, scrolly; + // Convert to left, center, or right alignment. + if (a == Layout.Alignment.ALIGN_NORMAL) { + a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_LEFT : + Layout.Alignment.ALIGN_RIGHT; + } else if (a == Layout.Alignment.ALIGN_OPPOSITE){ + a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_RIGHT : + Layout.Alignment.ALIGN_LEFT; + } + if (a == Layout.Alignment.ALIGN_CENTER) { /* * Keep centered if possible, or, if it is too wide to fit, @@ -6230,28 +6239,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener scrollx = left; } } - } else if (a == Layout.Alignment.ALIGN_NORMAL) { - /* - * Keep leading edge in view. - */ - - if (dir < 0) { - int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); - scrollx = right - hspace; - } else { - scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line)); - } - } else /* a == Layout.Alignment.ALIGN_OPPOSITE */ { - /* - * Keep trailing edge in view. - */ - - if (dir < 0) { - scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line)); - } else { - int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); - scrollx = right - hspace; - } + } else if (a == Layout.Alignment.ALIGN_LEFT) { + scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line)); + } else { // a == Layout.Alignment.ALIGN_RIGHT + int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); + scrollx = right - hspace; } if (ht < vspace) { @@ -6293,20 +6285,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int grav; switch (mLayout.getParagraphAlignment(line)) { - case ALIGN_NORMAL: + case ALIGN_LEFT: grav = 1; break; - - case ALIGN_OPPOSITE: + case ALIGN_RIGHT: grav = -1; break; - + case ALIGN_NORMAL: + grav = mLayout.getParagraphDirection(line); + break; + case ALIGN_OPPOSITE: + grav = -mLayout.getParagraphDirection(line); + break; + case ALIGN_CENTER: default: grav = 0; + break; } - grav *= mLayout.getParagraphDirection(line); - int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(); int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom(); diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java index 1ac191b..8c2c405 100755 --- a/core/java/com/android/internal/os/PkgUsageStats.java +++ b/core/java/com/android/internal/os/PkgUsageStats.java @@ -19,6 +19,9 @@ package com.android.internal.os; import android.os.Parcel; import android.os.Parcelable; +import java.util.HashMap; +import java.util.Map; + /** * implementation of PkgUsageStats associated with an * application package. @@ -28,6 +31,7 @@ public class PkgUsageStats implements Parcelable { public String packageName; public int launchCount; public long usageTime; + public Map<String, Long> componentResumeTimes; public static final Parcelable.Creator<PkgUsageStats> CREATOR = new Parcelable.Creator<PkgUsageStats>() { @@ -46,31 +50,45 @@ public class PkgUsageStats implements Parcelable { + " " + packageName + "}"; } - public PkgUsageStats(String pkgName, int count, long time) { + public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { packageName = pkgName; launchCount = count; usageTime = time; + componentResumeTimes = new HashMap<String, Long>(lastResumeTimes); } public PkgUsageStats(Parcel source) { packageName = source.readString(); launchCount = source.readInt(); usageTime = source.readLong(); + final int N = source.readInt(); + componentResumeTimes = new HashMap<String, Long>(N); + for (int i = 0; i < N; i++) { + String component = source.readString(); + long lastResumeTime = source.readLong(); + componentResumeTimes.put(component, lastResumeTime); + } } public PkgUsageStats(PkgUsageStats pStats) { packageName = pStats.packageName; launchCount = pStats.launchCount; usageTime = pStats.usageTime; + componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes); } public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int parcelableFlags){ + public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeString(packageName); dest.writeInt(launchCount); dest.writeLong(usageTime); + dest.writeInt(componentResumeTimes.size()); + for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { + dest.writeString(ent.getKey()); + dest.writeLong(ent.getValue()); + } } } diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index f13e770..53516c0 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -252,9 +252,10 @@ public class RuntimeInit { * <li> <code> [--] <start class name> <args> * </ul> * + * @param targetSdkVersion target SDK version * @param argv arg strings */ - public static final void zygoteInit(String[] argv) + public static final void zygoteInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); @@ -263,7 +264,7 @@ public class RuntimeInit { commonInit(); zygoteInitNative(); - applicationInit(argv); + applicationInit(targetSdkVersion, argv); } /** @@ -274,20 +275,22 @@ public class RuntimeInit { * which calls {@link WrapperInit#main} which then calls this method. * So we don't need to call commonInit() here. * + * @param targetSdkVersion target SDK version * @param argv arg strings */ - public static void wrapperInit(String[] argv) + public static void wrapperInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper"); - applicationInit(argv); + applicationInit(targetSdkVersion, argv); } - private static void applicationInit(String[] argv) + private static void applicationInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { // We want to be fairly aggressive about heap utilization, to avoid // holding on to a lot of memory that isn't needed. VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); + VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); final Arguments args; try { diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 860a08c..c6b3e7c 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -47,16 +47,22 @@ public class WrapperInit { * wrapper process instead of by forking Zygote. * * The first argument specifies the file descriptor for a pipe that should receive - * the pid of this process, or 0 if none. The remaining arguments are passed to - * the runtime. + * the pid of this process, or 0 if none. + * + * The second argument is the target SDK version for the app. + * + * The remaining arguments are passed to the runtime. * * @param args The command-line arguments. */ public static void main(String[] args) { try { + // Parse our mandatory arguments. + int fdNum = Integer.parseInt(args[0], 10); + int targetSdkVersion = Integer.parseInt(args[1], 10); + // Tell the Zygote what our actual PID is (since it only knows about the // wrapper that it directly forked). - int fdNum = Integer.parseInt(args[0], 10); if (fdNum != 0) { try { FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum); @@ -73,9 +79,9 @@ public class WrapperInit { ZygoteInit.preload(); // Launch the application. - String[] runtimeArgs = new String[args.length - 1]; - System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length); - RuntimeInit.wrapperInit(runtimeArgs); + String[] runtimeArgs = new String[args.length - 2]; + System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length); + RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs); } catch (ZygoteInit.MethodAndArgsCaller caller) { caller.run(); } @@ -87,11 +93,12 @@ public class WrapperInit { * * @param invokeWith The wrapper command. * @param niceName The nice name for the application, or null if none. + * @param targetSdkVersion The target SDK version for the app. * @param pipeFd The pipe to which the application's pid should be written, or null if none. * @param args Arguments for {@link RuntimeInit.main}. */ public static void execApplication(String invokeWith, String niceName, - FileDescriptor pipeFd, String[] args) { + int targetSdkVersion, FileDescriptor pipeFd, String[] args) { StringBuilder command = new StringBuilder(invokeWith); command.append(" /system/bin/app_process /system/bin --application"); if (niceName != null) { @@ -99,6 +106,8 @@ public class WrapperInit { } command.append(" com.android.internal.os.WrapperInit "); command.append(pipeFd != null ? pipeFd.getInt$() : 0); + command.append(' '); + command.append(targetSdkVersion); Zygote.appendQuotedShellArgs(command, args); Zygote.execShell(command.toString()); } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 7cb002c..9af7e96 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -18,6 +18,7 @@ package com.android.internal.os; import android.net.Credentials; import android.net.LocalSocket; +import android.os.Build; import android.os.Process; import android.os.SystemProperties; import android.util.Log; @@ -333,6 +334,10 @@ class ZygoteConnection { */ int debugFlags; + /** from --target-sdk-version. */ + int targetSdkVersion; + boolean targetSdkVersionSpecified; + /** from --classpath */ String classpath; @@ -402,6 +407,14 @@ class ZygoteConnection { gidSpecified = true; gid = Integer.parseInt( arg.substring(arg.indexOf('=') + 1)); + } else if (arg.startsWith("--target-sdk-version=")) { + if (targetSdkVersionSpecified) { + throw new IllegalArgumentException( + "Duplicate target-sdk-version specified"); + } + targetSdkVersionSpecified = true; + targetSdkVersion = Integer.parseInt( + arg.substring(arg.indexOf('=') + 1)); } else if (arg.equals("--enable-debugger")) { debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; } else if (arg.equals("--enable-safemode")) { @@ -821,9 +834,11 @@ class ZygoteConnection { if (parsedArgs.runtimeInit) { if (parsedArgs.invokeWith != null) { WrapperInit.execApplication(parsedArgs.invokeWith, - parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs); + parsedArgs.niceName, parsedArgs.targetSdkVersion, + pipeFd, parsedArgs.remainingArgs); } else { - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, + parsedArgs.remainingArgs); } } else { String className; @@ -885,6 +900,7 @@ class ZygoteConnection { } } + boolean usingWrapper = false; if (pipeFd != null && pid > 0) { DataInputStream is = new DataInputStream(new FileInputStream(pipeFd)); int innerPid = -1; @@ -909,6 +925,7 @@ class ZygoteConnection { if (parentPid > 0) { Log.i(TAG, "Wrapped process has pid " + innerPid); pid = innerPid; + usingWrapper = true; } else { Log.w(TAG, "Wrapped process reported a pid that is not a child of " + "the process that we forked: childPid=" + pid @@ -919,6 +936,7 @@ class ZygoteConnection { try { mSocketOutStream.writeInt(pid); + mSocketOutStream.writeBoolean(usingWrapper); } catch (IOException ex) { Log.e(TAG, "Error reading from command socket", ex); return true; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index b4a7e52..6ec186d 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -48,7 +48,7 @@ import java.util.ArrayList; * Startup class for the zygote process. * * Pre-initializes some classes, and then waits for commands on a UNIX domain - * socket. Based on these commands, forks of child processes that inherit + * socket. Based on these commands, forks off child processes that inherit * the initial state of the VM. * * Please see {@link ZygoteConnection.Arguments} for documentation on the @@ -453,12 +453,13 @@ public class ZygoteInit { if (parsedArgs.invokeWith != null) { WrapperInit.execApplication(parsedArgs.invokeWith, - parsedArgs.niceName, null, parsedArgs.remainingArgs); + parsedArgs.niceName, parsedArgs.targetSdkVersion, + null, parsedArgs.remainingArgs); } else { /* * Pass the remaining arguments to SystemServer. */ - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs); } /* should never reach here */ @@ -491,7 +492,9 @@ public class ZygoteInit { /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, - parsedArgs.gids, parsedArgs.debugFlags, null, + parsedArgs.gids, + parsedArgs.debugFlags, + null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index 2e7ec58..b754d94 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -26,6 +26,9 @@ package com.android.internal.util; * codes with Message.what starting at Protocol.WIFI + 1 and less than or equal to Protocol.WIFI + * Protocol.MAX_MESSAGE * + * NOTE: After a value is created and source released a value shouldn't be changed to + * maintain backwards compatibility. + * * {@hide} */ public class Protocol { @@ -40,7 +43,7 @@ public class Protocol { public static final int BASE_DHCP = 0x00030000; public static final int BASE_DATA_CONNECTION = 0x00040000; public static final int BASE_DATA_CONNECTION_AC = 0x00041000; - public static final int BASE_DATA_CONNECTION_TRACKER = 0x00050000; + public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000; //TODO: define all used protocols } diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index b86eb13..9c06d69 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -20,7 +20,8 @@ import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView; import android.content.Context; import android.content.res.Resources; -import android.util.Log; +import android.os.Parcel; +import android.os.Parcelable; import android.util.SparseBooleanArray; import android.view.MenuItem; import android.view.SoundEffectConstants; @@ -60,6 +61,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter { private OpenOverflowRunnable mPostedOpenRunnable; + final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback(); + int mOpenSubMenuId; + public ActionMenuPresenter() { super(com.android.internal.R.layout.action_menu_layout, com.android.internal.R.layout.action_menu_item_layout); @@ -196,8 +200,12 @@ public class ActionMenuPresenter extends BaseMenuPresenter { topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu(); } View anchor = findViewForItem(topSubMenu.getItem()); - if (anchor == null) return false; + if (anchor == null) { + if (mOverflowButton == null) return false; + anchor = mOverflowButton; + } + mOpenSubMenuId = subMenu.getItem().getItemId(); mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu); mActionButtonPopup.setAnchorView(anchor); mActionButtonPopup.show(); @@ -426,6 +434,57 @@ public class ActionMenuPresenter extends BaseMenuPresenter { super.onCloseMenu(menu, allMenusAreClosing); } + @Override + public Parcelable onSaveInstanceState() { + SavedState state = new SavedState(); + state.openSubMenuId = mOpenSubMenuId; + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState saved = (SavedState) state; + if (saved.openSubMenuId > 0) { + MenuItem item = mMenu.findItem(saved.openSubMenuId); + if (item != null) { + SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); + onSubMenuSelected(subMenu); + } + } + } + + private static class SavedState implements Parcelable { + public int openSubMenuId; + + SavedState() { + } + + SavedState(Parcel in) { + openSubMenuId = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(openSubMenuId); + } + + public static final Parcelable.Creator<SavedState> CREATOR + = new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + private class OverflowMenuButton extends ImageButton implements ActionMenuChildView { public OverflowMenuButton(Context context) { super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle); @@ -460,6 +519,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter { public OverflowPopup(Context context, MenuBuilder menu, View anchorView, boolean overflowOnly) { super(context, menu, anchorView, overflowOnly); + setCallback(mPopupPresenterCallback); } @Override @@ -482,6 +542,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter { // Give a reasonable anchor to nested submenus. setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton); } + + setCallback(mPopupPresenterCallback); } @Override @@ -489,6 +551,20 @@ public class ActionMenuPresenter extends BaseMenuPresenter { super.onDismiss(); mSubMenu.close(); mActionButtonPopup = null; + mOpenSubMenuId = 0; + } + } + + private class PopupPresenterCallback implements MenuPresenter.Callback { + + @Override + public boolean onOpenSubMenu(MenuBuilder subMenu) { + mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId(); + return false; + } + + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { } } diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java index ddbb08c..ed9d34a 100644 --- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java @@ -39,6 +39,8 @@ public abstract class BaseMenuPresenter implements MenuPresenter { protected MenuView mMenuView; + private int mId; + /** * Construct a new BaseMenuPresenter. * @@ -200,4 +202,12 @@ public abstract class BaseMenuPresenter implements MenuPresenter { public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { return false; } + + public int getId() { + return mId; + } + + public void setId(int id) { + mId = id; + } } diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java index f717904..56128d4 100644 --- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import android.util.SparseArray; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -35,7 +36,12 @@ public class IconMenuPresenter extends BaseMenuPresenter { private IconMenuItemView mMoreView; private int mMaxItems = -1; + int mOpenSubMenuId; + SubMenuPresenterCallback mSubMenuPresenterCallback = new SubMenuPresenterCallback(); + MenuDialogHelper mOpenSubMenu; + private static final String VIEWS_TAG = "android:menu:icon"; + private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu"; public IconMenuPresenter() { super(com.android.internal.R.layout.icon_menu_layout, @@ -86,7 +92,11 @@ public class IconMenuPresenter extends BaseMenuPresenter { if (!subMenu.hasVisibleItems()) return false; // The window manager will give us a token. - new MenuDialogHelper(subMenu).show(null); + MenuDialogHelper helper = new MenuDialogHelper(subMenu); + helper.setPresenterCallback(mSubMenuPresenterCallback); + helper.show(null); + mOpenSubMenu = helper; + mOpenSubMenuId = subMenu.getItem().getItemId(); super.onSubMenuSelected(subMenu); return true; } @@ -137,5 +147,47 @@ public class IconMenuPresenter extends BaseMenuPresenter { if (viewStates != null) { ((View) mMenuView).restoreHierarchyState(viewStates); } + int subMenuId = inState.getInt(OPEN_SUBMENU_KEY, 0); + if (subMenuId > 0 && mMenu != null) { + MenuItem item = mMenu.findItem(subMenuId); + if (item != null) { + onSubMenuSelected((SubMenuBuilder) item.getSubMenu()); + } + } + } + + @Override + public Parcelable onSaveInstanceState() { + if (mMenuView == null) { + return null; + } + + Bundle state = new Bundle(); + saveHierarchyState(state); + if (mOpenSubMenuId > 0) { + state.putInt(OPEN_SUBMENU_KEY, mOpenSubMenuId); + } + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + restoreHierarchyState((Bundle) state); + } + + class SubMenuPresenterCallback implements MenuPresenter.Callback { + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { + mOpenSubMenuId = 0; + mOpenSubMenu.dismiss(); + mOpenSubMenu = null; + } + + @Override + public boolean onOpenSubMenu(MenuBuilder subMenu) { + mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId(); + return false; + } + } } diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java index 27e4191..146c7ac 100644 --- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java @@ -47,6 +47,8 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick private Callback mCallback; private MenuAdapter mAdapter; + private int mId; + public static final String VIEWS_TAG = "android:menu:list"; /** @@ -182,6 +184,31 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick } } + public void setId(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + + @Override + public Parcelable onSaveInstanceState() { + if (mMenuView == null) { + return null; + } + + Bundle state = new Bundle(); + saveHierarchyState(state); + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + restoreHierarchyState((Bundle) state); + } + private class MenuAdapter extends BaseAdapter { public int getCount() { ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index fdfa954..a4edbc5 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -25,8 +25,8 @@ import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.os.Parcelable; -import android.util.Log; import android.util.SparseArray; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyCharacterMap; @@ -38,7 +38,6 @@ import android.view.View; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -49,6 +48,8 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MenuBuilder implements Menu { private static final String LOGTAG = "MenuBuilder"; + private static final String PRESENTER_KEY = "android:menu:presenters"; + private static final int[] sCategoryToOrder = new int[] { 1, /* No category */ 4, /* CONTAINER */ @@ -254,6 +255,58 @@ public class MenuBuilder implements Menu { return result; } + private void dispatchSaveInstanceState(Bundle outState) { + if (mPresenters.isEmpty()) return; + + SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>(); + + for (WeakReference<MenuPresenter> ref : mPresenters) { + final MenuPresenter presenter = ref.get(); + if (presenter == null) { + mPresenters.remove(ref); + } else { + final int id = presenter.getId(); + if (id > 0) { + final Parcelable state = presenter.onSaveInstanceState(); + if (state != null) { + presenterStates.put(id, state); + } + } + } + } + + outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates); + } + + private void dispatchRestoreInstanceState(Bundle state) { + SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY); + + if (presenterStates == null || mPresenters.isEmpty()) return; + + for (WeakReference<MenuPresenter> ref : mPresenters) { + final MenuPresenter presenter = ref.get(); + if (presenter == null) { + mPresenters.remove(ref); + } else { + final int id = presenter.getId(); + if (id > 0) { + Parcelable parcel = presenterStates.get(id); + if (parcel != null) { + presenter.onRestoreInstanceState(parcel); + } + } + } + } + } + + public void savePresenterStates(Bundle outState) { + dispatchSaveInstanceState(outState); + } + + public void restorePresenterStates(Bundle state) { + dispatchRestoreInstanceState(state); + } + public void setCallback(Callback cb) { mCallback = cb; } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index cffbb4e..4ecc828 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -18,6 +18,7 @@ package com.android.internal.view.menu; import android.content.Context; import android.content.res.Resources; +import android.os.Parcelable; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -296,4 +297,18 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On return convertView; } } + + @Override + public int getId() { + return 0; + } + + @Override + public Parcelable onSaveInstanceState() { + return null; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + } } diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java index bd66448..d913a39 100644 --- a/core/java/com/android/internal/view/menu/MenuPresenter.java +++ b/core/java/com/android/internal/view/menu/MenuPresenter.java @@ -17,6 +17,7 @@ package com.android.internal.view.menu; import android.content.Context; +import android.os.Parcelable; import android.view.Menu; import android.view.ViewGroup; @@ -125,4 +126,24 @@ public interface MenuPresenter { * @return true if this presenter collapsed the action view, false otherwise. */ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item); + + /** + * Returns an ID for determining how to save/restore instance state. + * @return a valid ID value. + */ + public int getId(); + + /** + * Returns a Parcelable describing the current state of the presenter. + * It will be passed to the {@link #onRestoreInstanceState(Parcelable)} + * method of the presenter sharing the same ID later. + * @return The saved instance state + */ + public Parcelable onSaveInstanceState(); + + /** + * Supplies the previously saved instance state to be restored. + * @param state The previously saved instance state + */ + public void onRestoreInstanceState(Parcelable state); } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 09bc1fc..595753a 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -337,6 +337,7 @@ public class ActionBarView extends AbsActionBarView { if (mActionMenuPresenter == null) { mActionMenuPresenter = new ActionMenuPresenter(); mActionMenuPresenter.setCallback(cb); + mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter); mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); } @@ -1301,5 +1302,19 @@ public class ActionBarView extends AbsActionBarView { item.setActionViewExpanded(false); return true; } + + @Override + public int getId() { + return 0; + } + + @Override + public Parcelable onSaveInstanceState() { + return null; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + } } } diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index ddae505..d9bd50e 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -26,7 +26,7 @@ extern "C" { int ifc_enable(const char *ifname); int ifc_disable(const char *ifname); -int ifc_reset_connections(const char *ifname); +int ifc_reset_connections(const char *ifname, int reset_mask); int dhcp_do_request(const char *ifname, const char *ipaddr, @@ -90,12 +90,17 @@ static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstri return (jint)result; } -static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname) +static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, + jstring ifname, jint mask) { int result; const char *nameStr = env->GetStringUTFChars(ifname, NULL); - result = ::ifc_reset_connections(nameStr); + + LOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n", + env, clazz, nameStr, mask); + + result = ::ifc_reset_connections(nameStr, mask); env->ReleaseStringUTFChars(ifname, nameStr); return (jint)result; } @@ -206,7 +211,7 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface }, { "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface }, - { "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections }, + { "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections }, { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcp }, { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcpRenew }, { "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp }, diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 1166ae4..86e7cc0 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -1396,7 +1396,6 @@ static jstring registerSinkHealthApplicationNative(JNIEnv *env, jobject object, LOG_AND_FREE_DBUS_ERROR(&err); } } else { - LOGE("--_Call made getting the patch..."); if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &c_path, DBUS_TYPE_INVALID)) { @@ -1405,7 +1404,6 @@ static jstring registerSinkHealthApplicationNative(JNIEnv *env, jobject object, } } else { path = env->NewStringUTF(c_path); - LOGE("----Path is %s", c_path); } dbus_message_unref(reply); } @@ -1459,7 +1457,6 @@ static jboolean createChannelNative(JNIEnv *env, jobject object, const char *c_device_path = env->GetStringUTFChars(devicePath, NULL); const char *c_app_path = env->GetStringUTFChars(appPath, NULL); const char *c_config = env->GetStringUTFChars(config, NULL); - LOGE("Params...%s, %s, %s \n", c_device_path, c_app_path, c_config); DBusMessage *reply = dbus_func_args(env, nat->conn, c_device_path, @@ -1531,7 +1528,6 @@ static jstring getMainChannelNative(JNIEnv *env, jobject object, jstring deviceP DBusError err; dbus_error_init(&err); - LOGE("---Args %s", c_device_path); DBusMessage *reply = dbus_func_args(env, nat->conn, c_device_path, DBUS_HEALTH_DEVICE_IFACE, "GetProperties", @@ -1566,8 +1562,6 @@ static jstring getChannelApplicationNative(JNIEnv *env, jobject object, jstring DBusError err; dbus_error_init(&err); - LOGE("---Args %s", c_channel_path); - DBusMessage *reply = dbus_func_args(env, nat->conn, c_channel_path, DBUS_HEALTH_CHANNEL_IFACE, "GetProperties", @@ -1596,7 +1590,6 @@ static jstring getChannelApplicationNative(JNIEnv *env, jobject object, jstring if (!strcmp(c_name, "Application")) { path = (jstring) env->GetObjectArrayElement(str_array, i+1); - LOGE("----Path is %s", env->GetStringUTFChars(path, NULL)); env->ReleaseStringUTFChars(name, c_name); return path; } @@ -1655,13 +1648,11 @@ static jobject getChannelFdNative(JNIEnv *env, jobject object, jstring channelPa fd = dbus_returns_unixfd(env, reply); if (fd == -1) return NULL; - LOGE("---got fd %d\n", fd); // Create FileDescriptor object jobject fileDesc = jniCreateFileDescriptor(env, fd); if (fileDesc == NULL) { // FileDescriptor constructor has thrown an exception releaseChannelFdNative(env, object, channelPath); - LOGE("---File Desc is null"); return NULL; } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 0960b25..d1ba2d1 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -318,17 +318,15 @@ jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz, jint pid, jint adj) { #ifdef HAVE_OOM_ADJ - if (ProcessState::self()->supportsProcesses()) { - char text[64]; - sprintf(text, "/proc/%d/oom_adj", pid); - int fd = open(text, O_WRONLY); - if (fd >= 0) { - sprintf(text, "%d", adj); - write(fd, text, strlen(text)); - close(fd); - } - return true; + char text[64]; + sprintf(text, "/proc/%d/oom_adj", pid); + int fd = open(text, O_WRONLY); + if (fd >= 0) { + sprintf(text, "%d", adj); + write(fd, text, strlen(text)); + close(fd); } + return true; #endif return false; } @@ -370,11 +368,6 @@ jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid) #endif } -jboolean android_os_Process_supportsProcesses(JNIEnv* env, jobject clazz) -{ - return ProcessState::self()->supportsProcesses(); -} - static int pid_compare(const void* v1, const void* v2) { //LOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2)); @@ -878,7 +871,6 @@ static const JNINativeMethod methods[] = { {"setGid", "(I)I", (void*)android_os_Process_setGid}, {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, - {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses}, {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png Binary files differindex 5225a81..acbbb38 100644 --- a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png Binary files differindex 2e7e973..6009528 100644 --- a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png Binary files differindex 4591627..30727d7 100644 --- a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png Binary files differindex 9cf1826..7cea5e1 100644 --- a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png Binary files differindex a47ef40..ba0d612 100644 --- a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png Binary files differindex 9b50c73..e8646b9 100644 --- a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png Binary files differindex a0d36de..14cb4c9 100644 --- a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png Binary files differindex 805b956..80fd218 100644 --- a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png +++ b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/ic_btn_back.png b/core/res/res/drawable-hdpi/ic_btn_back.png Binary files differdeleted file mode 100644 index f8b3285..0000000 --- a/core/res/res/drawable-hdpi/ic_btn_back.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ic_btn_next.png b/core/res/res/drawable-hdpi/ic_btn_next.png Binary files differdeleted file mode 100644 index b2c6e1b..0000000 --- a/core/res/res/drawable-hdpi/ic_btn_next.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png Binary files differindex b0fba52..1014d8a 100644 --- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png Binary files differindex 3ef8935..18cd171 100644 --- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png +++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png Binary files differindex 9769bbb..ad2cb5a 100644 --- a/core/res/res/drawable-hdpi/toast_frame.9.png +++ b/core/res/res/drawable-hdpi/toast_frame.9.png diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png Binary files differindex 9769bbb..ad2cb5a 100644 --- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-hdpi/toast_frame_holo.9.png diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png Binary files differindex a0bd4e3..4836da1 100644 --- a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png Binary files differindex 12abcd2..c299931 100644 --- a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png Binary files differindex adb8104..86edad7 100644 --- a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png Binary files differindex d7c6bbf..53ee68b 100644 --- a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png Binary files differindex 42cfc52..606adaf 100644 --- a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png Binary files differindex 9a08e15..14d2e5e 100644 --- a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png Binary files differindex 5d86b2a..2646332 100644 --- a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png Binary files differindex ad22f5b..48ec0a4 100644 --- a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png +++ b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/ic_btn_back.png b/core/res/res/drawable-mdpi/ic_btn_back.png Binary files differdeleted file mode 100644 index c9bff4c..0000000 --- a/core/res/res/drawable-mdpi/ic_btn_back.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_btn_next.png b/core/res/res/drawable-mdpi/ic_btn_next.png Binary files differdeleted file mode 100755 index c6cf436..0000000 --- a/core/res/res/drawable-mdpi/ic_btn_next.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png Binary files differindex 923f92d..dd5dd39 100644 --- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png Binary files differindex afada39..12d65be 100644 --- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png +++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png Binary files differindex 06cfc70..b9105de 100755 --- a/core/res/res/drawable-mdpi/toast_frame.9.png +++ b/core/res/res/drawable-mdpi/toast_frame.9.png diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png Binary files differindex 06cfc70..b9105de 100755 --- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-mdpi/toast_frame_holo.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png Binary files differindex b33e117..077e4d3 100644 --- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png Binary files differindex d84d427..357c17f 100644 --- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png Binary files differindex 9c0ff47..5b51072 100644 --- a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png Binary files differindex 331a4f2..2705a39 100644 --- a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png Binary files differindex cdc887d..101876f 100644 --- a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png Binary files differindex bdb6824..0df1503 100644 --- a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png Binary files differindex 600efb3..344a4e2 100644 --- a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png Binary files differindex aa8401d..249848f 100644 --- a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png Binary files differnew file mode 100644 index 0000000..92acc47 --- /dev/null +++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png Binary files differnew file mode 100644 index 0000000..4e54b4b6 --- /dev/null +++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/toast_frame.9.png b/core/res/res/drawable-xhdpi/toast_frame.9.png Binary files differindex f7debee..9f39a77 100644 --- a/core/res/res/drawable-xhdpi/toast_frame.9.png +++ b/core/res/res/drawable-xhdpi/toast_frame.9.png diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png Binary files differindex f7debee..9f39a77 100644 --- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png diff --git a/core/res/res/layout-sw600dp/preference_list_content.xml b/core/res/res/layout-sw600dp/preference_list_content.xml index 5a345c6..a5320a7 100644 --- a/core/res/res/layout-sw600dp/preference_list_content.xml +++ b/core/res/res/layout-sw600dp/preference_list_content.xml @@ -87,7 +87,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -95,8 +94,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -117,8 +114,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout-w600dp/preference_list_content_single.xml b/core/res/res/layout-w600dp/preference_list_content_single.xml index 6725996..bbad296 100644 --- a/core/res/res/layout-w600dp/preference_list_content_single.xml +++ b/core/res/res/layout-w600dp/preference_list_content_single.xml @@ -61,7 +61,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -69,8 +68,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -91,8 +88,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml index ad485c1..5093a5e 100644 --- a/core/res/res/layout-xlarge/activity_list.xml +++ b/core/res/res/layout-xlarge/activity_list.xml @@ -54,21 +54,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="4dip" - android:layout_marginLeft="16dip" - android:layout_marginRight="16dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:layout_height="2dip" + android:background="@android:color/holo_blue_light" /> <!-- If the client uses a customTitle, it will be added here. --> </LinearLayout> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_marginLeft="32dip" - android:layout_marginRight="32dip" android:layout_height="0dip" android:layout_weight="1"> @@ -95,15 +89,12 @@ android:minHeight="54dip" android:orientation="vertical" android:divider="?android:attr/dividerHorizontal" - android:showDividers="beginning" - android:dividerPadding="16dip"> + android:showDividers="beginning"> <LinearLayout style="?android:attr/buttonBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingLeft="2dip" - android:paddingRight="2dip" android:measureWithLargestChild="true"> <LinearLayout android:id="@+id/leftSpacer" android:layout_weight="0.25" diff --git a/core/res/res/layout/activity_chooser_list_footer.xml b/core/res/res/layout/activity_chooser_list_footer.xml index 7603a31..c05ba1a 100644 --- a/core/res/res/layout/activity_chooser_list_footer.xml +++ b/core/res/res/layout/activity_chooser_list_footer.xml @@ -23,13 +23,11 @@ android:gravity="center" android:orientation="vertical"> - <ImageView + <View android:id="@+id/divider" android:layout_width="match_parent" android:layout_height="2dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@drawable/divider_strong_holo" /> + android:background="@android:color/holo_blue_light" /> <TextView android:id="@+id/title" diff --git a/core/res/res/layout/activity_chooser_list_header.xml b/core/res/res/layout/activity_chooser_list_header.xml index 867014b..0fb256f 100644 --- a/core/res/res/layout/activity_chooser_list_header.xml +++ b/core/res/res/layout/activity_chooser_list_header.xml @@ -32,12 +32,10 @@ android:duplicateParentState="true" android:singleLine="true" /> - <ImageView + <View android:id="@+id/divider" android:layout_width="match_parent" android:layout_height="2dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" android:src="@drawable/divider_strong_holo" /> </LinearLayout> diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml index 2185467..2b686bd 100644 --- a/core/res/res/layout/alert_dialog_holo.xml +++ b/core/res/res/layout/alert_dialog_holo.xml @@ -30,13 +30,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> - <ImageView android:id="@+id/titleDividerTop" + <View android:id="@+id/titleDividerTop" android:layout_width="match_parent" - android:layout_height="1dip" + android:layout_height="2dip" android:visibility="gone" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:background="@android:color/holo_blue_light" /> <LinearLayout android:id="@+id/title_template" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -51,20 +49,17 @@ android:paddingRight="8dip" android:src="@null" /> <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle" - style="?android:attr/textAppearanceLarge" - android:textColor="@android:color/holo_blue" + style="?android:attr/windowTitleStyle" android:singleLine="true" android:ellipsize="end" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="1dip" + android:layout_height="2dip" android:visibility="gone" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:background="@android:color/holo_blue_light" /> <!-- If the client uses a customTitle, it will be added here. --> </LinearLayout> diff --git a/core/res/res/layout/dialog_custom_title_holo.xml b/core/res/res/layout/dialog_custom_title_holo.xml index 5261553..e2335a7 100644 --- a/core/res/res/layout/dialog_custom_title_holo.xml +++ b/core/res/res/layout/dialog_custom_title_holo.xml @@ -28,12 +28,10 @@ This is an custom layout for a dialog. android:gravity="center_vertical|left" style="?android:attr/windowTitleBackgroundStyle"> </FrameLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="1dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:layout_height="2dip" + android:background="@android:color/holo_blue_light" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/core/res/res/layout/dialog_title_holo.xml b/core/res/res/layout/dialog_title_holo.xml index 400ef60..50bb0ba 100644 --- a/core/res/res/layout/dialog_title_holo.xml +++ b/core/res/res/layout/dialog_title_holo.xml @@ -30,12 +30,10 @@ enabled. android:paddingLeft="16dip" android:paddingRight="16dip" android:gravity="center_vertical|left" /> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" - android:layout_height="1dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:layout_height="2dip" + android:background="@android:color/holo_blue_light" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/core/res/res/layout/dialog_title_icons_holo.xml b/core/res/res/layout/dialog_title_icons_holo.xml index f780ab0..7d7959f 100644 --- a/core/res/res/layout/dialog_title_icons_holo.xml +++ b/core/res/res/layout/dialog_title_icons_holo.xml @@ -48,12 +48,10 @@ enabled. android:layout_marginLeft="8dip" /> </LinearLayout> - <ImageView android:id="@+id/titleDivider" + <View android:id="@+id/titleDivider" android:layout_width="match_parent" android:layout_height="1dip" - android:scaleType="fitXY" - android:gravity="fill_horizontal" - android:src="@android:drawable/divider_strong_holo" /> + android:background="@android:color/holo_blue_light" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml index 82b3a4c..fb898ee 100644 --- a/core/res/res/layout/preference_list_content.xml +++ b/core/res/res/layout/preference_list_content.xml @@ -85,7 +85,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -93,8 +92,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -115,8 +112,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout/preference_list_content_single.xml b/core/res/res/layout/preference_list_content_single.xml index a015761..6902ffd 100644 --- a/core/res/res/layout/preference_list_content_single.xml +++ b/core/res/res/layout/preference_list_content_single.xml @@ -56,7 +56,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -64,8 +63,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -86,8 +83,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml index 986536e..315f708 100644 --- a/core/res/res/layout/preference_list_fragment.xml +++ b/core/res/res/layout/preference_list_fragment.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* +/* ** Copyright 2010, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,7 +41,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:background="@android:drawable/bottom_bar" android:visibility="gone"> <Button android:id="@+id/back_button" @@ -49,8 +48,6 @@ android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentLeft="true" - android:drawableLeft="@drawable/ic_btn_back" - android:drawablePadding="3dip" android:text="@string/back_button_label" /> <LinearLayout @@ -71,8 +68,6 @@ android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" - android:drawableRight="@drawable/ic_btn_next" - android:drawablePadding="3dip" android:text="@string/next_button_label" /> </LinearLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 39de054..9c2133f 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -40,6 +40,21 @@ to use accelerated drawing (thus setting state_accelerated), the cache hint is ignored and always assumed to be transparent. --> <attr name="colorBackgroundCacheHint" format="color" /> + + <!-- Default highlight color for items that are pressed. --> + <attr name="colorPressedHighlight" format="color" /> + <!-- Default highlight color for items that are long-pressed. --> + <attr name="colorLongPressedHighlight" format="color" /> + <!-- Default highlight color for items that are + focused. (Focused meaning cursor-based selection.) --> + <attr name="colorFocusedHighlight" format="color" /> + <!-- Default highlight color for items that are + activated. (Activated meaning persistent selection.) --> + <attr name="colorActivatedHighlight" format="color" /> + <!-- Default highlight color for items in multiple selection + mode. --> + <attr name="colorMultiSelectHighlight" format="color" /> + <!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. --> <attr name="disabledAlpha" format="float" /> <!-- Default background dim amount when a menu, dialog, or something similar pops up. --> @@ -5303,6 +5318,8 @@ <attr name="mtpReserve" format="integer" /> <!-- true if the storage can be shared via USB mass storage --> <attr name="allowMassStorage" format="boolean" /> + <!-- maximum file size for the volume in megabytes, zero or unspecified if it is unbounded --> + <attr name="maxFileSize" format="integer" /> </declare-styleable> <declare-styleable name="SwitchPreference"> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 2a1ebfc..631d8c6 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -143,19 +143,42 @@ <color name="link_text_holo_light">#0000ee</color> <!-- Group buttons --> + <eat-comment /> <color name="group_button_dialog_pressed_holo_dark">#46c5c1ff</color> <color name="group_button_dialog_focused_holo_dark">#2699cc00</color> <color name="group_button_dialog_pressed_holo_light">#ffffffff</color> <color name="group_button_dialog_focused_holo_light">#4699cc00</color> + <!-- Highlight colors for the legacy themes --> + <eat-comment /> + <color name="legacy_pressed_highlight">#fffeaa0c</color> + <color name="legacy_selected_highlight">#fff17a0a</color> + <color name="legacy_long_pressed_highlight">#ffffffff</color> + <!-- General purpose colors for Holo-themed elements --> <eat-comment /> - <!-- A Holo shade of blue --> - <color name="holo_blue">#ff6699ff</color> - <!-- A Holo shade of green --> - <color name="holo_green">#ff99cc00</color> + <!-- A light Holo shade of blue --> + <color name="holo_blue_light">#ff33b5e5</color> + <!-- A light Holo shade of green --> + <color name="holo_green_light">#ff99cc00</color> + <!-- A light Holo shade of red --> + <color name="holo_red_light">#ffff4444</color> + <!-- A dark Holo shade of blue --> + <color name="holo_blue_dark">#ff0099cc</color> + <!-- A dark Holo shade of green --> + <color name="holo_green_dark">#ff669900</color> + <!-- A dark Holo shade of red --> + <color name="holo_red_dark">#ffcc0000</color> + <!-- A Holo shade of purple --> + <color name="holo_purple">#ffaa66cc</color> + <!-- A light Holo shade of orange --> + <color name="holo_orange_light">#ffffbb33</color> + <!-- A dark Holo shade of orange --> + <color name="holo_orange_dark">#ffff8800</color> + <!-- A really bright Holo shade of blue --> + <color name="holo_blue_bright">#ff00ddff</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9f05cbc..1f2b7fb 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -660,4 +660,9 @@ extremely limited. --> <bool name="config_allowActionMenuItemTextWithIcon">false</bool> + <!-- Remote server that can provide NTP responses. --> + <string translatable="false" name="config_ntpServer">pool.ntp.org</string> + <!-- Timeout to wait for NTP server response. --> + <integer name="config_ntpTimeout">20000</integer> + </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index d05685c..547a192 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -74,4 +74,9 @@ <item type="id" name="rowTypeId" /> <item type="id" name="up" /> <item type="id" name="action_menu_divider" /> + <item type="id" name="icon_menu_presenter" /> + <item type="id" name="list_menu_presenter" /> + <item type="id" name="action_menu_presenter" /> + <item type="id" name="overflow_menu_presenter" /> + <item type="id" name="popup_submenu_presenter" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 3c23add..f7701f2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1781,6 +1781,12 @@ <public type="attr" name="textAllCaps" /> + <public type="attr" name="colorPressedHighlight" /> + <public type="attr" name="colorLongPressedHighlight" /> + <public type="attr" name="colorFocusedHighlight" /> + <public type="attr" name="colorActivatedHighlight" /> + <public type="attr" name="colorMultiSelectHighlight" /> + <public type="style" name="TextAppearance.SuggestionHighlight" /> <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" /> <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" /> @@ -1809,4 +1815,15 @@ <public type="integer" name="status_bar_notification_info_maxnum" /> <public type="string" name="status_bar_notification_info_overflow" /> + <public type="color" name="holo_blue_light" /> + <public type="color" name="holo_blue_dark" /> + <public type="color" name="holo_green_light" /> + <public type="color" name="holo_green_dark" /> + <public type="color" name="holo_red_light" /> + <public type="color" name="holo_red_dark" /> + <public type="color" name="holo_orange_light" /> + <public type="color" name="holo_orange_dark" /> + <public type="color" name="holo_purple" /> + <public type="color" name="holo_blue_bright" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 50f8df7..70c204e 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2038,13 +2038,13 @@ <string name="autofill_phone_re">phone<!-- de-DE -->|telefonnummer<!-- es -->|telefono|teléfono<!-- fr-FR -->|telfixe<!-- ja-JP -->|電話<!-- pt-BR, pt-PT -->|telefone|telemovel<!-- ru -->|телефон<!-- zh-CN -->|电话</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_area_code_re">area code</string> + <string name="autofill_area_code_re">area.*code|acode|area</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_phone_prefix_re">^-$|\\)$|prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string> + <string name="autofill_phone_prefix_re">prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_phone_suffix_re">^-$|suffix</string> + <string name="autofill_phone_suffix_re">suffix</string> <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_phone_extension_re">ext<!-- pt-BR, pt-PT -->|ramal</string> @@ -2077,14 +2077,50 @@ <string name="autofill_country_code_re">country.*code|ccode|_cc</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_area_code_notext_re">^\($</string> + <string name="autofill_area_code_notext_re">^\\($</string> <!-- Do not translate. Regex used by AutoFill. --> - <string name="autofill_phone_prefix_separator_re">^-$|^\)$</string> + <string name="autofill_phone_prefix_separator_re">^-$|^\\)$</string> <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_phone_suffix_separator_re">^-$</string> + <!-- Label in a web form for "Province" [CHAR-LIMIT=NONE] --> + <string name="autofill_province">Province</string> + + <!-- Label in a web form for "Postal code" [CHAR-LIMIT=NONE] --> + <string name="autofill_postal_code">Postal code</string> + + <!-- Label in a web form for "State" [CHAR-LIMIT=NONE] --> + <string name="autofill_state">State</string> + + <!-- Label in a web form for "ZIP code" [CHAR-LIMIT=NONE] --> + <string name="autofill_zip_code">ZIP code</string> + + <!-- Label in a web form for "County" [CHAR-LIMIT=NONE] --> + <string name="autofill_county">County</string> + + <!-- Label in a web form for "Island" [CHAR-LIMIT=NONE] --> + <string name="autofill_island">Island</string> + + <!-- Label in a web form for "District" [CHAR-LIMIT=NONE] --> + <string name="autofill_district">District</string> + + <!-- Label in a web form for "Department" [CHAR-LIMIT=NONE] --> + <string name="autofill_department">Department</string> + + <!-- Label in a web form for "Prefecture" [CHAR-LIMIT=NONE] --> + <string name="autofill_prefecture">Prefecture</string> + + <!-- Label in a web form for "Parish" [CHAR-LIMIT=NONE] --> + <string name="autofill_parish">Parish</string> + + <!-- Label in a web form for "Area" [CHAR-LIMIT=NONE] --> + <string name="autofill_area">Area</string> + + <!-- Label in a web form for "Emirate" [CHAR-LIMIT=NONE] --> + <string name="autofill_emirate">Emirate</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 8a8f81a..a5cd6e3 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1356,7 +1356,7 @@ <style name="TextAppearance.Holo.DialogWindowTitle"> <item name="android:textSize">22sp</item> - <item name="android:textColor">@android:color/holo_blue</item> + <item name="android:textColor">@android:color/holo_blue_light</item> </style> <style name="TextAppearance.Holo.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView"> @@ -1455,7 +1455,7 @@ <style name="TextAppearance.Holo.Light.DialogWindowTitle"> <item name="android:textSize">22sp</item> - <item name="android:textColor">@android:color/holo_blue</item> + <item name="android:textColor">@android:color/holo_blue_light</item> </style> <style name="TextAppearance.Holo.Light.CalendarViewWeekDayView" parent="TextAppearance.Small.CalendarViewWeekDayView"> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 9a52cff..90f3602 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -31,6 +31,13 @@ <item name="colorForegroundInverse">@android:color/bright_foreground_dark_inverse</item> <item name="colorBackground">@android:color/background_dark</item> <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item> + + <item name="colorPressedHighlight">@color/legacy_pressed_highlight</item> + <item name="colorLongPressedHighlight">@color/legacy_long_pressed_highlight</item> + <item name="colorFocusedHighlight">@color/legacy_selected_highlight</item> + <item name="colorMultiSelectHighlight">@color/legacy_selected_highlight</item> + <item name="colorActivatedHighlight">@color/legacy_selected_highlight</item> + <item name="disabledAlpha">0.5</item> <item name="backgroundDimAmount">0.6</item> @@ -799,6 +806,12 @@ <item name="disabledAlpha">0.5</item> <item name="backgroundDimAmount">0.6</item> + <item name="colorPressedHighlight">@color/holo_blue_light</item> + <item name="colorLongPressedHighlight">@color/holo_blue_bright</item> + <item name="colorFocusedHighlight">@color/holo_blue_dark</item> + <item name="colorMultiSelectHighlight">@color/holo_green_light</item> + <item name="colorActivatedHighlight">@color/holo_blue_dark</item> + <!-- Text styles --> <item name="textAppearance">@android:style/TextAppearance.Holo</item> <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Inverse</item> @@ -1090,6 +1103,11 @@ <item name="disabledAlpha">0.5</item> <item name="backgroundDimAmount">0.6</item> + <item name="colorPressedHighlight">@color/holo_blue_light</item> + <item name="colorLongPressedHighlight">@color/holo_blue_bright</item> + <item name="colorFocusedHighlight">@color/holo_blue_dark</item> + <item name="colorMultiSelectHighlight">@color/holo_green_light</item> + <item name="colorActivatedHighlight">@color/holo_blue_dark</item> <!-- Text styles --> <item name="textAppearance">@android:style/TextAppearance.Holo.Light</item> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 059503c..146466f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1051,6 +1051,13 @@ </intent-filter> </activity> + <activity android:name="android.widget.TextViewTestActivity" android:label="TextViewTestActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + <!-- Activity-level metadata --> diff --git a/core/tests/coretests/res/layout/textview_test.xml b/core/tests/coretests/res/layout/textview_test.xml new file mode 100644 index 0000000..f0c7b9e --- /dev/null +++ b/core/tests/coretests/res/layout/textview_test.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/textviewtest_layout" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView android:id="@+id/textviewtest_textview" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/textview_hebrew_text"/> + +</LinearLayout>
\ No newline at end of file diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml index f51b08e..71f3520 100644 --- a/core/tests/coretests/res/values/strings.xml +++ b/core/tests/coretests/res/values/strings.xml @@ -129,4 +129,6 @@ <string name="button8">Button8</string> <string name="button9">Button9</string> + <string name="textview_hebrew_text">םמab?!</string> + </resources> diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java index 3cb64c7..82345e2 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -44,20 +44,20 @@ public class NetworkStatsTest extends TestCase { public void testAddEntryGrow() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 2); - assertEquals(0, stats.size); + assertEquals(0, stats.size()); assertEquals(2, stats.iface.length); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L); - assertEquals(2, stats.size); + assertEquals(2, stats.size()); assertEquals(2, stats.iface.length); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L); stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L); - assertEquals(5, stats.size); + assertEquals(5, stats.size()); assertTrue(stats.iface.length >= 5); assertEquals(1L, stats.rx[0]); diff --git a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java deleted file mode 100644 index 05000f1..0000000 --- a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Copyright (C) 2006 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.pim; - -import android.pim.EventRecurrence.InvalidFormatException; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; - -import junit.framework.TestCase; - -import java.util.Arrays; - -/** - * Test android.pim.EventRecurrence. - * - * adb shell am instrument -w -e class android.pim.EventRecurrenceTest \ - * com.android.frameworks.coretests/android.test.InstrumentationTestRunner - */ -public class EventRecurrenceTest extends TestCase { - - @SmallTest - public void test0() throws Exception { - verifyRecurType("FREQ=SECONDLY", - /* int freq */ EventRecurrence.SECONDLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test1() throws Exception { - verifyRecurType("FREQ=MINUTELY", - /* int freq */ EventRecurrence.MINUTELY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test2() throws Exception { - verifyRecurType("FREQ=HOURLY", - /* int freq */ EventRecurrence.HOURLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test3() throws Exception { - verifyRecurType("FREQ=DAILY", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test4() throws Exception { - verifyRecurType("FREQ=WEEKLY", - /* int freq */ EventRecurrence.WEEKLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test5() throws Exception { - verifyRecurType("FREQ=MONTHLY", - /* int freq */ EventRecurrence.MONTHLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test6() throws Exception { - verifyRecurType("FREQ=YEARLY", - /* int freq */ EventRecurrence.YEARLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test7() throws Exception { - // with an until - verifyRecurType("FREQ=DAILY;UNTIL=112233T223344Z", - /* int freq */ EventRecurrence.DAILY, - /* String until */ "112233T223344Z", - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test8() throws Exception { - // with a count - verifyRecurType("FREQ=DAILY;COUNT=334", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 334, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test9() throws Exception { - // with a count - verifyRecurType("FREQ=DAILY;INTERVAL=5000", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 5000, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @SmallTest - public void test10() throws Exception { - // verifyRecurType all of the BY* ones with one element - verifyRecurType("FREQ=DAILY" - + ";BYSECOND=0" - + ";BYMINUTE=1" - + ";BYHOUR=2" - + ";BYMONTHDAY=30" - + ";BYYEARDAY=300" - + ";BYWEEKNO=53" - + ";BYMONTH=12" - + ";BYSETPOS=-15" - + ";WKST=SU", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ new int[]{0}, - /* int[] byminute */ new int[]{1}, - /* int[] byhour */ new int[]{2}, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ new int[]{30}, - /* int[] byyearday */ new int[]{300}, - /* int[] byweekno */ new int[]{53}, - /* int[] bymonth */ new int[]{12}, - /* int[] bysetpos */ new int[]{-15}, - /* int wkst */ EventRecurrence.SU - ); - } - - @SmallTest - public void test11() throws Exception { - // verifyRecurType all of the BY* ones with one element - verifyRecurType("FREQ=DAILY" - + ";BYSECOND=0,30,59" - + ";BYMINUTE=0,41,59" - + ";BYHOUR=0,4,23" - + ";BYMONTHDAY=-31,-1,1,31" - + ";BYYEARDAY=-366,-1,1,366" - + ";BYWEEKNO=-53,-1,1,53" - + ";BYMONTH=1,12" - + ";BYSETPOS=1,2,3,4,500,10000" - + ";WKST=SU", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ new int[]{0, 30, 59}, - /* int[] byminute */ new int[]{0, 41, 59}, - /* int[] byhour */ new int[]{0, 4, 23}, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ new int[]{-31, -1, 1, 31}, - /* int[] byyearday */ new int[]{-366, -1, 1, 366}, - /* int[] byweekno */ new int[]{-53, -1, 1, 53}, - /* int[] bymonth */ new int[]{1, 12}, - /* int[] bysetpos */ new int[]{1, 2, 3, 4, 500, 10000}, - /* int wkst */ EventRecurrence.SU - ); - } - - private static class Check { - Check(String k, int... v) { - key = k; - values = v; - } - - String key; - int[] values; - } - - // this is a negative verifyRecurType case to verifyRecurType the range of the numbers accepted - @SmallTest - public void test12() throws Exception { - Check[] checks = new Check[]{ - new Check("BYSECOND", -100, -1, 60, 100), - new Check("BYMINUTE", -100, -1, 60, 100), - new Check("BYHOUR", -100, -1, 24, 100), - new Check("BYMONTHDAY", -100, -32, 0, 32, 100), - new Check("BYYEARDAY", -400, -367, 0, 367, 400), - new Check("BYWEEKNO", -100, -54, 0, 54, 100), - new Check("BYMONTH", -100, -5, 0, 13, 100) - }; - - for (Check ck : checks) { - for (int n : ck.values) { - String recur = "FREQ=DAILY;" + ck.key + "=" + n; - try { - EventRecurrence er = new EventRecurrence(); - er.parse(recur); - fail("Negative verifyRecurType failed. " - + " parse failed to throw an exception for '" - + recur + "'"); - } catch (EventRecurrence.InvalidFormatException e) { - // expected - } - } - } - } - - // verifyRecurType BYDAY - @SmallTest - public void test13() throws Exception { - verifyRecurType("FREQ=DAILY;BYDAY=1SU,-2MO,+33TU,WE,TH,FR,SA", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.SU, - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - EventRecurrence.TH, - EventRecurrence.FR, - EventRecurrence.SA - }, - /* int[] bydayNum */ new int[]{1, -2, 33, 0, 0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - @Suppress - // Repro bug #2331761 - this should fail because of the last comma into BYDAY - public void test14() throws Exception { - verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;BYDAY=MO,TU,WE,", - /* int freq */ EventRecurrence.WEEKLY, - /* String until */ "20100129T130000Z", - /* int count */ 0, - /* int interval */ 1, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - }, - /* int[] bydayNum */ new int[]{0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // This test should pass - public void test15() throws Exception { - verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;" - + "BYDAY=MO,TU,WE,TH,FR,SA,SU", - /* int freq */ EventRecurrence.WEEKLY, - /* String until */ "20100129T130000Z", - /* int count */ 0, - /* int interval */ 1, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - EventRecurrence.TH, - EventRecurrence.FR, - EventRecurrence.SA, - EventRecurrence.SU - }, - /* int[] bydayNum */ new int[]{0, 0, 0, 0, 0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from RFC2445 - public void test16() throws Exception { - verifyRecurType("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1", - /* int freq */ EventRecurrence.MONTHLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.MO, - EventRecurrence.TU, - EventRecurrence.WE, - EventRecurrence.TH, - EventRecurrence.FR - }, - /* int[] bydayNum */ new int[] {0, 0, 0, 0, 0}, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ new int[] { -1 }, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from RFC2445 - public void test17() throws Exception { - verifyRecurType("FREQ=DAILY;COUNT=10;INTERVAL=2", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 10, - /* int interval */ 2, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from RFC2445 - public void test18() throws Exception { - verifyRecurType("FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10", - /* int freq */ EventRecurrence.YEARLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.SU - }, - /* int[] bydayNum */ new int[] { -1 }, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ new int[] { 10 }, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // Sample coming from bug #1640517 - public void test19() throws Exception { - verifyRecurType("FREQ=YEARLY;BYMONTH=3;BYDAY=TH", - /* int freq */ EventRecurrence.YEARLY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ new int[] { - EventRecurrence.TH - }, - /* int[] bydayNum */ new int[] { 0 }, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ new int[] { 3 }, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - // for your copying pleasure - public void fakeTestXX() throws Exception { - verifyRecurType("FREQ=DAILY;", - /* int freq */ EventRecurrence.DAILY, - /* String until */ null, - /* int count */ 0, - /* int interval */ 0, - /* int[] bysecond */ null, - /* int[] byminute */ null, - /* int[] byhour */ null, - /* int[] byday */ null, - /* int[] bydayNum */ null, - /* int[] bymonthday */ null, - /* int[] byyearday */ null, - /* int[] byweekno */ null, - /* int[] bymonth */ null, - /* int[] bysetpos */ null, - /* int wkst */ EventRecurrence.MO - ); - } - - private static void cmp(int vlen, int[] v, int[] correct, String name) { - if ((correct == null && v != null) - || (correct != null && v == null)) { - throw new RuntimeException("One is null, one isn't for " + name - + ": correct=" + Arrays.toString(correct) - + " actual=" + Arrays.toString(v)); - } - if ((correct == null && vlen != 0) - || (vlen != (correct == null ? 0 : correct.length))) { - throw new RuntimeException("Reported length mismatch for " + name - + ": correct=" + ((correct == null) ? "null" : correct.length) - + " actual=" + vlen); - } - if (correct == null) { - return; - } - if (v.length < correct.length) { - throw new RuntimeException("Array length mismatch for " + name - + ": correct=" + Arrays.toString(correct) - + " actual=" + Arrays.toString(v)); - } - for (int i = 0; i < correct.length; i++) { - if (v[i] != correct[i]) { - throw new RuntimeException("Array value mismatch for " + name - + ": correct=" + Arrays.toString(correct) - + " actual=" + Arrays.toString(v)); - } - } - } - - private static boolean eq(String a, String b) { - if ((a == null && b != null) || (a != null && b == null)) { - return false; - } else { - return a == b || a.equals(b); - } - } - - private static void verifyRecurType(String recur, - int freq, String until, int count, int interval, - int[] bysecond, int[] byminute, int[] byhour, - int[] byday, int[] bydayNum, int[] bymonthday, - int[] byyearday, int[] byweekno, int[] bymonth, - int[] bysetpos, int wkst) { - EventRecurrence eventRecurrence = new EventRecurrence(); - eventRecurrence.parse(recur); - if (eventRecurrence.freq != freq - || !eq(eventRecurrence.until, until) - || eventRecurrence.count != count - || eventRecurrence.interval != interval - || eventRecurrence.wkst != wkst) { - System.out.println("Error... got:"); - print(eventRecurrence); - System.out.println("expected:"); - System.out.println("{"); - System.out.println(" freq=" + freq); - System.out.println(" until=" + until); - System.out.println(" count=" + count); - System.out.println(" interval=" + interval); - System.out.println(" wkst=" + wkst); - System.out.println(" bysecond=" + Arrays.toString(bysecond)); - System.out.println(" byminute=" + Arrays.toString(byminute)); - System.out.println(" byhour=" + Arrays.toString(byhour)); - System.out.println(" byday=" + Arrays.toString(byday)); - System.out.println(" bydayNum=" + Arrays.toString(bydayNum)); - System.out.println(" bymonthday=" + Arrays.toString(bymonthday)); - System.out.println(" byyearday=" + Arrays.toString(byyearday)); - System.out.println(" byweekno=" + Arrays.toString(byweekno)); - System.out.println(" bymonth=" + Arrays.toString(bymonth)); - System.out.println(" bysetpos=" + Arrays.toString(bysetpos)); - System.out.println("}"); - throw new RuntimeException("Mismatch in fields"); - } - cmp(eventRecurrence.bysecondCount, eventRecurrence.bysecond, bysecond, "bysecond"); - cmp(eventRecurrence.byminuteCount, eventRecurrence.byminute, byminute, "byminute"); - cmp(eventRecurrence.byhourCount, eventRecurrence.byhour, byhour, "byhour"); - cmp(eventRecurrence.bydayCount, eventRecurrence.byday, byday, "byday"); - cmp(eventRecurrence.bydayCount, eventRecurrence.bydayNum, bydayNum, "bydayNum"); - cmp(eventRecurrence.bymonthdayCount, eventRecurrence.bymonthday, bymonthday, "bymonthday"); - cmp(eventRecurrence.byyeardayCount, eventRecurrence.byyearday, byyearday, "byyearday"); - cmp(eventRecurrence.byweeknoCount, eventRecurrence.byweekno, byweekno, "byweekno"); - cmp(eventRecurrence.bymonthCount, eventRecurrence.bymonth, bymonth, "bymonth"); - cmp(eventRecurrence.bysetposCount, eventRecurrence.bysetpos, bysetpos, "bysetpos"); - } - - private static void print(EventRecurrence er) { - System.out.println("{"); - System.out.println(" freq=" + er.freq); - System.out.println(" until=" + er.until); - System.out.println(" count=" + er.count); - System.out.println(" interval=" + er.interval); - System.out.println(" wkst=" + er.wkst); - System.out.println(" bysecond=" + Arrays.toString(er.bysecond)); - System.out.println(" bysecondCount=" + er.bysecondCount); - System.out.println(" byminute=" + Arrays.toString(er.byminute)); - System.out.println(" byminuteCount=" + er.byminuteCount); - System.out.println(" byhour=" + Arrays.toString(er.byhour)); - System.out.println(" byhourCount=" + er.byhourCount); - System.out.println(" byday=" + Arrays.toString(er.byday)); - System.out.println(" bydayNum=" + Arrays.toString(er.bydayNum)); - System.out.println(" bydayCount=" + er.bydayCount); - System.out.println(" bymonthday=" + Arrays.toString(er.bymonthday)); - System.out.println(" bymonthdayCount=" + er.bymonthdayCount); - System.out.println(" byyearday=" + Arrays.toString(er.byyearday)); - System.out.println(" byyeardayCount=" + er.byyeardayCount); - System.out.println(" byweekno=" + Arrays.toString(er.byweekno)); - System.out.println(" byweeknoCount=" + er.byweeknoCount); - System.out.println(" bymonth=" + Arrays.toString(er.bymonth)); - System.out.println(" bymonthCount=" + er.bymonthCount); - System.out.println(" bysetpos=" + Arrays.toString(er.bysetpos)); - System.out.println(" bysetposCount=" + er.bysetposCount); - System.out.println("}"); - } - - - /** A list of valid rules. The parser must accept these. */ - private static final String[] GOOD_RRULES = { - /* extracted wholesale from from RFC 2445 section 4.8.5.4 */ - "FREQ=DAILY;COUNT=10", - "FREQ=DAILY;UNTIL=19971224T000000Z", - "FREQ=DAILY;INTERVAL=2", - "FREQ=DAILY;INTERVAL=10;COUNT=5", - "FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA", - "FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1", - "FREQ=WEEKLY;COUNT=10", - "FREQ=WEEKLY;UNTIL=19971224T000000Z", - "FREQ=WEEKLY;INTERVAL=2;WKST=SU", - "FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH", - "FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH", - "FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR", - "FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH", - "FREQ=MONTHLY;COUNT=10;BYDAY=1FR", - "FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR", - "FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU", - "FREQ=MONTHLY;COUNT=6;BYDAY=-2MO", - "FREQ=MONTHLY;BYMONTHDAY=-3", - "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15", - "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1", - "FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15", - "FREQ=MONTHLY;INTERVAL=2;BYDAY=TU", - "FREQ=YEARLY;COUNT=10;BYMONTH=6,7", - "FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3", - "FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200", - "FREQ=YEARLY;BYDAY=20MO", - "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO", - "FREQ=YEARLY;BYMONTH=3;BYDAY=TH", - "FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8", - "FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13", - "FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13", - "FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8", - "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3", - "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2", - "FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z", - "FREQ=MINUTELY;INTERVAL=15;COUNT=6", - "FREQ=MINUTELY;INTERVAL=90;COUNT=4", - "FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40", - "FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16", - "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO", - "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU", - /* a few more */ - "FREQ=SECONDLY;BYSECOND=0,15,59", - "FREQ=MINUTELY;BYMINUTE=0,15,59", - "FREQ=HOURLY;BYHOUR=+0,+15,+23", - "FREQ=DAILY;X-WHATEVER=blah", // fails on old parser - //"freq=daily;wkst=su", // fails on old parser - }; - - /** The parser must reject these. */ - private static final String[] BAD_RRULES = { - "INTERVAL=4;FREQ=YEARLY", // FREQ must come first - "FREQ=MONTHLY;FREQ=MONTHLY", // can't specify twice - "FREQ=MONTHLY;COUNT=1;COUNT=1", // can't specify twice - "FREQ=SECONDLY;BYSECOND=60", // range - "FREQ=MINUTELY;BYMINUTE=-1", // range - "FREQ=HOURLY;BYHOUR=24", // range - "FREQ=YEARLY;BYMONTHDAY=0", // zero not valid - //"FREQ=YEARLY;COUNT=1;UNTIL=12345", // can't have both COUNT and UNTIL - //"FREQ=DAILY;UNTIL=19970829T021400e", // invalid date - }; - - /** - * Simple test of good/bad rules. - */ - @SmallTest - public void testBasicParse() { - for (String rule : GOOD_RRULES) { - EventRecurrence recur = new EventRecurrence(); - recur.parse(rule); - } - - for (String rule : BAD_RRULES) { - EventRecurrence recur = new EventRecurrence(); - boolean didThrow = false; - - try { - recur.parse(rule); - } catch (InvalidFormatException ife) { - didThrow = true; - } - - assertTrue("Expected throw on " + rule, didThrow); - } - } -} diff --git a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java deleted file mode 100644 index e5ab179..0000000 --- a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.pim; - -import android.content.ContentValues; -import android.pim.ICalendar; -import android.pim.RecurrenceSet; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; -import android.provider.CalendarContract; -import junit.framework.TestCase; - -/** - * Test some pim.RecurrenceSet functionality. - */ -public class RecurrenceSetTest extends TestCase { - - // Test a recurrence - @SmallTest - public void testRecurrenceSet0() throws Exception { - String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n" - + "DTEND;TZID=America/New_York:20080221T190000\n" - + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n" - + "EXDATE:20080222T120000Z"; - verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null, - null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0); - } - - // Test 1 day all-day event - @SmallTest - public void testRecurrenceSet1() throws Exception { - String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090822\n" - + "RRULE:FREQ=YEARLY;WKST=SU"; - verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null, - null, null, 1250812800000L, "UTC", "P1D", 1); - } - - // Test 2 day all-day event - @SmallTest - public void testRecurrenceSet2() throws Exception { - String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090823\n" - + "RRULE:FREQ=YEARLY;WKST=SU"; - verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null, - null, null, 1250812800000L, "UTC", "P2D", 1); - } - - // run populateContentValues and verify the results - private void verifyPopulateContentValues(String recurrence, String rrule, String rdate, - String exrule, String exdate, long dtstart, String tzid, String duration, int allDay) - throws ICalendar.FormatException { - ICalendar.Component recurrenceComponent = - new ICalendar.Component("DUMMY", null /* parent */); - ICalendar.parseComponent(recurrenceComponent, recurrence); - ContentValues values = new ContentValues(); - RecurrenceSet.populateContentValues(recurrenceComponent, values); - Log.d("KS", "values " + values); - - assertEquals(rrule, values.get(android.provider.CalendarContract.Events.RRULE)); - assertEquals(rdate, values.get(android.provider.CalendarContract.Events.RDATE)); - assertEquals(exrule, values.get(android.provider.CalendarContract.Events.EXRULE)); - assertEquals(exdate, values.get(android.provider.CalendarContract.Events.EXDATE)); - assertEquals(dtstart, (long) values.getAsLong(CalendarContract.Events.DTSTART)); - assertEquals(tzid, values.get(android.provider.CalendarContract.Events.EVENT_TIMEZONE)); - assertEquals(duration, values.get(android.provider.CalendarContract.Events.DURATION)); - assertEquals(allDay, - (int) values.getAsInteger(android.provider.CalendarContract.Events.ALL_DAY)); - } -} diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 6db67c0..c54e4a1 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -16,23 +16,25 @@ package android.widget; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; +import com.android.frameworks.coretests.R; -import android.test.AndroidTestCase; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.GetChars; import android.view.View; -import android.widget.TextView; /** * TextViewTest tests {@link TextView}. */ -public class TextViewTest extends AndroidTestCase { +public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewTestActivity> { + + public TextViewTest() { + super(TextViewTestActivity.class); + } @SmallTest public void testArray() throws Exception { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; @@ -62,13 +64,13 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testTextDirectionDefault() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection()); } @SmallTest public void testSetGetTextDirection() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection()); @@ -88,7 +90,7 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionLtr() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("this is a test"); tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); @@ -109,10 +111,10 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionLtrWithInheritance() { - LinearLayout ll = new LinearLayout(mContext); + LinearLayout ll = new LinearLayout(getActivity()); ll.setTextDirection(View.TEXT_DIRECTION_RTL); - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("this is a test"); ll.addView(tv); @@ -134,7 +136,7 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionRtl() { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("\u05DD\u05DE"); // hebrew tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); @@ -155,10 +157,9 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testGetResolvedTextDirectionRtlWithInheritance() { - LinearLayout ll = new LinearLayout(mContext); - ll.setTextDirection(View.TEXT_DIRECTION_RTL); + LinearLayout ll = new LinearLayout(getActivity()); - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); tv.setText("\u05DD\u05DE"); // hebrew ll.addView(tv); @@ -169,6 +170,24 @@ public class TextViewTest extends AndroidTestCase { assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); + assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_LTR); + assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + // Force to RTL text direction on the layout + ll.setTextDirection(View.TEXT_DIRECTION_RTL); + + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + + tv.setTextDirection(View.TEXT_DIRECTION_INHERIT); assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); tv.setTextDirection(View.TEXT_DIRECTION_LTR); @@ -180,10 +199,10 @@ public class TextViewTest extends AndroidTestCase { @SmallTest public void testCharCountHeuristic() { - LinearLayout ll = new LinearLayout(mContext); + LinearLayout ll = new LinearLayout(getActivity()); ll.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); ll.addView(tv); tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT); @@ -211,4 +230,23 @@ public class TextViewTest extends AndroidTestCase { tv.setText("ab \u05DD\u05DE"); // latin + hebrew at 50% each assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); } + + @SmallTest + public void testResetTextDirection() { + final TextViewTestActivity activity = getActivity(); + + final LinearLayout ll = (LinearLayout) activity.findViewById(R.id.textviewtest_layout); + final TextView tv = (TextView) activity.findViewById(R.id.textviewtest_textview); + + getActivity().runOnUiThread(new Runnable() { + public void run() { + tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG); + assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection()); + assertEquals(true, tv.isResolvedTextDirection()); + + ll.removeView(tv); + assertEquals(false, tv.isResolvedTextDirection()); + } + }); + } } diff --git a/core/tests/coretests/src/android/widget/TextViewTestActivity.java b/core/tests/coretests/src/android/widget/TextViewTestActivity.java new file mode 100644 index 0000000..1bb4d24 --- /dev/null +++ b/core/tests/coretests/src/android/widget/TextViewTestActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.widget; + +import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; + +public class TextViewTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.textview_test); + } +} |