diff options
author | Mathias Agopian <mathias@google.com> | 2009-07-01 18:33:18 -0700 |
---|---|---|
committer | Mathias Agopian <mathias@google.com> | 2009-07-01 18:33:18 -0700 |
commit | dfe983bd7979ccb1602f29b8f9804c98411d9cd6 (patch) | |
tree | 8a5547078b72cb262e54f0640dd4ed746b5805ef /core | |
parent | 3a6b160a3b52cd96fb383d5ee93c22e5e938e0e2 (diff) | |
parent | 7f32b426cd6a865ac5e6e3e9fa833e9327fb415a (diff) | |
download | frameworks_base-dfe983bd7979ccb1602f29b8f9804c98411d9cd6.zip frameworks_base-dfe983bd7979ccb1602f29b8f9804c98411d9cd6.tar.gz frameworks_base-dfe983bd7979ccb1602f29b8f9804c98411d9cd6.tar.bz2 |
Merge commit 'goog/master' into merge_master
Diffstat (limited to 'core')
109 files changed, 2884 insertions, 1007 deletions
diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java index b383c61..fde920e 100644 --- a/core/java/android/accounts/Constants.java +++ b/core/java/android/accounts/Constants.java @@ -25,6 +25,7 @@ public class Constants { public static final int ERROR_CODE_INVALID_RESPONSE = 5; public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6; public static final int ERROR_CODE_BAD_ARGUMENTS = 7; + public static final int ERROR_CODE_BAD_REQUEST = 8; public static final String ACCOUNTS_KEY = "accounts"; public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types"; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 79588ea..62dc651 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3697,6 +3697,13 @@ public final class ActivityThread { */ Locale.setDefault(data.config.locale); + /* + * Update the system configuration since its preloaded and might not + * reflect configuration changes. The configuration object passed + * in AppBindData can be safely assumed to be up to date + */ + Resources.getSystem().updateConfiguration(mConfiguration, null); + data.info = getPackageInfoNoCheck(data.appInfo); if (data.debugMode != IApplicationThread.DEBUG_OFF) { diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java index e810775..0ac8a1e 100644 --- a/core/java/android/app/BackupAgent.java +++ b/core/java/android/app/BackupAgent.java @@ -67,7 +67,7 @@ public abstract class BackupAgent extends ContextWrapper { * here after writing the requested data to dataFd. */ public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState); + ParcelFileDescriptor newState) throws IOException; /** * The application is being restored from backup, and should replace any @@ -120,6 +120,9 @@ public abstract class BackupAgent extends ContextWrapper { BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); try { BackupAgent.this.onBackup(oldState, output, newState); + } catch (IOException ex) { + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw new RuntimeException(ex); } catch (RuntimeException ex) { Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); throw ex; diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 8d249da..accdda9 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -60,26 +60,20 @@ public abstract class LauncherActivity extends ListActivity { * An item in the list */ public static class ListItem { + public ResolveInfo resolveInfo; public CharSequence label; - //public CharSequence description; public Drawable icon; public String packageName; public String className; public Bundle extras; ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) { + this.resolveInfo = resolveInfo; label = resolveInfo.loadLabel(pm); if (label == null && resolveInfo.activityInfo != null) { label = resolveInfo.activityInfo.name; } - /* - if (resolveInfo.activityInfo != null && - resolveInfo.activityInfo.applicationInfo != null) { - description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm); - } - */ - icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm)); packageName = resolveInfo.activityInfo.applicationInfo.packageName; className = resolveInfo.activityInfo.name; @@ -122,6 +116,14 @@ public abstract class LauncherActivity extends ListActivity { return intent; } + public ListItem itemForPosition(int position) { + if (mActivitiesList == null) { + return null; + } + + return mActivitiesList.get(position); + } + public int getCount() { return mActivitiesList != null ? mActivitiesList.size() : 0; } @@ -354,6 +356,16 @@ public abstract class LauncherActivity extends ListActivity { } /** + * Return the {@link ListItem} for a specific position in our + * {@link android.widget.ListView}. + * @param position The item to return + */ + protected ListItem itemForPosition(int position) { + ActivityAdapter adapter = (ActivityAdapter) mAdapter; + return adapter.itemForPosition(position); + } + + /** * Get the base intent to use when running * {@link PackageManager#queryIntentActivities(Intent, int)}. */ diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 6ddf50f..44d1eaa 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -33,8 +33,8 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; -import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Animatable; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -107,7 +107,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private Button mGoButton; private ImageButton mVoiceButton; private View mSearchPlate; - private AnimationDrawable mWorkingSpinner; + private Drawable mWorkingSpinner; // interaction with searchable application private SearchableInfo mSearchable; @@ -188,7 +188,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); mSearchPlate = findViewById(com.android.internal.R.id.search_plate); - mWorkingSpinner = (AnimationDrawable) getContext().getResources(). + mWorkingSpinner = getContext().getResources(). getDrawable(com.android.internal.R.drawable.search_spinner); // attach listeners @@ -423,11 +423,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (working) { mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( null, null, mWorkingSpinner, null); - mWorkingSpinner.start(); + ((Animatable) mWorkingSpinner).start(); } else { mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( null, null, null, null); - mWorkingSpinner.stop(); + ((Animatable) mWorkingSpinner).stop(); } } @@ -601,7 +601,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchPlate.getPaddingBottom()); } else { PackageManager pm = getContext().getPackageManager(); - Drawable icon = null; + Drawable icon; try { ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0); icon = pm.getApplicationIcon(info.applicationInfo); diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java new file mode 100644 index 0000000..ab24675 --- /dev/null +++ b/core/java/android/backup/AbsoluteFileBackupHelper.java @@ -0,0 +1,66 @@ +/* + * 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.backup; + +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; + +/** + * Like FileBackupHelper, but takes absolute paths for the files instead of + * subpaths of getFilesDir() + * + * @hide + */ +public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper { + private static final String TAG = "AbsoluteFileBackupHelper"; + + Context mContext; + String[] mFiles; + + public AbsoluteFileBackupHelper(Context context, String... files) { + super(context); + + mContext = context; + mFiles = files; + } + + /** + * Based on oldState, determine which of the files from the application's data directory + * need to be backed up, write them to the data stream, and fill in newState with the + * state as it exists now. + */ + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + // use the file paths as the keys, too + performBackup_checked(oldState, data, newState, mFiles, mFiles); + } + + public void restoreEntity(BackupDataInputStream data) { + // TODO: turn this off before ship + Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); + String key = data.getKey(); + if (isKeyInList(key, mFiles)) { + File f = new File(key); + writeFile(f, data); + } + } +} + diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java index 3720d50..5d0c4a2 100644 --- a/core/java/android/backup/BackupHelperAgent.java +++ b/core/java/android/backup/BackupHelperAgent.java @@ -34,7 +34,7 @@ public class BackupHelperAgent extends BackupAgent { @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { + ParcelFileDescriptor newState) throws IOException { mDispatcher.performBackup(oldState, data, newState); } diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java index b25c3e3..6ccb83e 100644 --- a/core/java/android/backup/BackupHelperDispatcher.java +++ b/core/java/android/backup/BackupHelperDispatcher.java @@ -19,7 +19,10 @@ package android.backup; import android.os.ParcelFileDescriptor; import android.util.Log; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; +import java.io.FileDescriptor; import java.util.TreeMap; import java.util.Map; @@ -27,6 +30,11 @@ import java.util.Map; public class BackupHelperDispatcher { private static final String TAG = "BackupHelperDispatcher"; + private static class Header { + int chunkSize; // not including the header + String keyPrefix; + } + TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>(); public BackupHelperDispatcher() { @@ -36,13 +44,63 @@ public class BackupHelperDispatcher { mHelpers.put(keyPrefix, helper); } - /** TODO: Make this save and restore the key prefix. */ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { - // Write out the state files -- mHelpers is a TreeMap, so the order is well defined. - for (Map.Entry<String,BackupHelper> entry: mHelpers.entrySet()) { - data.setKeyPrefix(entry.getKey()); - entry.getValue().performBackup(oldState, data, newState); + ParcelFileDescriptor newState) throws IOException { + // First, do the helpers that we've already done, since they're already in the state + // file. + int err; + Header header = new Header(); + TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone(); + FileDescriptor oldStateFD = null; + FileDescriptor newStateFD = newState.getFileDescriptor(); + + if (oldState != null) { + oldStateFD = oldState.getFileDescriptor(); + while ((err = readHeader_native(header, oldStateFD)) >= 0) { + if (err == 0) { + BackupHelper helper = helpers.get(header.keyPrefix); + Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper); + if (helper != null) { + doOneBackup(oldState, data, newState, header, helper); + helpers.remove(header.keyPrefix); + } else { + skipChunk_native(oldStateFD, header.chunkSize); + } + } + } + } + + // Then go through and do the rest that we haven't done. + for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) { + header.keyPrefix = entry.getKey(); + Log.d(TAG, "handling new helper '" + header.keyPrefix + "'"); + BackupHelper helper = entry.getValue(); + doOneBackup(oldState, data, newState, header, helper); + } + } + + private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState, Header header, BackupHelper helper) + throws IOException { + int err; + FileDescriptor newStateFD = newState.getFileDescriptor(); + + // allocate space for the header in the file + int pos = allocateHeader_native(header, newStateFD); + if (pos < 0) { + throw new IOException("allocateHeader_native failed (error " + pos + ")"); + } + + data.setKeyPrefix(header.keyPrefix); + + // do the backup + helper.performBackup(oldState, data, newState); + + // fill in the header (seeking back to pos). The file pointer will be returned to + // where it was at the end of performBackup. Header.chunkSize will not be filled in. + err = writeHeader_native(header, newStateFD, pos); + if (err != 0) { + throw new IOException("writeHeader_native failed (error " + err + ")"); } } @@ -83,5 +141,11 @@ public class BackupHelperDispatcher { helper.writeRestoreSnapshot(newState); } } + + private static native int readHeader_native(Header h, FileDescriptor fd); + private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip); + + private static native int allocateHeader_native(Header h, FileDescriptor fd); + private static native int writeHeader_native(Header h, FileDescriptor fd, int pos); } diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index 8df7eae..5b4ac0d 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -68,9 +68,11 @@ public class BackupManager { * {@link android.app.BackupAgent} subclass will be scheduled when you call this method. */ public void dataChanged() { - try { - mService.dataChanged(mContext.getPackageName()); - } catch (RemoteException e) { + if (mService != null) { + try { + mService.dataChanged(mContext.getPackageName()); + } catch (RemoteException e) { + } } } @@ -81,11 +83,13 @@ public class BackupManager { * * {@hide} */ - public IRestoreSession beginRestoreSession(int transportID) { + public IRestoreSession beginRestoreSession(String transport) { IRestoreSession binder = null; - try { - binder = mService.beginRestoreSession(transportID); - } catch (RemoteException e) { + if (mService != null) { + try { + binder = mService.beginRestoreSession(transport); + } catch (RemoteException e) { + } } return binder; } diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl index d6283d0..1f11762 100644 --- a/core/java/android/backup/IBackupManager.aidl +++ b/core/java/android/backup/IBackupManager.aidl @@ -48,6 +48,24 @@ interface IBackupManager { void agentDisconnected(String packageName); /** + * Enable/disable the backup service entirely. When disabled, no backup + * or restore operations will take place. Data-changed notifications will + * still be observed and collected, however, so that changes made while the + * mechanism was disabled will still be backed up properly if it is enabled + * at some point in the future. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + */ + void setBackupEnabled(boolean isEnabled); + + /** + * Report whether the backup mechanism is currently enabled. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + */ + boolean isBackupEnabled(); + + /** * Schedule an immediate backup attempt for all pending updates. This is * primarily intended for transports to use when they detect a suitable * opportunity for doing a backup pass. If there are no pending updates to @@ -63,23 +81,32 @@ interface IBackupManager { * Identify the currently selected transport. Callers must hold the * android.permission.BACKUP permission to use this method. */ - int getCurrentTransport(); + String getCurrentTransport(); + + /** + * Request a list of all available backup transports' names. Callers must + * hold the android.permission.BACKUP permission to use this method. + */ + String[] listAllTransports(); /** - * Specify a default backup transport. Callers must hold the + * Specify the current backup transport. Callers must hold the * android.permission.BACKUP permission to use this method. * - * @param transportID The ID of the transport to select. This should be one + * @param transport The name of the transport to select. This should be one * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}. - * @return The ID of the previously selected transport. + * @return The name of the previously selected transport. If the given transport + * name is not one of the currently available transports, no change is made to + * the current transport setting and the method returns null. */ - int selectBackupTransport(int transportID); + String selectBackupTransport(String transport); /** * Begin a restore session with the given transport (which may differ from the * currently-active backup transport). * + * @param transport The name of the transport to use for the restore operation. * @return An interface to the restore session, or null on error. */ - IRestoreSession beginRestoreSession(int transportID); + IRestoreSession beginRestoreSession(String transportID); } diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl new file mode 100644 index 0000000..59e59fc --- /dev/null +++ b/core/java/android/backup/IRestoreObserver.aidl @@ -0,0 +1,50 @@ +/* + * 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.backup; + +/** + * Callback class for receiving progress reports during a restore operation. + * + * @hide + */ +interface IRestoreObserver { + /** + * The restore operation has begun. + * + * @param numPackages The total number of packages being processed in + * this restore operation. + */ + void restoreStarting(int numPackages); + + /** + * An indication of which package is being restored currently, out of the + * total number provided in the restoreStarting() callback. This method + * is not guaranteed to be called. + * + * @param nowBeingRestored The index, between 1 and the numPackages parameter + * to the restoreStarting() callback, of the package now being restored. + */ + void onUpdate(int nowBeingRestored); + + /** + * The restore operation has completed. + * + * @param error Zero on success; a nonzero error code if the restore operation + * as a whole failed. + */ + void restoreFinished(int error); +} diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl index 6bca865..2a1fbc1 100644 --- a/core/java/android/backup/IRestoreSession.aidl +++ b/core/java/android/backup/IRestoreSession.aidl @@ -17,6 +17,7 @@ package android.backup; import android.backup.RestoreSet; +import android.backup.IRestoreObserver; /** * Binder interface used by clients who wish to manage a restore operation. Every @@ -41,8 +42,10 @@ interface IRestoreSession { * * @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. */ - int performRestore(int token); + int performRestore(long token, IRestoreObserver observer); /** * End this restore session. After this method is called, the IRestoreSession binder diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index f492629..4a7b399 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -30,7 +30,7 @@ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implemen private Context mContext; private String[] mPrefGroups; - public SharedPreferencesBackupHelper(Context context, String[] prefGroups) { + public SharedPreferencesBackupHelper(Context context, String... prefGroups) { super(context); mContext = context; diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 85d877a..27783ef 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -235,6 +235,12 @@ public class ActivityInfo extends ComponentInfo public static final int CONFIG_ORIENTATION = 0x0080; /** * Bit in {@link #configChanges} that indicates that the activity + * can itself handle changes to the screen layout. Set from the + * {@link android.R.attr#configChanges} attribute. + */ + public static final int CONFIG_SCREEN_LAYOUT = 0x0100; + /** + * Bit in {@link #configChanges} that indicates that the activity * can itself handle changes to the font scaling factor. Set from the * {@link android.R.attr#configChanges} attribute. This is * not a core resource configutation, but a higher-level value, so its @@ -248,8 +254,8 @@ public class ActivityInfo extends ComponentInfo * Contains any combination of {@link #CONFIG_FONT_SCALE}, * {@link #CONFIG_MCC}, {@link #CONFIG_MNC}, * {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN}, - * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, and - * {@link #CONFIG_ORIENTATION}. Set from the + * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, + * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}. Set from the * {@link android.R.attr#configChanges} attribute. */ public int configChanges; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 2a2cf93..bcf95b6 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -138,10 +138,27 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Value for {@link #flags}: true when the application's window can be - * expanded over default window size in target density (320x480 for - * 1.0 density, 480x720 for 1.5 density etc) + * reduced in size for smaller screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_smallScreens + * android:smallScreens}. */ - public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<9; + public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9; + + /** + * Value for {@link #flags}: true when the application's window can be + * displayed on normal screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens + * android:normalScreens}. + */ + public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; + + /** + * Value for {@link #flags}: true when the application's window can be + * increased in size for larger screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_largeScreens + * android:smallScreens}. + */ + public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11; /** * Value for {@link #flags}: this is false if the application has set @@ -149,7 +166,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * * {@hide} */ - public static final int FLAG_ALLOW_BACKUP = 1<<10; + public static final int FLAG_ALLOW_BACKUP = 1<<12; /** * Indicates that the application supports any densities; @@ -164,7 +181,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and * {@link #FLAG_ALLOW_TASK_REPARENTING} * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP}, - * {@link #FLAG_TEST_ONLY}. + * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS}, + * {@link #FLAG_SUPPORTS_NORMAL_SCREENS}, + * {@link #FLAG_SUPPORTS_LARGE_SCREENS}. */ public int flags = 0; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ab9518e..558b0c3 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -668,6 +668,11 @@ public class PackageParser { } sa.recycle(); + // Resource boolean are -1, so 1 means we don't know the value. + int supportsSmallScreens = 1; + int supportsNormalScreens = 1; + int supportsLargeScreens = 1; + int outerDepth = parser.getDepth(); while ((type=parser.next()) != parser.END_DOCUMENT && (type != parser.END_TAG || parser.getDepth() > outerDepth)) { @@ -876,8 +881,24 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); - } else if (tagName.equals("expandable")) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + } else if (tagName.equals("supports-screens")) { + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestSupportsScreens); + + // This is a trick to get a boolean and still able to detect + // if a value was actually set. + supportsSmallScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens, + supportsSmallScreens); + supportsNormalScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens, + supportsNormalScreens); + supportsLargeScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens, + supportsLargeScreens); + + sa.recycle(); + XmlUtils.skipCurrentTag(parser); } else { Log.w(TAG, "Bad element under <manifest>: " @@ -910,7 +931,20 @@ public class PackageParser { pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()]; pkg.usesLibraries.toArray(pkg.usesLibraryFiles); } - // TODO: enable all density & expandable if target sdk is higher than donut + + if (supportsSmallScreens < 0 || (supportsSmallScreens > 0 + && pkg.applicationInfo.targetSdkVersion + >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; + } + if (supportsNormalScreens != 0) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS; + } + if (supportsLargeScreens < 0 || (supportsLargeScreens > 0 + && pkg.applicationInfo.targetSdkVersion + >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + } int size = pkg.supportsDensityList.size(); if (size > 0) { diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 1c91736..5c7b01f 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -601,7 +601,7 @@ public final class AssetManager { public native final void setConfiguration(int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int majorVersion); + int screenLayout, int majorVersion); /** * Retrieve the resource identifier for the given resource name. diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 179b9bd..4e6fe07 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -65,7 +65,7 @@ public class CompatibilityInfo { /** * A compatibility flags */ - private int compatibilityFlags; + private int mCompatibilityFlags; /** * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f) @@ -101,7 +101,11 @@ public class CompatibilityInfo { */ public final float applicationInvertedScale; - + /** + * The flags from ApplicationInfo. + */ + public final int appFlags; + /** * Window size in Compatibility Mode, in real pixels. This is updated by * {@link DisplayMetrics#updateMetrics}. @@ -117,8 +121,10 @@ public class CompatibilityInfo { private int mXOffset; public CompatibilityInfo(ApplicationInfo appInfo) { + appFlags = appInfo.flags; + if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { - compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; + mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; } float packageDensityScale = -1.0f; @@ -149,13 +155,16 @@ public class CompatibilityInfo { } applicationInvertedScale = 1.0f / applicationScale; if (applicationScale != 1.0f) { - compatibilityFlags |= SCALING_REQUIRED; + mCompatibilityFlags |= SCALING_REQUIRED; } } private CompatibilityInfo() { + appFlags = ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; applicationScale = applicationInvertedScale = 1.0f; - compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; + mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; } /** @@ -175,9 +184,9 @@ public class CompatibilityInfo { */ public void setExpandable(boolean expandable) { if (expandable) { - compatibilityFlags |= CompatibilityInfo.EXPANDABLE; + mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE; } else { - compatibilityFlags &= ~CompatibilityInfo.EXPANDABLE; + mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE; } } @@ -185,20 +194,20 @@ public class CompatibilityInfo { * @return true if the application is configured to be expandable. */ public boolean isConfiguredExpandable() { - return (compatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0; + return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0; } /** * @return true if the scaling is required */ public boolean isScalingRequired() { - return (compatibilityFlags & SCALING_REQUIRED) != 0; + return (mCompatibilityFlags & SCALING_REQUIRED) != 0; } @Override public String toString() { return "CompatibilityInfo{scale=" + applicationScale + - ", compatibility flag=" + compatibilityFlags + "}"; + ", compatibility flag=" + mCompatibilityFlags + "}"; } /** @@ -222,13 +231,13 @@ public class CompatibilityInfo { * @param params the window's parameter */ public Translator getTranslator(WindowManager.LayoutParams params) { - if ( (compatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK) + if ( (mCompatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK) == CompatibilityInfo.EXPANDABLE) { if (DBG) Log.d(TAG, "no translation required"); return null; } - if ((compatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) { + if ((mCompatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) { if ((params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) { if (DBG) Log.d(TAG, "translation for surface view selected"); return new Translator(X_SHIFT_WINDOW, false, 1.0f, 1.0f); diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index bb3486c..577aa60 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -116,6 +116,18 @@ public final class Configuration implements Parcelable, Comparable<Configuration */ public int orientation; + public static final int SCREENLAYOUT_UNDEFINED = 0; + public static final int SCREENLAYOUT_SMALL = 1; + public static final int SCREENLAYOUT_NORMAL = 2; + public static final int SCREENLAYOUT_LARGE = 3; + + /** + * Overall layout of the screen. May be one of + * {@link #SCREENLAYOUT_SMALL}, {@link #SCREENLAYOUT_NORMAL}, + * or {@link #SCREENLAYOUT_LARGE}. + */ + public int screenLayout; + /** * Construct an invalid Configuration. You must call {@link #setToDefaults} * for this object to be valid. {@more} @@ -141,6 +153,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration hardKeyboardHidden = o.hardKeyboardHidden; navigation = o.navigation; orientation = o.orientation; + screenLayout = o.screenLayout; } public String toString() { @@ -165,6 +178,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration sb.append(navigation); sb.append(" orien="); sb.append(orientation); + sb.append(" layout="); + sb.append(screenLayout); sb.append('}'); return sb.toString(); } @@ -183,6 +198,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED; navigation = NAVIGATION_UNDEFINED; orientation = ORIENTATION_UNDEFINED; + screenLayout = SCREENLAYOUT_UNDEFINED; } /** {@hide} */ @@ -253,6 +269,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_ORIENTATION; orientation = delta.orientation; } + if (delta.screenLayout != SCREENLAYOUT_UNDEFINED + && screenLayout != delta.screenLayout) { + changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT; + screenLayout = delta.screenLayout; + } return changed; } @@ -276,9 +297,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD * PackageManager.ActivityInfo.CONFIG_KEYBOARD}, * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION - * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, or + * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION - * PackageManager.ActivityInfo.CONFIG_ORIENTATION}. + * PackageManager.ActivityInfo.CONFIG_ORIENTATION}, or + * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT + * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}. */ public int diff(Configuration delta) { int changed = 0; @@ -319,6 +342,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration && orientation != delta.orientation) { changed |= ActivityInfo.CONFIG_ORIENTATION; } + if (delta.screenLayout != SCREENLAYOUT_UNDEFINED + && screenLayout != delta.screenLayout) { + changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT; + } return changed; } @@ -368,6 +395,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(hardKeyboardHidden); dest.writeInt(navigation); dest.writeInt(orientation); + dest.writeInt(screenLayout); } public static final Parcelable.Creator<Configuration> CREATOR @@ -399,6 +427,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration hardKeyboardHidden = source.readInt(); navigation = source.readInt(); orientation = source.readInt(); + screenLayout = source.readInt(); } public int compareTo(Configuration that) { @@ -428,6 +457,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration n = this.navigation - that.navigation; if (n != 0) return n; n = this.orientation - that.orientation; + if (n != 0) return n; + n = this.screenLayout - that.screenLayout; //if (n != 0) return n; return n; } @@ -450,6 +481,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration return ((int)this.fontScale) + this.mcc + this.mnc + this.locale.hashCode() + this.touchscreen + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden - + this.navigation + this.orientation; + + this.navigation + this.orientation + this.screenLayout; } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index cb9d46e..d7512bb 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1267,7 +1267,8 @@ public class Resources { } if (metrics != null) { mMetrics.setTo(metrics); - mMetrics.updateMetrics(mCompatibilityInfo, mConfiguration.orientation); + mMetrics.updateMetrics(mCompatibilityInfo, + mConfiguration.orientation, mConfiguration.screenLayout); } mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; @@ -1299,7 +1300,7 @@ public class Resources { mConfiguration.touchscreen, (int)(mMetrics.density*160), mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, - sSdkVersion); + mConfiguration.screenLayout, sSdkVersion); int N = mDrawableCache.size(); if (DEBUG_CONFIG) { Log.d(TAG, "Cleaning up drawables config changes: 0x" diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index ca579b6..3ce951f 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -39,13 +39,16 @@ import android.os.Message; public class Camera { private static final String TAG = "Camera"; - // These match the enum in libs/android_runtime/android_hardware_Camera.cpp - private static final int SHUTTER_CALLBACK = 0; - private static final int RAW_PICTURE_CALLBACK = 1; - private static final int JPEG_PICTURE_CALLBACK = 2; - private static final int PREVIEW_CALLBACK = 3; - private static final int AUTOFOCUS_CALLBACK = 4; - private static final int ERROR_CALLBACK = 5; + // These match the enums in frameworks/base/include/ui/Camera.h + private static final int CAMERA_MSG_ERROR = 0; + private static final int CAMERA_MSG_SHUTTER = 1; + private static final int CAMERA_MSG_FOCUS = 2; + private static final int CAMERA_MSG_ZOOM = 3; + private static final int CAMERA_MSG_PREVIEW_FRAME = 4; + private static final int CAMERA_MSG_VIDEO_FRAME = 5; + private static final int CAMERA_MSG_POSTVIEW_FRAME = 6; + private static final int CAMERA_MSG_RAW_IMAGE = 7; + private static final int CAMERA_MSG_COMPRESSED_IMAGE = 8; private int mNativeContext; // accessed by native methods private EventHandler mEventHandler; @@ -152,7 +155,11 @@ public class Camera { * @throws IOException if the method fails. */ public final void setPreviewDisplay(SurfaceHolder holder) throws IOException { - setPreviewDisplay(holder.getSurface()); + if (holder != null) { + setPreviewDisplay(holder.getSurface()); + } else { + setPreviewDisplay((Surface)null); + } } private native final void setPreviewDisplay(Surface surface); @@ -231,22 +238,23 @@ public class Camera { @Override public void handleMessage(Message msg) { switch(msg.what) { - case SHUTTER_CALLBACK: + case CAMERA_MSG_SHUTTER: if (mShutterCallback != null) { mShutterCallback.onShutter(); } return; - case RAW_PICTURE_CALLBACK: + + case CAMERA_MSG_RAW_IMAGE: if (mRawImageCallback != null) mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera); return; - case JPEG_PICTURE_CALLBACK: + case CAMERA_MSG_COMPRESSED_IMAGE: if (mJpegCallback != null) mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera); return; - case PREVIEW_CALLBACK: + case CAMERA_MSG_PREVIEW_FRAME: if (mPreviewCallback != null) { mPreviewCallback.onPreviewFrame((byte[])msg.obj, mCamera); if (mOneShot) { @@ -255,12 +263,12 @@ public class Camera { } return; - case AUTOFOCUS_CALLBACK: + case CAMERA_MSG_FOCUS: if (mAutoFocusCallback != null) mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera); return; - case ERROR_CALLBACK: + case CAMERA_MSG_ERROR : Log.e(TAG, "Error " + msg.arg1); if (mErrorCallback != null) mErrorCallback.onError(msg.arg1, mCamera); diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index c4ee5b0..6a97951 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -159,11 +159,11 @@ public class RequestHandle { e.printStackTrace(); } - // update the "cookie" header based on the redirected url - mHeaders.remove("cookie"); + // update the "Cookie" header based on the redirected url + mHeaders.remove("Cookie"); String cookie = CookieManager.getInstance().getCookie(mUri); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) { diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 6c13582..abfb274 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -127,12 +127,12 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; - private static final int CORE_POOL_SIZE = 1; - private static final int MAXIMUM_POOL_SIZE = 10; + private static final int CORE_POOL_SIZE = 5; + private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 10; private static final BlockingQueue<Runnable> sWorkQueue = - new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE); + new LinkedBlockingQueue<Runnable>(10); private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 333c7cb..1214abc 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -573,7 +573,21 @@ public class Process { * directly to a gid. */ public static final native int getGidForName(String name); - + + /** + * Returns a uid for a currently running process. + * @param pid the process id + * @return the uid of the process, or -1 if the process is not running. + * @hide pending API council review + */ + public static final int getUidForPid(int pid) { + String[] procStatusLabels = { "Uid:" }; + long[] procStatusValues = new long[1]; + procStatusValues[0] = -1; + Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues); + return (int) procStatusValues[0]; + } + /** * Set the priority of a thread, based on Linux priorities. * diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 6ea2528..95e5432 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -150,7 +150,7 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi // Set the title bar if title is available, else no title bar final CharSequence title = getTitle(); - Dialog dialog = mDialog = new Dialog(context, !TextUtils.isEmpty(title) + Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title) ? com.android.internal.R.style.Theme_NoTitleBar : com.android.internal.R.style.Theme); dialog.setContentView(listView); diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index 2490b8a..b95e4e1 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -91,6 +91,17 @@ public class Browser { */ public static final String EXTRA_APPEND_LOCATION = "com.android.browser.append_location"; + /** + * The name of the extra data in the VIEW intent. The data is in the format of + * a byte array. + * <p> + * Any value sent here will be passed in the http request to the provided url as post data. + * <p> + * pending api approval + * @hide + */ + public static final String EXTRA_POST_DATA = "com.android.browser.post_data"; + /* if you change column order you must also change indices below */ public static final String[] HISTORY_PROJECTION = new String[] { diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 48dc3ae..5cc3315 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -16,6 +16,9 @@ package android.provider; +import java.util.Arrays; +import java.util.List; + import android.content.Intent; import android.graphics.BitmapFactory; import android.net.Uri; @@ -127,6 +130,19 @@ public final class ContactsContract { public static final String STARRED = "starred"; /** + * A custom ringtone associated with a person. Not always present. + * <P>Type: TEXT (URI to the ringtone)</P> + */ + public static final String CUSTOM_RINGTONE = "custom_ringtone"; + + /** + * Whether the person should always be sent to voicemail. Not always + * present. + * <P>Type: INTEGER (0 for false, 1 for true)</P> + */ + public static final String SEND_TO_VOICEMAIL = "send_to_voicemail"; + + /** * Reference to the row in the data table holding the primary phone number. * <P>Type: INTEGER REFERENCES data(_id)</P> */ @@ -143,12 +159,6 @@ public final class ContactsContract { * <P>Type: INTEGER REFERENCES data(_id)</P> */ public static final String PHOTO_ID = "photo_id"; - - /** - * Reference to a row containing custom ringtone and send to voicemail information. - * <P>Type: INTEGER REFERENCES data(_id)</P> - */ - public static final String CUSTOM_RINGTONE_ID = "custom_ringtone_id"; } /** @@ -261,6 +271,19 @@ public final class ContactsContract { private Contacts() {} /** + * The package name that owns this contact and all of its details. This + * package has control over the {@link #IS_RESTRICTED} flag, and can + * grant {@link RestrictionExceptions} to other packages. + */ + public static final String PACKAGE = "package"; + + /** + * Flag indicating that this data entry has been restricted by the owner + * {@link #PACKAGE}. + */ + public static final String IS_RESTRICTED = "is_restricted"; + + /** * A reference to the {@link Accounts#_ID} that this data belongs to. */ public static final String ACCOUNTS_ID = "accounts_id"; @@ -332,11 +355,6 @@ public final class ContactsContract { private interface DataColumns { /** - * The package name that defines this type of data. - */ - public static final String PACKAGE = "package"; - - /** * The mime-type of the item represented by this row. */ public static final String MIMETYPE = "mimetype"; @@ -361,12 +379,6 @@ public final class ContactsContract { public static final String IS_SUPER_PRIMARY = "is_super_primary"; /** - * Flag indicating that this data entry has been restricted by the owner - * {@link #PACKAGE}. - */ - public static final String IS_RESTRICTED = "is_restricted"; - - /** * The version of this data record. This is a read-only value. The data column is * guaranteed to not change without the version going up. This value is monotonically * increasing. @@ -519,6 +531,18 @@ public final class ContactsContract { } /** + * Returns the precedence of the status code the higher number being the higher precedence. + * + * @param status The status code. + * @return An integer representing the precedence, 0 being the lowest. + */ + public static final int getPresencePrecedence(int status) { + // Keep this function here incase we want to enforce a different precedence than the + // natural order of the status constants. + return status; + } + + /** * The MIME type of {@link #CONTENT_URI} providing a directory of * presence details. */ @@ -914,28 +938,6 @@ public final class ContactsContract { } /** - * Custom ringtone associated with the contact. - */ - public static final class CustomRingtone implements BaseCommonColumns { - private CustomRingtone() {} - - public static final String CONTENT_ITEM_TYPE = - "vnd.android.cursor.item/custom_ringtone"; - - /** - * Whether to send the number to voicemail. - * <P>Type: INTEGER (if set, non-0 means true)</P> - */ - public static final String SEND_TO_VOICEMAIL = "data1"; - - /** - * The ringtone uri. - * <P>Type: TEXT</P> - */ - public static final String RINGTONE_URI = "data2"; - } - - /** * Group Membership. */ public static final class GroupMembership implements BaseCommonColumns { diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 51d1951..bc7b5be 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -344,7 +344,10 @@ public final class MediaStore // Check if file exists with a FileInputStream FileInputStream stream = new FileInputStream(imagePath); try { - return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description); + Bitmap bm = BitmapFactory.decodeFile(imagePath); + String ret = insertImage(cr, bm, name, description); + bm.recycle(); + return ret; } finally { try { stream.close(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ebe54fc..303d3fc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1290,6 +1290,50 @@ public final class Settings { public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone"; /** + * CDMA only settings + * DTMF tone type played by the dialer when dialing. + * 0 = Normal + * 1 = Long + * @hide + */ + public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type"; + + /** + * CDMA only settings + * Emergency Tone 0 = Off + * 1 = Alert + * 2 = Vibrate + * @hide + */ + public static final String EMERGENCY_TONE = "emergency_tone"; + + /** + * CDMA only settings + * Whether the auto retry is enabled. The value is + * boolean (1 or 0). + * @hide + */ + public static final String CALL_AUTO_RETRY = "call_auto_retry"; + + /** + * Whether the hearing aid is enabled. The value is + * boolean (1 or 0). + * @hide + */ + public static final String HEARING_AID = "hearing_aid"; + + /** + * CDMA only settings + * TTY Mode + * 0 = OFF + * 1 = FULL + * 2 = VCO + * 3 = HCO + * @hide + */ + public static final String TTY_MODE = "tty_mode"; + + /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is * boolean (1 or 0). */ @@ -1884,6 +1928,12 @@ public final class Settings { public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed"; /** + * Whether assisted GPS should be enabled or not. + * @hide + */ + public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled"; + + /** * The Logging ID (a unique 64-bit value) as a hex string. * Used as a pseudonymous identifier for logging. * @deprecated This identifier is poorly initialized and has @@ -2712,6 +2762,12 @@ public final class Settings { "gtalk_max_retries_for_auth_expired"; /** + * This is the url for getting the app token for server-to-device data messaging. + */ + public static final String DATA_MESSAGE_GET_APP_TOKEN_URL = + "data_messaging_get_app_token_url"; + + /** * Enable use of ssl session caching. * 'db' - save each session in a (per process) database * 'file' - save each session in a (per process) file diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 75c590d..77b1b1d 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -545,15 +545,28 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { Log.e(TAG, "*Error*: GetAdapterProperties returned NULL"); return; } - for (int i = 0; i < properties.length; i+=2) { - String value = null; - if (mProperties.containsKey(properties[i])) { - value = mProperties.get(properties[i]); - value = value + ',' + properties[i+1]; - } else - value = properties[i+1]; - mProperties.put(properties[i], value); + for (int i = 0; i < properties.length; i++) { + String name = properties[i]; + String newValue; + int len; + if (name == null) { + Log.e(TAG, "Error:Adapter Property at index" + i + "is null"); + continue; + } + if (name.equals("Devices")) { + len = Integer.valueOf(properties[++i]); + if (len != 0) + newValue = ""; + else + newValue = null; + for (int j = 0; j < len; j++) { + newValue += properties[++i] + ","; + } + } else { + newValue = properties[++i]; + } + mProperties.put(name, newValue); } // Add adapter object path property. @@ -819,15 +832,27 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { propertyValues = new HashMap<String, String>(); } - for (int i = 0; i < properties.length; i+=2) { - String value = null; - if (propertyValues.containsKey(properties[i])) { - value = propertyValues.get(properties[i]); - value = value + ',' + properties[i+1]; + for (int i = 0; i < properties.length; i++) { + String name = properties[i]; + String newValue; + int len; + if (name == null) { + Log.e(TAG, "Error: Remote Device Property at index" + i + "is null"); + continue; + } + if (name.equals("UUIDs") || name.equals("Nodes")) { + len = Integer.valueOf(properties[++i]); + if (len != 0) + newValue = ""; + else + newValue = null; + for (int j = 0; j < len; j++) { + newValue += properties[++i] + ","; + } } else { - value = properties[i+1]; + newValue = properties[++i]; } - propertyValues.put(properties[i], value); + propertyValues.put(name, newValue); } mRemoteDeviceProperties.put(address, propertyValues); } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index ed66dce..38eb4d7 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -260,11 +260,15 @@ class BluetoothEventLoop { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mBluetoothService.setProperty(name, propValues[1]); } else if (name.equals("Devices")) { - String value = ""; - for (int i = 1; i < propValues.length; i++) { - value = value + propValues[i] + ','; + String value = null; + int len = Integer.valueOf(propValues[1]); + if (len > 0) { + value = ""; + for (int i = 2; i < propValues.length; i++) { + value = value + propValues[i] + ','; + } } - mBluetoothService.setProperty(name, value.equals("") ? null : value); + mBluetoothService.setProperty(name, value); } else if (name.equals("Powered")) { // bluetoothd has restarted, re-read all our properties. // Note: bluez only sends this property change when it restarts. @@ -303,12 +307,15 @@ class BluetoothEventLoop { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); } else if (name.equals("UUIDs")) { - String uuid = "" ; - for (int i = 1; i < propValues.length; i++) { - uuid = uuid + propValues[i] + ","; + String uuid = null; + int len = Integer.valueOf(propValues[1]); + if (len > 0) { + uuid = ""; + for (int i = 2; i < propValues.length; i++) { + uuid = uuid + propValues[i] + ","; + } } - mBluetoothService.setRemoteDeviceProperty(address, name, - uuid.equals("") ? null : uuid); + mBluetoothService.setRemoteDeviceProperty(address, name, uuid); } } diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl index 75c3b30..15f3876 100755 --- a/core/java/android/speech/tts/ITts.aidl +++ b/core/java/android/speech/tts/ITts.aidl @@ -33,6 +33,8 @@ interface ITts { void speak(in String text, in int queueMode, in String[] params);
+ void speakIpa(in String ipaText, in int queueMode, in String[] params);
+
boolean isSpeaking();
void stop();
@@ -41,9 +43,15 @@ interface ITts { void addSpeechFile(in String text, in String filename);
+ String[] getLanguage();
+
+ int isLanguageAvailable(in String language, in String country, in String variant);
+
void setLanguage(in String language, in String country, in String variant);
- boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
+ boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory); +
+ boolean synthesizeIpaToFile(in String ipaText, in String[] params, in String outputDirectory);
void playEarcon(in String earcon, in int queueMode, in String[] params);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index c064284..b245713 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -46,10 +46,6 @@ public class TextToSpeech { * Denotes a generic operation failure. */ public static final int TTS_ERROR = -1; - /** - * Denotes a failure due to a missing resource. - */ - public static final int TTS_ERROR_MISSING_RESOURCE = -2; /** * Queue mode where all entries in the playback queue (media to be played @@ -61,6 +57,37 @@ public class TextToSpeech { */ public static final int TTS_QUEUE_ADD = 1; + + /** + * Denotes the language is available exactly as specified by the locale + */ + public static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = 2; + + + /** + * Denotes the language is available for the language and country specified + * by the locale, but not the variant. + */ + public static final int TTS_LANG_COUNTRY_AVAILABLE = 1; + + + /** + * Denotes the language is available for the language by the locale, + * but not the country and variant. + */ + public static final int TTS_LANG_AVAILABLE = 0; + + /** + * Denotes the language data is missing. + */ + public static final int TTS_LANG_MISSING_DATA = -1; + + /** + * Denotes the language is not supported by the current TTS engine. + */ + public static final int TTS_LANG_NOT_SUPPORTED = -2; + + /** * Called when the TTS has initialized. * @@ -72,15 +99,6 @@ public class TextToSpeech { } /** - * Called when the TTS has finished speaking by itself (speaking - * finished without being canceled). - * - */ - public interface OnSpeechCompletedListener { - public void onSpeechCompleted(); - } - - /** * Internal constants for the TTS functionality * * {@hide} @@ -100,6 +118,16 @@ public class TextToSpeech { public static final int CHECK_VOICE_DATA_BAD_DATA = -1; public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3; + + // keys for the parameters passed with speak commands + public static final String TTS_KEY_PARAM_RATE = "rate"; + public static final String TTS_KEY_PARAM_LANGUAGE = "language"; + public static final String TTS_KEY_PARAM_COUNTRY = "country"; + public static final String TTS_KEY_PARAM_VARIANT = "variant"; + public static final int TTS_PARAM_POSITION_RATE = 0; + public static final int TTS_PARAM_POSITION_LANGUAGE = 2; + public static final int TTS_PARAM_POSITION_COUNTRY = 4; + public static final int TTS_PARAM_POSITION_VARIANT = 6; } /** @@ -112,11 +140,11 @@ public class TextToSpeech { private OnInitListener mInitListener = null; private boolean mStarted = false; private final Object mStartLock = new Object(); - private ITtsCallback mITtsCallback; - private OnSpeechCompletedListener mSpeechCompListener = null; - private final Object mSpeechCompListenerLock = new Object(); - - + private int mCachedRate = Engine.FALLBACK_TTS_DEFAULT_RATE; + private String mCachedLang = Engine.FALLBACK_TTS_DEFAULT_LANG; + private String mCachedCountry = Engine.FALLBACK_TTS_DEFAULT_COUNTRY; + private String mCachedVariant = Engine.FALLBACK_TTS_DEFAULT_VARIANT; + private String[] mCachedParams; /** * The constructor for the TTS. @@ -130,24 +158,23 @@ public class TextToSpeech { public TextToSpeech(Context context, OnInitListener listener) { mContext = context; mInitListener = listener; - initTts(); - } + mCachedParams = new String[2*4]; //4 parameters, store key and value + mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE; + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE; + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY; + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT; + updateCachedParamArray(); - public void setOnSpeechCompletedListener(final OnSpeechCompletedListener listener) { - synchronized(mSpeechCompListenerLock) { - mSpeechCompListener = listener; - } + initTts(); } - private boolean dataFilesCheck() { - // TODO #TTS# config manager will be in settings - Log.i("TTS_FIXME", "FIXME in Tts: config manager will be in settings"); - // TODO #TTS# implement checking of the correct installation of - // the data files. - - return true; + private void updateCachedParamArray() { + mCachedParams[Engine.TTS_PARAM_POSITION_RATE+1] = String.valueOf(mCachedRate); + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE+1] = mCachedLang; + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY+1] = mCachedCountry; + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT+1] = mCachedVariant; } @@ -159,34 +186,7 @@ public class TextToSpeech { public void onServiceConnected(ComponentName name, IBinder service) { synchronized(mStartLock) { mITts = ITts.Stub.asInterface(service); - try { - mITtsCallback = new ITtsCallback.Stub() { - public void markReached(String mark) - throws RemoteException { - // call the listener of that event, but not - // while locked. - OnSpeechCompletedListener listener = null; - synchronized(mSpeechCompListenerLock) { - listener = mSpeechCompListener; - } - if (listener != null) { - listener.onSpeechCompleted(); - } - } - }; - mITts.registerCallback(mITtsCallback); - - } catch (RemoteException e) { - initTts(); - return; - } - mStarted = true; - // The callback can become null if the Android OS decides to - // restart the TTS process as well as whatever is using it. - // In such cases, do nothing - the error handling from the - // speaking calls will kick in and force a proper restart of - // the TTS. if (mInitListener != null) { // TODO manage failures and missing resources mInitListener.onInit(TTS_SUCCESS); @@ -251,14 +251,17 @@ public class TextToSpeech { * * @param resourceId * Example: <b><code>R.raw.south_south_east</code></b> + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void addSpeech(String text, String packagename, int resourceId) { + public int addSpeech(String text, String packagename, int resourceId) { synchronized(mStartLock) { if (!mStarted) { - return; + return TTS_ERROR; } try { mITts.addSpeech(text, packagename, resourceId); + return TTS_SUCCESS; } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -272,6 +275,7 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -285,14 +289,17 @@ public class TextToSpeech { * @param filename * The full path to the sound file (for example: * "/sdcard/mysounds/hello.wav") + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void addSpeech(String text, String filename) { + public int addSpeech(String text, String filename) { synchronized (mStartLock) { if (!mStarted) { - return; + return TTS_ERROR; } try { mITts.addSpeechFile(text, filename); + return TTS_SUCCESS; } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -306,6 +313,7 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -324,17 +332,20 @@ public class TextToSpeech { * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. * @param params * The hashmap of speech parameters to be used. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void speak(String text, int queueMode, HashMap<String,String> params) + public int speak(String text, int queueMode, HashMap<String,String> params) { synchronized (mStartLock) { Log.i("TTS received: ", text); if (!mStarted) { - return; + return TTS_ERROR; } try { - // TODO support extra parameters, passing null for the moment - mITts.speak(text, queueMode, null); + // TODO support extra parameters, passing cache of current parameters for the moment + mITts.speak(text, queueMode, mCachedParams); + return TTS_SUCCESS; } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -348,6 +359,55 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; + } + } + + + /** + * Speaks the IPA string using the specified queuing strategy and speech + * parameters. Note that the speech parameters are not universally supported + * by all engines and will be treated as a hint. The TTS library will try to + * fulfill these parameters as much as possible, but there is no guarantee + * that the voice used will have the properties specified. + * + * @param ipaText + * The string of IPA text to be spoken. + * @param queueMode + * The queuing strategy to use. + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * @param params + * The hashmap of speech parameters to be used. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + * + * {@hide} + */ + public int speakIpa(String ipaText, int queueMode, HashMap<String,String> params) + { + synchronized (mStartLock) { + Log.i("TTS received: ", ipaText); + if (!mStarted) { + return TTS_ERROR; + } + try { + // TODO support extra parameters, passing cache of current parameters for the moment + mITts.speakIpa(ipaText, queueMode, mCachedParams); + return TTS_SUCCESS; + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; } } @@ -361,16 +421,19 @@ public class TextToSpeech { * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. * @param params * The hashmap of parameters to be used. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void playEarcon(String earcon, int queueMode, + public int playEarcon(String earcon, int queueMode, HashMap<String,String> params) { synchronized (mStartLock) { if (!mStarted) { - return; + return TTS_ERROR; } try { // TODO support extra parameters, passing null for the moment mITts.playEarcon(earcon, queueMode, null); + return TTS_SUCCESS; } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -384,12 +447,45 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } - - public void playSilence(long durationInMs, int queueMode) { - // TODO implement, already present in TTS service + /** + * Plays silence for the specified amount of time using the specified + * queue mode. + * + * @param durationInMs + * A long that indicates how long the silence should last. + * @param queueMode + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int playSilence(long durationInMs, int queueMode) { + synchronized (mStartLock) { + if (!mStarted) { + return TTS_ERROR; + } + try { + // TODO support extra parameters, passing cache of current parameters for the moment + mITts.playSilence(durationInMs, queueMode, mCachedParams); + return TTS_SUCCESS; + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } } @@ -425,14 +521,17 @@ public class TextToSpeech { /** * Stops speech from the TTS. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void stop() { + public int stop() { synchronized (mStartLock) { if (!mStarted) { - return; + return TTS_ERROR; } try { mITts.stop(); + return TTS_SUCCESS; } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -446,6 +545,7 @@ public class TextToSpeech { mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -462,21 +562,27 @@ public class TextToSpeech { * The speech rate for the TTS engine. 1 is the normal speed, * lower values slow down the speech (0.5 is half the normal speech rate), * greater values accelerate it (2 is twice the normal speech rate). + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void setSpeechRate(float speechRate) { + public int setSpeechRate(float speechRate) { synchronized (mStartLock) { if (!mStarted) { - return; + return TTS_SUCCESS; } try { if (speechRate > 0) { - mITts.setSpeechRate((int)(speechRate*100)); + mCachedRate = (int)(speechRate*100); + updateCachedParamArray(); + mITts.setSpeechRate(mCachedRate); + return TTS_SUCCESS; } } catch (RemoteException e) { // TTS died; restart it. mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -493,21 +599,25 @@ public class TextToSpeech { * The pitch for the TTS engine. 1 is the normal pitch, * lower values lower the tone of the synthesized voice, * greater values increase it. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void setPitch(float pitch) { + public int setPitch(float pitch) { synchronized (mStartLock) { if (!mStarted) { - return; + return TTS_ERROR; } try { if (pitch > 0) { mITts.setPitch((int)(pitch*100)); + return TTS_SUCCESS; } } catch (RemoteException e) { // TTS died; restart it. mStarted = false; initTts(); } + return TTS_ERROR; } } @@ -521,25 +631,86 @@ public class TextToSpeech { * * @param loc * The locale describing the language to be used. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public void setLanguage(Locale loc) { + public int setLanguage(Locale loc) { synchronized (mStartLock) { if (!mStarted) { - return; + return TTS_ERROR; } try { - mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant()); + mCachedLang = loc.getISO3Language(); + mCachedCountry = loc.getISO3Country(); + mCachedVariant = loc.getVariant(); + updateCachedParamArray(); + mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant); + return TTS_SUCCESS; } catch (RemoteException e) { // TTS died; restart it. mStarted = false; initTts(); } + return TTS_ERROR; } } /** - * Speaks the given text using the specified queueing mode and parameters. + * Returns a Locale instance describing the language currently being used by the TTS engine. + * @return language, country (if any) and variant (if any) used by the engine stored in a Locale + * instance, or null is the TTS engine has failed. + */ + public Locale getLanguage() { + synchronized (mStartLock) { + if (!mStarted) { + return null; + } + try { + String[] locStrings = mITts.getLanguage(); + if (locStrings.length == 3) { + return new Locale(locStrings[0], locStrings[1], locStrings[2]); + } else { + return null; + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return null; + } + } + + /** + * Checks if the specified language as represented by the Locale is available. + * + * @param loc + * The Locale describing the language to be used. + * + * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE, + * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE. + */ + public int isLanguageAvailable(Locale loc) { + synchronized (mStartLock) { + if (!mStarted) { + return TTS_LANG_NOT_SUPPORTED; + } + try { + return mITts.isLanguageAvailable(loc.getISO3Language(), loc.getISO3Country(), + loc.getVariant()); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_LANG_NOT_SUPPORTED; + } + } + + + /** + * Synthesizes the given text to a file using the specified parameters. * * @param text * The String of text that should be synthesized @@ -548,17 +719,20 @@ public class TextToSpeech { * @param filename * The string that gives the full output filename; it should be * something like "/sdcard/myappsounds/mysound.wav". - * @return A boolean that indicates if the synthesis succeeded + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. */ - public boolean synthesizeToFile(String text, HashMap<String,String> params, + public int synthesizeToFile(String text, HashMap<String,String> params, String filename) { synchronized (mStartLock) { if (!mStarted) { - return false; + return TTS_ERROR; } try { // TODO support extra parameters, passing null for the moment - return mITts.synthesizeToFile(text, null, filename); + if (mITts.synthesizeToFile(text, null, filename)){ + return TTS_SUCCESS; + } } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -572,9 +746,52 @@ public class TextToSpeech { mStarted = false; initTts(); } - return false; + return TTS_ERROR; } } + /** + * Synthesizes the given IPA text to a file using the specified parameters. + * + * @param text + * The String of text that should be synthesized + * @param params + * A hashmap of parameters. + * @param filename + * The string that gives the full output filename; it should be + * something like "/sdcard/myappsounds/mysound.wav". + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + * + * {@hide} + */ + public int synthesizeIpaToFile(String ipaText, + HashMap<String,String> params, String filename) { + synchronized (mStartLock) { + if (!mStarted) { + return TTS_ERROR; + } + try { + // TODO support extra parameters, passing null for the moment + if (mITts.synthesizeIpaToFile(ipaText, null, filename)){ + return TTS_SUCCESS; + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } + } + } diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 1a4eb69..9dd8ceb 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -25,7 +25,9 @@ import android.pim.DateException; import java.util.Calendar; import java.util.Date; +import java.util.Formatter; import java.util.GregorianCalendar; +import java.util.Locale; import java.util.TimeZone; /** @@ -1040,6 +1042,31 @@ public class DateUtils /** * Formats a date or a time range according to the local conventions. + * <p> + * Note that this is a convenience method. Using it involves creating an + * internal {@link java.util.Formatter} instance on-the-fly, which is + * somewhat costly in terms of memory and time. This is probably acceptable + * if you use the method only rarely, but if you rely on it for formatting a + * large number of dates, consider creating and reusing your own + * {@link java.util.Formatter} instance and use the version of + * {@link #formatDateRange(Context, long, long, int) formatDateRange} + * that takes a {@link java.util.Formatter}. + * + * @param context the context is required only if the time is shown + * @param startMillis the start time in UTC milliseconds + * @param endMillis the end time in UTC milliseconds + * @param flags a bit mask of options See + * {@link #formatDateRange(Context, long, long, int) formatDateRange} + * @return a string containing the formatted date/time range. + */ + public static String formatDateRange(Context context, long startMillis, + long endMillis, int flags) { + Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); + return formatDateRange(context, f, startMillis, endMillis, flags).toString(); + } + + /** + * Formats a date or a time range according to the local conventions. * * <p> * Example output strings (date formats in these examples are shown using @@ -1181,14 +1208,17 @@ public class DateUtils * instead of "December 31, 2008". * * @param context the context is required only if the time is shown + * @param formatter the Formatter used for formatting the date range. + * Note: be sure to call setLength(0) on StringBuilder passed to + * the Formatter constructor unless you want the results to accumulate. * @param startMillis the start time in UTC milliseconds * @param endMillis the end time in UTC milliseconds * @param flags a bit mask of options * - * @return a string containing the formatted date/time range. + * @return the formatter with the formatted date/time range appended to the string buffer. */ - public static String formatDateRange(Context context, long startMillis, - long endMillis, int flags) { + public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis, + long endMillis, int flags) { Resources res = Resources.getSystem(); boolean showTime = (flags & FORMAT_SHOW_TIME) != 0; boolean showWeekDay = (flags & FORMAT_SHOW_WEEKDAY) != 0; @@ -1423,8 +1453,7 @@ public class DateUtils if (noMonthDay && startMonthNum == endMonthNum) { // Example: "January, 2008" - String startDateString = startDate.format(defaultDateFormat); - return startDateString; + return formatter.format("%s", startDate.format(defaultDateFormat)); } if (startYear != endYear || noMonthDay) { @@ -1436,10 +1465,9 @@ public class DateUtils // The values that are used in a fullFormat string are specified // by position. - dateRange = String.format(fullFormat, + return formatter.format(fullFormat, startWeekDayString, startDateString, startTimeString, endWeekDayString, endDateString, endTimeString); - return dateRange; } // Get the month, day, and year strings for the start and end dates @@ -1476,12 +1504,11 @@ public class DateUtils // The values that are used in a fullFormat string are specified // by position. - dateRange = String.format(fullFormat, + return formatter.format(fullFormat, startWeekDayString, startMonthString, startMonthDayString, startYearString, startTimeString, endWeekDayString, endMonthString, endMonthDayString, endYearString, endTimeString); - return dateRange; } if (startDay != endDay) { @@ -1496,12 +1523,11 @@ public class DateUtils // The values that are used in a fullFormat string are specified // by position. - dateRange = String.format(fullFormat, + return formatter.format(fullFormat, startWeekDayString, startMonthString, startMonthDayString, startYearString, startTimeString, endWeekDayString, endMonthString, endMonthDayString, endYearString, endTimeString); - return dateRange; } // Same start and end day @@ -1522,6 +1548,7 @@ public class DateUtils } else { // Example: "10:00 - 11:00 am" String timeFormat = res.getString(com.android.internal.R.string.time1_time2); + // Don't use the user supplied Formatter because the result will pollute the buffer. timeString = String.format(timeFormat, startTimeString, endTimeString); } } @@ -1545,7 +1572,7 @@ public class DateUtils fullFormat = res.getString(com.android.internal.R.string.time_date); } else { // Example: "Oct 9" - return dateString; + return formatter.format("%s", dateString); } } } else if (showWeekDay) { @@ -1554,16 +1581,15 @@ public class DateUtils fullFormat = res.getString(com.android.internal.R.string.time_wday); } else { // Example: "Tue" - return startWeekDayString; + return formatter.format("%s", startWeekDayString); } } else if (showTime) { - return timeString; + return formatter.format("%s", timeString); } // The values that are used in a fullFormat string are specified // by position. - dateRange = String.format(fullFormat, timeString, startWeekDayString, dateString); - return dateRange; + return formatter.format(fullFormat, timeString, startWeekDayString, dateString); } /** diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index d89ada0..4179edb 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -103,40 +103,43 @@ public class DisplayMetrics { /** * Update the display metrics based on the compatibility info and orientation + * NOTE: DO NOT EXPOSE THIS API! It is introducing a circular dependency + * with the higher-level android.res package. * {@hide} */ - public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation) { + public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation, + int screenLayout) { int xOffset = 0; if (!compatibilityInfo.isConfiguredExpandable()) { // Note: this assume that configuration is updated before calling // updateMetrics method. - int defaultWidth; - int defaultHeight; - switch (orientation) { - case Configuration.ORIENTATION_LANDSCAPE: { - defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); - defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); - break; - } - case Configuration.ORIENTATION_PORTRAIT: - case Configuration.ORIENTATION_SQUARE: - default: { - defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); - defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); - break; - } - case Configuration.ORIENTATION_UNDEFINED: { - // don't change - return; - } - } - - if (defaultWidth == widthPixels && defaultHeight == heightPixels) { - // the screen size is same as expected size. make it expandable - compatibilityInfo.setExpandable(true); - } else { + if (screenLayout == Configuration.SCREENLAYOUT_LARGE) { + // This is a large screen device and the app is not + // compatible with large screens, to diddle it. + compatibilityInfo.setExpandable(false); - // adjust the size only when the device's screen is bigger. + // Figure out the compatibility width and height of the screen. + int defaultWidth; + int defaultHeight; + switch (orientation) { + case Configuration.ORIENTATION_LANDSCAPE: { + defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); + defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); + break; + } + case Configuration.ORIENTATION_PORTRAIT: + case Configuration.ORIENTATION_SQUARE: + default: { + defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); + defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); + break; + } + case Configuration.ORIENTATION_UNDEFINED: { + // don't change + return; + } + } + if (defaultWidth < widthPixels) { // content/window's x offset in original pixels xOffset = ((widthPixels - defaultWidth) / 2); @@ -145,6 +148,10 @@ public class DisplayMetrics { if (defaultHeight < heightPixels) { heightPixels = defaultHeight; } + + } else { + // the screen size is same as expected size. make it expandable + compatibilityInfo.setExpandable(true); } } compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3bfdde8..b3180ca 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1690,6 +1690,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private int[] mDrawableState = null; private SoftReference<Bitmap> mDrawingCache; + private SoftReference<Bitmap> mUnscaledDrawingCache; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -5783,28 +5784,52 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p> + * + * @return A non-scaled bitmap representing this view or null if cache is disabled. + * + * @see #getDrawingCache(boolean) + */ + public Bitmap getDrawingCache() { + return getDrawingCache(false); + } + + /** * <p>Returns the bitmap in which this view drawing is cached. The returned bitmap * is null when caching is disabled. If caching is enabled and the cache is not ready, * this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not * draw from the cache when the cache is enabled. To benefit from the cache, you must * request the drawing cache by calling this method and draw it on screen if the * returned bitmap is not null.</p> - * - * @return a bitmap representing this view or null if cache is disabled - * + * + * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.</p> + * + * @param autoScale Indicates whether the generated bitmap should be scaled based on + * the current density of the screen when the application is in compatibility + * mode. + * + * @return A bitmap representing this view or null if cache is disabled. + * * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() - * @see #buildDrawingCache() + * @see #buildDrawingCache(boolean) * @see #destroyDrawingCache() */ - public Bitmap getDrawingCache() { + public Bitmap getDrawingCache(boolean autoScale) { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null; } if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { - buildDrawingCache(); + buildDrawingCache(autoScale); } - return mDrawingCache == null ? null : mDrawingCache.get(); + return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); } /** @@ -5823,6 +5848,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (bitmap != null) bitmap.recycle(); mDrawingCache = null; } + if (mUnscaledDrawingCache != null) { + final Bitmap bitmap = mUnscaledDrawingCache.get(); + if (bitmap != null) bitmap.recycle(); + mUnscaledDrawingCache = null; + } } /** @@ -5850,18 +5880,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p> + * + * @see #buildDrawingCache(boolean) + */ + public void buildDrawingCache() { + buildDrawingCache(false); + } + + /** * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p> * * <p>If you call {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p> + * + * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.</p> * * @see #getDrawingCache() * @see #destroyDrawingCache() */ - public void buildDrawingCache() { - if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null || - mDrawingCache.get() == null) { + public void buildDrawingCache(boolean autoScale) { + if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ? + (mDrawingCache == null || mDrawingCache.get() == null) : + (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); @@ -5874,12 +5922,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility int height = mBottom - mTop; final AttachInfo attachInfo = mAttachInfo; - if (attachInfo != null) { - final boolean scalingRequired = attachInfo.mScalingRequired; - if (scalingRequired) { - width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); - height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); - } + final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired; + + if (autoScale && scalingRequired) { + width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); + height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); } final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; @@ -5894,7 +5941,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } boolean clear = true; - Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get(); + Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { @@ -5923,12 +5971,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility try { bitmap = Bitmap.createBitmap(width, height, quality); - mDrawingCache = new SoftReference<Bitmap>(bitmap); + if (autoScale) { + mDrawingCache = new SoftReference<Bitmap>(bitmap); + } else { + mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap); + } } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the // view hierarchy - mDrawingCache = null; + if (autoScale) { + mDrawingCache = null; + } else { + mUnscaledDrawingCache = null; + } return; } @@ -5940,13 +5996,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility canvas = attachInfo.mCanvas; if (canvas == null) { canvas = new Canvas(); - - // NOTE: This should have to happen only once since compatibility - // mode should not change at runtime - if (attachInfo.mScalingRequired) { - final float scale = attachInfo.mApplicationScale; - canvas.scale(scale, scale); - } } canvas.setBitmap(bitmap); // Temporarily clobber the cached Canvas in case one of our children @@ -5965,6 +6014,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility computeScroll(); final int restoreCount = canvas.save(); + + if (autoScale && scalingRequired) { + final float scale = attachInfo.mApplicationScale; + canvas.scale(scale, scale); + } + canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f803b5a..f7b7f02 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1166,7 +1166,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } @@ -1208,7 +1208,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager bindLayoutAnimation(child); if (cache) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } } @@ -1448,7 +1448,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager Bitmap cache = null; if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { - cache = child.getDrawingCache(); + cache = child.getDrawingCache(true); if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 9a0f467..7393737 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -340,10 +340,19 @@ public class BaseInputConnection implements InputConnection { } /** - * The default implementation does nothing. + * The default implementation turns this into the enter key. */ public boolean performEditorAction(int actionCode) { - return false; + long eventTime = SystemClock.uptimeMillis(); + sendKeyEvent(new KeyEvent(eventTime, eventTime, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + return true; } /** diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java index 3e1b602..de8f888 100644 --- a/core/java/android/webkit/CacheLoader.java +++ b/core/java/android/webkit/CacheLoader.java @@ -17,6 +17,7 @@ package android.webkit; import android.net.http.Headers; +import android.text.TextUtils; /** * This class is a concrete implementation of StreamLoader that uses a @@ -49,17 +50,22 @@ class CacheLoader extends StreamLoader { @Override protected void buildHeaders(Headers headers) { StringBuilder sb = new StringBuilder(mCacheResult.mimeType); - if (mCacheResult.encoding != null && - mCacheResult.encoding.length() > 0) { + if (!TextUtils.isEmpty(mCacheResult.encoding)) { sb.append(';'); sb.append(mCacheResult.encoding); } headers.setContentType(sb.toString()); - if (mCacheResult.location != null && - mCacheResult.location.length() > 0) { + if (!TextUtils.isEmpty(mCacheResult.location)) { headers.setLocation(mCacheResult.location); } - } + if (!TextUtils.isEmpty(mCacheResult.expiresString)) { + headers.setExpires(mCacheResult.expiresString); + } + + if (!TextUtils.isEmpty(mCacheResult.contentdisposition)) { + headers.setContentDisposition(mCacheResult.contentdisposition); + } + } } diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java index 4d471f7..9a02fbe 100644 --- a/core/java/android/webkit/CacheManager.java +++ b/core/java/android/webkit/CacheManager.java @@ -79,12 +79,14 @@ public final class CacheManager { int httpStatusCode; long contentLength; long expires; + String expiresString; String localPath; String lastModified; String etag; String mimeType; String location; String encoding; + String contentdisposition; // these fields are NOT saved to the database InputStream inStream; @@ -107,6 +109,13 @@ public final class CacheManager { return expires; } + /** + * @hide Pending API council approval + */ + public String getExpiresString() { + return expiresString; + } + public String getLastModified() { return lastModified; } @@ -127,6 +136,13 @@ public final class CacheManager { return encoding; } + /** + * @hide Pending API council approval + */ + public String getContentDisposition() { + return contentdisposition; + } + // For out-of-package access to the underlying streams. public InputStream getInputStream() { return inStream; @@ -603,21 +619,27 @@ public final class CacheManager { if (location != null) ret.location = location; ret.expires = -1; - String expires = headers.getExpires(); - if (expires != null) { + ret.expiresString = headers.getExpires(); + if (ret.expiresString != null) { try { - ret.expires = HttpDateTime.parse(expires); + ret.expires = HttpDateTime.parse(ret.expiresString); } catch (IllegalArgumentException ex) { // Take care of the special "-1" and "0" cases - if ("-1".equals(expires) || "0".equals(expires)) { + if ("-1".equals(ret.expiresString) + || "0".equals(ret.expiresString)) { // make it expired, but can be used for history navigation ret.expires = 0; } else { - Log.e(LOGTAG, "illegal expires: " + expires); + Log.e(LOGTAG, "illegal expires: " + ret.expiresString); } } } + String contentDisposition = headers.getContentDisposition(); + if (contentDisposition != null) { + ret.contentdisposition = contentDisposition; + } + String lastModified = headers.getLastModified(); if (lastModified != null) ret.lastModified = lastModified; diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index c407044..9a8c3c0 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -147,6 +147,16 @@ class CallbackProxy extends Handler { } /** + * Get the WebChromeClient. + * @return the current WebChromeClient instance. + * + *@hide pending API council approval. + */ + public WebChromeClient getWebChromeClient() { + return mWebChromeClient; + } + + /** * Set the client DownloadListener. * @param client An implementation of DownloadListener. */ diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java index 89cb606..8e25395 100644 --- a/core/java/android/webkit/DebugFlags.java +++ b/core/java/android/webkit/DebugFlags.java @@ -42,6 +42,7 @@ class DebugFlags { public static final boolean WEB_BACK_FORWARD_LIST = false; public static final boolean WEB_SETTINGS = false; public static final boolean WEB_SYNC_MANAGER = false; + public static final boolean WEB_TEXT_VIEW = false; public static final boolean WEB_VIEW = false; public static final boolean WEB_VIEW_CORE = false; diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index c33744e..f98c5d3 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -364,7 +364,7 @@ class FrameLoader { String cookie = CookieManager.getInstance().getCookie( mListener.getWebAddress()); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } } } diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java new file mode 100644 index 0000000..973cb01 --- /dev/null +++ b/core/java/android/webkit/HTML5VideoViewProxy.java @@ -0,0 +1,128 @@ +/* + * 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.webkit; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.View; +import android.widget.MediaController; +import android.widget.VideoView; + +import java.util.HashMap; + +/** + * <p>A View that displays Videos. Instances of this class + * are created on the WebCore thread. However, their code + * executes on the UI thread. Right now there is only one + * such view for fullscreen video rendering. + * + */ +class HTML5VideoViewProxy extends Handler { + // Logging tag. + private static final String LOGTAG = "HTML5VideoViewProxy"; + + // Message Ids + private static final int INIT = 100; + private static final int PLAY = 101; + + // The singleton instance. + private static HTML5VideoViewProxy sInstance; + // The VideoView driven via this proxy. + private VideoView mVideoView; + // The context object used to initialize the VideoView and the + // MediaController. + private Context mContext; + + /** + * Private constructor. + * @param context is the application context. + */ + private HTML5VideoViewProxy(Context context) { + // This handler is for the main (UI) thread. + super(Looper.getMainLooper()); + // Save the context object. + mContext = context; + // Send a message to the UI thread to create the VideoView. + // This need to be done on the UI thread, or else the + // event Handlers used by the VideoView and MediaController + // will be attached to the wrong thread. + Message message = obtainMessage(INIT); + sendMessage(message); + } + + @Override + public void handleMessage(Message msg) { + // This executes on the UI thread. + switch (msg.what) { + case INIT: + // Create the video view and set a default controller. + mVideoView = new VideoView(mContext); + mVideoView.setMediaController(new MediaController(mContext)); + break; + case PLAY: + // Check if the fullscreen video view is currently playing. + // If it is, ignore the message. + if (!mVideoView.isPlaying()) { + HashMap<String, Object> map = + (HashMap<String, Object>) msg.obj; + String url = (String) map.get("url"); + WebView webview = (WebView) map.get("webview"); + WebChromeClient client = webview.getWebChromeClient(); + if (client != null) { + mVideoView.setVideoURI(Uri.parse(url)); + mVideoView.start(); + client.onShowCustomView(mVideoView); + } + } + break; + } + } + + /** + * Play a video stream. + * @param url is the URL of the video stream. + * @param webview is the WebViewCore that is requesting the playback. + */ + public void play(String url, WebViewCore webviewCore) { + // We need to know the webview that is requesting the playback. + Message message = obtainMessage(PLAY); + HashMap<String, Object> map = new HashMap(); + map.put("url", url); + map.put("webview", webviewCore.getWebView()); + message.obj = map; + sendMessage(message); + } + + /** + * The factory for HTML5VideoViewProxy instances. Right now, + * it only produces a singleton. + * @param webViewCore is the WebViewCore that is requesting the proxy. + * + * @return the HTML5VideoViewProxy singleton. + */ + public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) { + if (sInstance == null) { + sInstance = new HTML5VideoViewProxy(webViewCore.getWebView().getContext()); + } + return sInstance; + } +} diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 08854f7..6f228a4 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -948,8 +948,7 @@ class LoadListener extends Handler implements EventHandler { // pass content-type content-length and content-encoding final int nativeResponse = nativeCreateResponse( mUrl, statusCode, mStatusText, - mMimeType, mContentLength, mEncoding, - mCacheResult == null ? 0 : mCacheResult.expires / 1000); + mMimeType, mContentLength, mEncoding); if (mHeaders != null) { mHeaders.getHeaders(new Headers.HeaderCallback() { public void header(String name, String value) { @@ -1460,12 +1459,11 @@ class LoadListener extends Handler implements EventHandler { * @param expectedLength An estimate of the content length or the length * given by the server. * @param encoding HTTP encoding. - * @param expireTime HTTP expires converted to seconds since the epoch. * @return The native response pointer. */ private native int nativeCreateResponse(String url, int statusCode, String statusText, String mimeType, long expectedLength, - String encoding, long expireTime); + String encoding); /** * Add a response header to the native object. diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 754b1d9..19e39ed 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -18,6 +18,7 @@ package android.webkit; import android.graphics.Bitmap; import android.os.Message; +import android.view.View; public class WebChromeClient { @@ -44,6 +45,23 @@ public class WebChromeClient { public void onReceivedIcon(WebView view, Bitmap icon) {} /** + * Notify the host application that the current page would + * like to show a custom View. + * @param view is the View object to be shown. + * + * @hide pending council approval + */ + public void onShowCustomView(View view) {} + + /** + * Notify the host application that the current page would + * like to hide its custom view. + * + * @hide pending council approval + */ + public void onHideCustomView() {} + + /** * Request the host application to create a new Webview. The host * application should handle placement of the new WebView in the view * system. The default behavior returns null. diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index f57c647..0ee3a60 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -69,7 +69,24 @@ public class WebSettings { } int value; } - + + /** + * Enum for specifying the WebView's desired density. + * FAR makes 100% looking like in 240dpi + * MEDIUM makes 100% looking like in 160dpi + * CLOSE makes 100% looking like in 120dpi + * @hide Pending API council approval + */ + public enum ZoomDensity { + FAR(150), // 240dpi + MEDIUM(100), // 160dpi + CLOSE(75); // 120dpi + ZoomDensity(int size) { + value = size; + } + int value; + } + /** * Default cache usage pattern Use with {@link #setCacheMode}. */ @@ -105,6 +122,8 @@ public class WebSettings { LOW } + // WebView associated with this WebSettings. + private WebView mWebView; // BrowserFrame used to access the native frame pointer. private BrowserFrame mBrowserFrame; // Flag to prevent multiple SYNC messages at one time. @@ -149,6 +168,7 @@ public class WebSettings { // Don't need to synchronize the get/set methods as they // are basic types, also none of these values are used in // native WebCore code. + private ZoomDensity mDefaultZoom = ZoomDensity.MEDIUM; private RenderPriority mRenderPriority = RenderPriority.NORMAL; private int mOverrideCacheMode = LOAD_DEFAULT; private boolean mSaveFormData = true; @@ -232,13 +252,13 @@ public class WebSettings { // User agent strings. private static final String DESKTOP_USERAGENT = - "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en)" - + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2" - + " Safari/525.20.1"; + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)" + + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0" + + " Safari/530.17"; private static final String IPHONE_USERAGENT = - "Mozilla/5.0 (iPhone; U; CPU iPhone 2_1 like Mac OS X; en)" - + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2" - + " Mobile/5F136 Safari/525.20.1"; + "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)" + + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0" + + " Mobile/7A341 Safari/528.16"; private static Locale sLocale; private static Object sLockForLocaleSettings; @@ -246,9 +266,10 @@ public class WebSettings { * Package constructor to prevent clients from creating a new settings * instance. */ - WebSettings(Context context) { + WebSettings(Context context, WebView webview) { mEventHandler = new EventHandler(); mContext = context; + mWebView = webview; mDefaultTextEncoding = context.getString(com.android.internal. R.string.default_text_encoding); @@ -456,6 +477,31 @@ public class WebSettings { } /** + * Set the default zoom density of the page. This should be called from UI + * thread. + * @param zoom A ZoomDensity value + * @see WebSettings.ZoomDensity + * @hide Pending API council approval + */ + public void setDefaultZoom(ZoomDensity zoom) { + if (mDefaultZoom != zoom) { + mDefaultZoom = zoom; + mWebView.updateDefaultZoomDensity(zoom.value); + } + } + + /** + * Get the default zoom density of the page. This should be called from UI + * thread. + * @return A ZoomDensity value + * @see WebSettings.ZoomDensity + * @hide Pending API council approval + */ + public ZoomDensity getDefaultZoom() { + return mDefaultZoom; + } + + /** * Enables using light touches to make a selection and activate mouseovers. */ public void setLightTouchEnabled(boolean enabled) { diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 4a8fa3c..25a20f2 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -17,20 +17,21 @@ package android.webkit; import android.content.Context; -import android.graphics.Rect; import android.text.Editable; import android.text.InputFilter; import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; import android.text.method.MovementMethod; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; +import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputConnection; import android.widget.AbsoluteLayout.LayoutParams; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; @@ -45,6 +46,8 @@ import java.util.ArrayList; */ /* package */ class WebTextView extends AutoCompleteTextView { + static final String LOGTAG = "webtextview"; + private WebView mWebView; private boolean mSingle; private int mWidthSpec; @@ -54,9 +57,6 @@ import java.util.ArrayList; // on the enter key. The method for blocking unmatched key ups prevents // the shift key from working properly. private boolean mGotEnterDown; - // mScrollToAccommodateCursor being set to false prevents us from scrolling - // the cursor on screen when using the trackball to select a textfield. - private boolean mScrollToAccommodateCursor; private int mMaxLength; // Keep track of the text before the change so we know whether we actually // need to send down the DOM events. @@ -82,6 +82,7 @@ import java.util.ArrayList; setImeOptions(EditorInfo.IME_ACTION_NONE); // Allow webkit's drawing to show through setWillNotDraw(true); + setCursorVisible(false); } @Override @@ -184,7 +185,6 @@ import java.util.ArrayList; if (maxedOut && !isArrowKey && keyCode != KeyEvent.KEYCODE_DEL) { if (oldEnd == oldStart) { // Return true so the key gets dropped. - mScrollToAccommodateCursor = true; return true; } else if (!oldText.equals(getText().toString())) { // FIXME: This makes the text work properly, but it @@ -198,7 +198,6 @@ import java.util.ArrayList; int newEnd = Selection.getSelectionEnd(span); mWebView.replaceTextfieldText(0, oldLength, span.toString(), newStart, newEnd); - mScrollToAccommodateCursor = true; return true; } } @@ -214,7 +213,6 @@ import java.util.ArrayList; sendDomEvent(event); } */ - mScrollToAccommodateCursor = true; return true; } // Ignore the key up event for newlines. This prevents @@ -265,9 +263,25 @@ import java.util.ArrayList; return ptr == mNodePointer; } + @Override public InputConnection onCreateInputConnection( + EditorInfo outAttrs) { + InputConnection connection = super.onCreateInputConnection(outAttrs); + if (mWebView != null) { + // Use the name of the textfield + the url. Use backslash as an + // arbitrary separator. + outAttrs.fieldName = mWebView.nativeFocusCandidateName() + "\\" + + mWebView.getUrl(); + } + return connection; + } + @Override protected void onSelectionChanged(int selStart, int selEnd) { if (mWebView != null) { + if (DebugFlags.WEB_TEXT_VIEW) { + Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart + + " selEnd=" + selEnd); + } mWebView.setSelection(selStart, selEnd); } } @@ -313,6 +327,10 @@ import java.util.ArrayList; } else { // This corrects the selection which may have been affected by the // trackball or auto-correct. + if (DebugFlags.WEB_TEXT_VIEW) { + Log.v(LOGTAG, "onTextChanged start=" + start + + " start + before=" + (start + before)); + } mWebView.setSelection(start, start + before); } if (!cannotUseKeyEvents) { @@ -348,12 +366,6 @@ import java.util.ArrayList; // Selection is changed in onSelectionChanged return true; } - // If the user is in a textfield, and the movement method is not - // handling the trackball events, it means they are at the end of the - // field and continuing to move the trackball. In this case, we should - // not scroll the cursor on screen bc the user may be attempting to - // scroll the page, possibly in the opposite direction of the cursor. - mScrollToAccommodateCursor = false; return false; } @@ -367,11 +379,6 @@ import java.util.ArrayList; getWindowToken(), 0); mWebView.removeView(this); mWebView.requestFocus(); - mScrollToAccommodateCursor = false; - } - - /* package */ void enableScrollOnScreen(boolean enable) { - mScrollToAccommodateCursor = enable; } /* package */ void bringIntoView() { @@ -380,14 +387,6 @@ import java.util.ArrayList; } } - @Override - public boolean requestRectangleOnScreen(Rect rectangle) { - if (mScrollToAccommodateCursor) { - return super.requestRectangleOnScreen(rectangle); - } - return false; - } - /** * Send the DOM events for the specified event. * @param event KeyEvent to be translated into a DOM event. @@ -542,6 +541,10 @@ import java.util.ArrayList; } else if (start > length) { start = length; } + if (DebugFlags.WEB_TEXT_VIEW) { + Log.v(LOGTAG, "setText start=" + start + + " end=" + end); + } Selection.setSelection(span, start, end); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 186e1d1..e061a4c 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -329,9 +329,18 @@ public class WebView extends AbsoluteLayout /** * The minimum elapsed time before sending another ACTION_MOVE event to - * WebViewCore + * WebViewCore. This really should be tuned for each type of the devices. + * For example in Google Map api test case, it takes Dream device at least + * 150ms to do a full cycle in the WebViewCore by processing a touch event, + * triggering the layout and drawing the picture. While the same process + * takes 60+ms on the current high speed device. If we make + * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent + * to WebViewCore queue and the real layout and draw events will be pushed + * to further, which slows down the refresh rate. Choose 50 to favor the + * current high speed devices. For Dream like devices, 100 is a better + * choice. Maybe make this in the buildspec later. */ - private static final int TOUCH_SENT_INTERVAL = 100; + private static final int TOUCH_SENT_INTERVAL = 50; /** * Helper class to get velocity for fling @@ -449,6 +458,8 @@ public class WebView extends AbsoluteLayout static final int WEBCORE_INITIALIZED_MSG_ID = 16; static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17; static final int DID_FIRST_LAYOUT_MSG_ID = 18; + static final int MOVE_OUT_OF_PLUGIN = 19; + static final int CLEAR_TEXT_ENTRY = 20; static final int UPDATE_CLIPBOARD = 22; static final int LONG_PRESS_CENTER = 23; @@ -456,6 +467,7 @@ public class WebView extends AbsoluteLayout static final int WEBCORE_NEED_TOUCH_EVENTS = 25; // obj=Rect in doc coordinates static final int INVAL_RECT_MSG_ID = 26; + static final int REQUEST_KEYBOARD = 27; static final String[] HandlerDebugString = { "REMEMBER_PASSWORD", // = 1; @@ -476,14 +488,15 @@ public class WebView extends AbsoluteLayout "WEBCORE_INITIALIZED_MSG_ID", // = 16; "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17; "DID_FIRST_LAYOUT_MSG_ID", // = 18; - "19", - "20", + "MOVE_OUT_OF_PLUGIN", // = 19; + "CLEAR_TEXT_ENTRY", // = 20; "21", // = 21; "UPDATE_CLIPBOARD", // = 22; "LONG_PRESS_CENTER", // = 23; "PREVENT_TOUCH_ID", // = 24; "WEBCORE_NEED_TOUCH_EVENTS", // = 25; - "INVAL_RECT_MSG_ID" // = 26; + "INVAL_RECT_MSG_ID", // = 26; + "REQUEST_KEYBOARD" // = 27; }; // width which view is considered to be fully zoomed out @@ -502,7 +515,7 @@ public class WebView extends AbsoluteLayout // default scale. Depending on the display density. static int DEFAULT_SCALE_PERCENT; - private float DEFAULT_SCALE; + private float mDefaultScale; // set to true temporarily while the zoom control is being dragged private boolean mPreviewZoomOnly = false; @@ -745,7 +758,7 @@ public class WebView extends AbsoluteLayout mNavSlop = (int) (16 * density); // density adjusted scale factors DEFAULT_SCALE_PERCENT = (int) (100 * density); - DEFAULT_SCALE = density; + mDefaultScale = density; mActualScale = density; mInvActualScale = 1 / density; DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; @@ -754,6 +767,23 @@ public class WebView extends AbsoluteLayout mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; } + /* package */void updateDefaultZoomDensity(int zoomDensity) { + final float density = getContext().getResources().getDisplayMetrics().density + * 100 / zoomDensity; + if (Math.abs(density - mDefaultScale) > 0.01) { + float scaleFactor = density / mDefaultScale; + // adjust the limits + mNavSlop = (int) (16 * density); + DEFAULT_SCALE_PERCENT = (int) (100 * density); + DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; + DEFAULT_MIN_ZOOM_SCALE = 0.25f * density; + mDefaultScale = density; + mMaxZoomScale *= scaleFactor; + mMinZoomScale *= scaleFactor; + setNewZoomScale(mActualScale * scaleFactor, false); + } + } + /* package */ boolean onSavePassword(String schemePlusHost, String username, String password, final Message resumeMsg) { boolean rVal = false; @@ -980,6 +1010,17 @@ public class WebView extends AbsoluteLayout } /** + * Sets JavaScript engine flags. + * + * @param flags JS engine flags in a String + * + * @hide pending API solidification + */ + public void setJsFlags(String flags) { + mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags); + } + + /** * Inform WebView of the network state. This is used to set * the javascript property window.navigator.isOnline and * generates the online/offline event as specified in HTML5, sec. 5.7.7 @@ -2318,6 +2359,16 @@ public class WebView extends AbsoluteLayout } /** + * Gets the chrome handler. + * @return the current WebChromeClient instance. + * + * @hide API council approval. + */ + public WebChromeClient getWebChromeClient() { + return mCallbackProxy.getWebChromeClient(); + } + + /** * Set the Picture listener. This is an interface used to receive * notifications of a new Picture. * @param listener An implementation of WebView.PictureListener. @@ -2454,6 +2505,14 @@ public class WebView extends AbsoluteLayout @Override public boolean performLongClick() { + if (mNativeClass != 0 && nativeCursorIsTextInput()) { + // Send the click so that the textfield is in focus + // FIXME: When we start respecting changes to the native textfield's + // selection, need to make sure that this does not change it. + mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(), + nativeCursorNodePointer()); + rebuildWebTextView(); + } if (inEditingMode()) { return mWebTextView.performLongClick(); } else { @@ -2964,24 +3023,37 @@ public class WebView extends AbsoluteLayout } // Called by JNI when a touch event puts a textfield into focus. - private void displaySoftKeyboard() { + private void displaySoftKeyboard(boolean isTextView) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mWebTextView, 0); - mWebTextView.enableScrollOnScreen(true); - // Now we need to fake a touch event to place the cursor where the - // user touched. - AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams) - mWebTextView.getLayoutParams(); - if (lp != null) { - // Take the last touch and adjust for the location of the - // WebTextView. - float x = mLastTouchX + (float) (mScrollX - lp.x); - float y = mLastTouchY + (float) (mScrollY - lp.y); - mWebTextView.fakeTouchEvent(x, y); + + if (isTextView) { + imm.showSoftInput(mWebTextView, 0); + // Now we need to fake a touch event to place the cursor where the + // user touched. + AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams) + mWebTextView.getLayoutParams(); + if (lp != null) { + // Take the last touch and adjust for the location of the + // WebTextView. + float x = mLastTouchX + (float) (mScrollX - lp.x); + float y = mLastTouchY + (float) (mScrollY - lp.y); + mWebTextView.fakeTouchEvent(x, y); + } + } + else { // used by plugins + imm.showSoftInput(this, 0); } } + // Called by WebKit to instruct the UI to hide the keyboard + private void hideSoftKeyboard() { + InputMethodManager imm = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + + imm.hideSoftInputFromWindow(this.getWindowToken(), 0); + } + /* * This method checks the current focus and cursor and potentially rebuilds * mWebTextView to have the appropriate properties, such as password, @@ -3017,8 +3089,7 @@ public class WebView extends AbsoluteLayout // should be in content coordinates. Rect bounds = nativeFocusCandidateNodeBounds(); if (!Rect.intersects(bounds, visibleRect)) { - // Node is not on screen, so do not bother. - return; + mWebTextView.bringIntoView(); } String text = nativeFocusCandidateText(); int nodePointer = nativeFocusCandidatePointer(); @@ -3072,6 +3143,9 @@ public class WebView extends AbsoluteLayout mWebTextView.setInPassword(nativeFocusCandidateIsPassword()); if (null == text) { mWebTextView.setText("", 0, 0); + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "rebuildWebTextView null == text"); + } } else { // Change to true to enable the old style behavior, where // entering a textfield/textarea always set the selection to the @@ -3086,8 +3160,14 @@ public class WebView extends AbsoluteLayout } else if (isTextField) { int length = text.length(); mWebTextView.setText(text, length, length); + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "rebuildWebTextView length=" + length); + } } else { mWebTextView.setText(text, 0, 0); + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "rebuildWebTextView !isTextField"); + } } } mWebTextView.requestFocus(); @@ -3129,7 +3209,7 @@ public class WebView extends AbsoluteLayout public boolean onKeyDown(int keyCode, KeyEvent event) { if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis() - + ", " + event); + + ", " + event + ", unicode=" + event.getUnicodeChar()); } if (mNativeClass == 0) { @@ -3175,7 +3255,7 @@ public class WebView extends AbsoluteLayout && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { // always handle the navigation keys in the UI thread switchOutDrawHistory(); - if (navHandledKey(keyCode, 1, false, event.getEventTime())) { + if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) { playSoundEffect(keyCodeToSoundsEffect(keyCode)); return true; } @@ -3235,15 +3315,23 @@ public class WebView extends AbsoluteLayout } } - if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) { + if (nativeCursorIsPlugin()) { + nativeUpdatePluginReceivesEvents(); + invalidate(); + } else if (nativeCursorIsTextInput()) { // This message will put the node in focus, for the DOM's notion - // of focus + // of focus, and make the focuscontroller active mWebViewCore.sendMessage(EventHub.CLICK); - if (nativeCursorIsTextInput()) { - // This will bring up the WebTextView and put it in focus, for - // our view system's notion of focus - rebuildWebTextView(); - // Now we need to pass the event to it + // This will bring up the WebTextView and put it in focus, for + // our view system's notion of focus + rebuildWebTextView(); + // Now we need to pass the event to it + return mWebTextView.onKeyDown(keyCode, event); + } else if (nativeHasFocusNode()) { + // In this case, the cursor is not on a text input, but the focus + // might be. Check it, and if so, hand over to the WebTextView. + rebuildWebTextView(); + if (inEditingMode()) { return mWebTextView.onKeyDown(keyCode, event); } } @@ -3264,7 +3352,7 @@ public class WebView extends AbsoluteLayout public boolean onKeyUp(int keyCode, KeyEvent event) { if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis() - + ", " + event); + + ", " + event + ", unicode=" + event.getUnicodeChar()); } if (mNativeClass == 0) { @@ -3409,9 +3497,7 @@ public class WebView extends AbsoluteLayout public void onChildViewRemoved(View p, View child) { if (child == this) { - if (inEditingMode()) { - clearTextEntry(); - } + clearTextEntry(); } } @@ -3435,6 +3521,9 @@ public class WebView extends AbsoluteLayout mDrawCursorRing = true; if (mNativeClass != 0) { nativeRecordButtons(true, false, true); + if (inEditingMode()) { + mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0); + } } } else { // If our window gained focus, but we do not have it, do not @@ -3477,7 +3566,7 @@ public class WebView extends AbsoluteLayout // Do not need to also check whether mWebViewCore is null, because // mNativeClass is only set if mWebViewCore is non null if (mNativeClass == 0) return; - mWebViewCore.sendMessage(EventHub.SET_INACTIVE); + mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0); } @Override @@ -3947,9 +4036,10 @@ public class WebView extends AbsoluteLayout if (ev.getAction() == MotionEvent.ACTION_DOWN) { mPrivateHandler.removeMessages(SWITCH_TO_CLICK); mTrackballDown = true; - if (mNativeClass != 0) { - nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true); + if (mNativeClass == 0) { + return false; } + nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true); if (time - mLastCursorTime <= TRACKBALL_TIMEOUT && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) { nativeSelectBestAt(mLastCursorBounds); @@ -4162,7 +4252,7 @@ public class WebView extends AbsoluteLayout + " mTrackballRemainsX=" + mTrackballRemainsX + " mTrackballRemainsY=" + mTrackballRemainsY); } - if (navHandledKey(selectKeyCode, count, false, time)) { + if (navHandledKey(selectKeyCode, count, false, time, false)) { playSoundEffect(keyCodeToSoundsEffect(selectKeyCode)); } mTrackballRemainsX = mTrackballRemainsY = 0; @@ -4238,8 +4328,8 @@ public class WebView extends AbsoluteLayout float oldScale = mActualScale; // snap to DEFAULT_SCALE if it is close - if (scale > (DEFAULT_SCALE - 0.05) && scale < (DEFAULT_SCALE + 0.05)) { - scale = DEFAULT_SCALE; + if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) { + scale = mDefaultScale; } setNewZoomScale(scale, false); @@ -4444,7 +4534,7 @@ public class WebView extends AbsoluteLayout return result; } if (mNativeClass != 0 && !nativeHasCursorNode()) { - navHandledKey(fakeKeyDirection, 1, true, 0); + navHandledKey(fakeKeyDirection, 1, true, 0, true); } } } @@ -4622,9 +4712,11 @@ public class WebView extends AbsoluteLayout break; } case SWITCH_TO_LONGPRESS: { - mTouchMode = TOUCH_DONE_MODE; - performLongClick(); - rebuildWebTextView(); + if (!mPreventDrag) { + mTouchMode = TOUCH_DONE_MODE; + performLongClick(); + rebuildWebTextView(); + } break; } case SWITCH_TO_CLICK: @@ -4639,13 +4731,15 @@ public class WebView extends AbsoluteLayout break; } nativeSetFollowedLink(true); - mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, - cursorData()); + nativeUpdatePluginReceivesEvents(); + WebViewCore.CursorData data = cursorData(); + mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data); playSoundEffect(SoundEffectConstants.CLICK); boolean isTextInput = nativeCursorIsTextInput(); if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading( nativeCursorText())) { - mWebViewCore.sendMessage(EventHub.CLICK); + mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame, + nativeCursorNodePointer()); } if (isTextInput) { rebuildWebTextView(); @@ -4769,7 +4863,7 @@ public class WebView extends AbsoluteLayout int initialScale = msg.arg1; int viewportWidth = msg.arg2; // start a new page with DEFAULT_SCALE zoom scale. - float scale = DEFAULT_SCALE; + float scale = mDefaultScale; if (mInitialScale > 0) { scale = mInitialScale / 100.0f; } else { @@ -4791,6 +4885,11 @@ public class WebView extends AbsoluteLayout } setNewZoomScale(scale, false); break; + case MOVE_OUT_OF_PLUGIN: + if (nativePluginEatsNavKey()) { + navHandledKey(msg.arg1, 1, false, 0, true); + } + break; case UPDATE_TEXT_ENTRY_MSG_ID: // this is sent after finishing resize in WebViewCore. Make // sure the text edit box is still on the screen. @@ -4799,6 +4898,9 @@ public class WebView extends AbsoluteLayout } rebuildWebTextView(); break; + case CLEAR_TEXT_ENTRY: + clearTextEntry(); + break; case INVAL_RECT_MSG_ID: { Rect r = (Rect)msg.obj; if (r == null) { @@ -4860,6 +4962,14 @@ public class WebView extends AbsoluteLayout } break; + case REQUEST_KEYBOARD: + if (msg.arg1 == 0) { + hideSoftKeyboard(); + } else { + displaySoftKeyboard(false); + } + break; + default: super.handleMessage(msg); break; @@ -5118,12 +5228,23 @@ public class WebView extends AbsoluteLayout new WebViewCore.CursorData(frame, node, x, y)); } - // called by JNI - private void sendMoveMouseIfLatest(boolean setFocusControllerInactive) { - if (setFocusControllerInactive) { + /* + * Send a mouse move event to the webcore thread. + * + * @param removeFocus Pass true if the "mouse" cursor is now over a node + * which wants key events, but it is not the focus. This + * will make the visual appear as though nothing is in + * focus. Remove the WebTextView, if present, and stop + * drawing the blinking caret. + * called by JNI + */ + private void sendMoveMouseIfLatest(boolean removeFocus) { + if (removeFocus) { + clearTextEntry(); setFocusControllerInactive(); } - mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST, cursorData()); + mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST, + cursorData()); } // called by JNI @@ -5176,11 +5297,21 @@ public class WebView extends AbsoluteLayout } // return true if the key was handled - private boolean navHandledKey(int keyCode, int count, boolean noScroll - , long time) { + private boolean navHandledKey(int keyCode, int count, boolean noScroll, + long time, boolean ignorePlugin) { if (mNativeClass == 0) { return false; } + if (ignorePlugin == false && nativePluginEatsNavKey()) { + KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN + , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0) + | (false ? KeyEvent.META_ALT_ON : 0) // FIXME + | (false ? KeyEvent.META_SYM_ON : 0) // FIXME + , 0, 0, 0); + mWebViewCore.sendMessage(EventHub.KEY_DOWN, event); + mWebViewCore.sendMessage(EventHub.KEY_UP, event); + return true; + } mLastCursorTime = time; mLastCursorBounds = nativeGetCursorRingBounds(); boolean keyHandled @@ -5263,6 +5394,7 @@ public class WebView extends AbsoluteLayout /* package */ native boolean nativeCursorMatchesFocus(); private native boolean nativeCursorIntersects(Rect visibleRect); private native boolean nativeCursorIsAnchor(); + private native boolean nativeCursorIsPlugin(); private native boolean nativeCursorIsTextInput(); private native Point nativeCursorPosition(); private native String nativeCursorText(); @@ -5286,7 +5418,7 @@ public class WebView extends AbsoluteLayout private native boolean nativeFocusCandidateIsTextField(); private native boolean nativeFocusCandidateIsTextInput(); private native int nativeFocusCandidateMaxLength(); - private native String nativeFocusCandidateName(); + /* package */ native String nativeFocusCandidateName(); private native Rect nativeFocusCandidateNodeBounds(); /* package */ native int nativeFocusCandidatePointer(); private native String nativeFocusCandidateText(); @@ -5306,6 +5438,7 @@ public class WebView extends AbsoluteLayout private native int nativeMoveGeneration(); private native void nativeMoveSelection(int x, int y, boolean extendSelection); + private native boolean nativePluginEatsNavKey(); // Like many other of our native methods, you must make sure that // mNativeClass is not null before calling this method. private native void nativeRecordButtons(boolean focused, @@ -5319,5 +5452,5 @@ public class WebView extends AbsoluteLayout // we always want to pass in our generation number. private native void nativeUpdateCachedTextfield(String updatedText, int generation); - + private native void nativeUpdatePluginReceivesEvents(); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 4ad9a1a..d8d9808 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -136,7 +136,7 @@ final class WebViewCore { // ready. mEventHub = new EventHub(); // Create a WebSettings object for maintaining all settings - mSettings = new WebSettings(mContext); + mSettings = new WebSettings(mContext, mWebView); // The WebIconDatabase needs to be initialized within the UI thread so // just request the instance here. WebIconDatabase.getInstance(); @@ -346,9 +346,10 @@ final class WebViewCore { private native void nativeSplitContent(); private native boolean nativeKey(int keyCode, int unichar, - int repeatCount, boolean isShift, boolean isAlt, boolean isDown); + int repeatCount, boolean isShift, boolean isAlt, boolean isSym, + boolean isDown); - private native boolean nativeClick(); + private native void nativeClick(int framePtr, int nodePtr); private native void nativeSendListBoxChoices(boolean[] choices, int size); @@ -375,7 +376,7 @@ final class WebViewCore { String currentText, int keyCode, int keyValue, boolean down, boolean cap, boolean fn, boolean sym); - private native void nativeSetFocusControllerInactive(); + private native void nativeSetFocusControllerActive(boolean active); private native void nativeSaveDocumentState(int frame); @@ -406,6 +407,8 @@ final class WebViewCore { private native void nativeDumpNavTree(); + private native void nativeSetJsFlags(String flags); + /** * Delete text from start to end in the focused textfield. If there is no * focus, or if start == end, silently fail. If start and end are out of @@ -610,7 +613,7 @@ final class WebViewCore { "LOAD_DATA", // = 139; "TOUCH_UP", // = 140; "TOUCH_EVENT", // = 141; - "SET_INACTIVE", // = 142; + "SET_ACTIVE", // = 142; "ON_PAUSE", // = 143 "ON_RESUME", // = 144 "FREE_MEMORY", // = 145 @@ -668,7 +671,7 @@ final class WebViewCore { // Used to tell the focus controller not to draw the blinking cursor, // based on whether the WebView has focus and whether the WebView's // cursor matches the webpage's focus. - static final int SET_INACTIVE = 142; + static final int SET_ACTIVE = 142; // lifecycle activities for just this DOM (unlike pauseTimers, which // is global) @@ -688,6 +691,8 @@ final class WebViewCore { static final int DUMP_RENDERTREE = 171; static final int DUMP_NAVTREE = 172; + static final int SET_JS_FLAGS = 173; + // private message ids private static final int DESTROY = 200; @@ -719,9 +724,11 @@ final class WebViewCore { @Override public void handleMessage(Message msg) { if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, msg.what < LOAD_URL || msg.what + Log.v(LOGTAG, (msg.what < LOAD_URL || msg.what > FREE_MEMORY ? Integer.toString(msg.what) - : HandlerDebugString[msg.what - LOAD_URL]); + : HandlerDebugString[msg.what - LOAD_URL]) + + " arg1=" + msg.arg1 + " arg2=" + msg.arg2 + + " obj=" + msg.obj); } switch (msg.what) { case WEBKIT_DRAW: @@ -803,7 +810,7 @@ final class WebViewCore { break; case CLICK: - nativeClick(); + nativeClick(msg.arg1, msg.arg2); break; case VIEW_SIZE_CHANGED: @@ -947,8 +954,8 @@ final class WebViewCore { break; } - case SET_INACTIVE: - nativeSetFocusControllerInactive(); + case SET_ACTIVE: + nativeSetFocusControllerActive(msg.arg1 == 1); break; case ADD_JS_INTERFACE: @@ -1054,6 +1061,10 @@ final class WebViewCore { nativeDumpNavTree(); break; + case SET_JS_FLAGS: + nativeSetJsFlags((String)msg.obj); + break; + case SYNC_SCROLL: mWebkitScrollX = msg.arg1; mWebkitScrollY = msg.arg2; @@ -1260,7 +1271,19 @@ final class WebViewCore { int keyCode = evt.getKeyCode(); if (!nativeKey(keyCode, evt.getUnicodeChar(), evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(), + evt.isSymPressed(), isDown) && keyCode != KeyEvent.KEYCODE_ENTER) { + if (keyCode >= KeyEvent.KEYCODE_DPAD_UP + && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { + if (DebugFlags.WEB_VIEW_CORE) { + Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode); + } + if (mWebView != null && evt.isDown()) { + Message.obtain(mWebView.mPrivateHandler, + WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget(); + } + return; + } // bubble up the event handling // but do not bubble up the ENTER key, which would open the search // bar without any text. @@ -1618,6 +1641,20 @@ final class WebViewCore { // set the viewport settings from WebKit setViewportSettingsFromNative(); + // adjust the default scale to match the density + if (WebView.DEFAULT_SCALE_PERCENT != 100) { + float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f; + if (mViewportInitialScale > 0) { + mViewportInitialScale *= adjust; + } + if (mViewportMinimumScale > 0) { + mViewportMinimumScale *= adjust; + } + if (mViewportMaximumScale > 0) { + mViewportMaximumScale *= adjust; + } + } + // infer the values if they are not defined. if (mViewportWidth == 0) { if (mViewportInitialScale == 0) { @@ -1722,6 +1759,13 @@ final class WebViewCore { } } + // called by JNI + private void clearTextEntry() { + if (mWebView == null) return; + Message.obtain(mWebView.mPrivateHandler, + WebView.CLEAR_TEXT_ENTRY).sendToTarget(); + } + // these must be in document space (i.e. not scaled/zoomed). private native void nativeSetScrollOffset(int gen, int dx, int dy); @@ -1744,6 +1788,15 @@ final class WebViewCore { } + // called by JNI + private void requestKeyboard(boolean showKeyboard) { + if (mWebView != null) { + Message.obtain(mWebView.mPrivateHandler, + WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) + .sendToTarget(); + } + } + private native void nativePause(); private native void nativeResume(); private native void nativeFreeMemory(); diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 1004e30..4e76254 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -48,7 +48,9 @@ public class WebViewDatabase { // 6 -> 7 Change cache localPath from int to String // 7 -> 8 Move cache to its own db // 8 -> 9 Store both scheme and host when storing passwords - private static final int CACHE_DATABASE_VERSION = 1; + private static final int CACHE_DATABASE_VERSION = 3; + // 1 -> 2 Add expires String + // 2 -> 3 Add content-disposition private static WebViewDatabase mInstance = null; @@ -107,6 +109,8 @@ public class WebViewDatabase { private static final String CACHE_EXPIRES_COL = "expires"; + private static final String CACHE_EXPIRES_STRING_COL = "expiresstring"; + private static final String CACHE_MIMETYPE_COL = "mimetype"; private static final String CACHE_ENCODING_COL = "encoding"; @@ -117,6 +121,8 @@ public class WebViewDatabase { private static final String CACHE_CONTENTLENGTH_COL = "contentlength"; + private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition"; + // column id strings for "password" table private static final String PASSWORD_HOST_COL = "host"; @@ -150,11 +156,13 @@ public class WebViewDatabase { private static int mCacheLastModifyColIndex; private static int mCacheETagColIndex; private static int mCacheExpiresColIndex; + private static int mCacheExpiresStringColIndex; private static int mCacheMimeTypeColIndex; private static int mCacheEncodingColIndex; private static int mCacheHttpStatusColIndex; private static int mCacheLocationColIndex; private static int mCacheContentLengthColIndex; + private static int mCacheContentDispositionColIndex; private static int mCacheTransactionRefcount; @@ -220,6 +228,8 @@ public class WebViewDatabase { .getColumnIndex(CACHE_ETAG_COL); mCacheExpiresColIndex = mCacheInserter .getColumnIndex(CACHE_EXPIRES_COL); + mCacheExpiresStringColIndex = mCacheInserter + .getColumnIndex(CACHE_EXPIRES_STRING_COL); mCacheMimeTypeColIndex = mCacheInserter .getColumnIndex(CACHE_MIMETYPE_COL); mCacheEncodingColIndex = mCacheInserter @@ -230,6 +240,8 @@ public class WebViewDatabase { .getColumnIndex(CACHE_LOCATION_COL); mCacheContentLengthColIndex = mCacheInserter .getColumnIndex(CACHE_CONTENTLENGTH_COL); + mCacheContentDispositionColIndex = mCacheInserter + .getColumnIndex(CACHE_CONTENTDISPOSITION_COL); } } @@ -320,11 +332,12 @@ public class WebViewDatabase { + " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, " + CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL + " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, " + + CACHE_EXPIRES_STRING_COL + " TEXT, " + CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL + " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, " + CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL - + " INTEGER, " + " UNIQUE (" + CACHE_URL_COL - + ") ON CONFLICT REPLACE);"); + + " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, " + + " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);"); mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache (" + CACHE_URL_COL + ")"); } @@ -537,8 +550,8 @@ public class WebViewDatabase { } Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, " - + "mimetype, encoding, httpstatus, location, contentlength " - + "FROM cache WHERE url = ?", + + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, " + + "contentdisposition FROM cache WHERE url = ?", new String[] { url }); try { @@ -548,11 +561,13 @@ public class WebViewDatabase { ret.lastModified = cursor.getString(1); ret.etag = cursor.getString(2); ret.expires = cursor.getLong(3); - ret.mimeType = cursor.getString(4); - ret.encoding = cursor.getString(5); - ret.httpStatusCode = cursor.getInt(6); - ret.location = cursor.getString(7); - ret.contentLength = cursor.getLong(8); + ret.expiresString = cursor.getString(4); + ret.mimeType = cursor.getString(5); + ret.encoding = cursor.getString(6); + ret.httpStatusCode = cursor.getInt(7); + ret.location = cursor.getString(8); + ret.contentLength = cursor.getLong(9); + ret.contentdisposition = cursor.getString(10); return ret; } } finally { @@ -591,11 +606,14 @@ public class WebViewDatabase { mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified); mCacheInserter.bind(mCacheETagColIndex, c.etag); mCacheInserter.bind(mCacheExpiresColIndex, c.expires); + mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString); mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType); mCacheInserter.bind(mCacheEncodingColIndex, c.encoding); mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode); mCacheInserter.bind(mCacheLocationColIndex, c.location); mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength); + mCacheInserter.bind(mCacheContentDispositionColIndex, + c.contentdisposition); mCacheInserter.execute(); } diff --git a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java index 74d27ed..b3d7f69 100644 --- a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java +++ b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java @@ -38,7 +38,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; import java.lang.StringBuilder; -import java.util.Date; import java.util.Map; import java.util.HashMap; import java.util.Iterator; @@ -57,7 +56,6 @@ import org.apache.http.impl.client.AbstractHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.conn.ssl.StrictHostnameVerifier; -import org.apache.http.impl.cookie.DateUtils; import org.apache.http.util.CharArrayBuffer; import java.util.concurrent.locks.Condition; @@ -863,12 +861,9 @@ public final class ApacheHttpRequestAndroid { mResponseHeaders = new HashMap<String, String[]>(); String contentLength = Long.toString(cacheResult.getContentLength()); setResponseHeader(KEY_CONTENT_LENGTH, contentLength); - long expires = cacheResult.getExpires(); - if (expires >= 0) { - // "Expires" header is valid and finite. Milliseconds since 1970 - // epoch, formatted as RFC-1123. - String expiresString = DateUtils.formatDate(new Date(expires)); - setResponseHeader(KEY_EXPIRES, expiresString); + String expires = cacheResult.getExpiresString(); + if (expires != null) { + setResponseHeader(KEY_EXPIRES, expires); } String lastModified = cacheResult.getLastModified(); if (lastModified != null) { diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 96cc2fd..ba47df5 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -1185,7 +1185,11 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe int position, long id) { if (position != -1) { - mDropDownList.mListSelectionHidden = false; + DropDownListView dropDownList = mDropDownList; + + if (dropDownList != null) { + dropDownList.mListSelectionHidden = false; + } } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 441414a..2c9e71e 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.Animatable; import android.graphics.drawable.shapes.RoundRectShape; import android.graphics.drawable.shapes.Shape; import android.util.AttributeSet; @@ -683,7 +684,7 @@ public class ProgressBar extends View { return; } - if (mIndeterminateDrawable instanceof AnimationDrawable) { + if (mIndeterminateDrawable instanceof Animatable) { mShouldStartAnimationDrawable = true; mAnimation = null; } else { @@ -708,8 +709,8 @@ public class ProgressBar extends View { void stopAnimation() { mAnimation = null; mTransformation = null; - if (mIndeterminateDrawable instanceof AnimationDrawable) { - ((AnimationDrawable) mIndeterminateDrawable).stop(); + if (mIndeterminateDrawable instanceof Animatable) { + ((Animatable) mIndeterminateDrawable).stop(); mShouldStartAnimationDrawable = false; } } @@ -818,8 +819,8 @@ public class ProgressBar extends View { } d.draw(canvas); canvas.restore(); - if (mShouldStartAnimationDrawable && d instanceof AnimationDrawable) { - ((AnimationDrawable) d).start(); + if (mShouldStartAnimationDrawable && d instanceof Animatable) { + ((Animatable) d).start(); mShouldStartAnimationDrawable = false; } } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 955475e..e62dda5 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -41,6 +41,7 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.LinkedList; import java.util.HashSet; +import java.util.ArrayList; /** * A Layout where the positions of the children can be described in relation to each other or to the @@ -339,11 +340,17 @@ public class RelativeLayout extends ViewGroup { int right = Integer.MIN_VALUE; int bottom = Integer.MIN_VALUE; + boolean offsetHorizontalAxis = false; + boolean offsetVerticalAxis = false; + if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } - View[] views = mSortedVerticalChildren; + final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY; + final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY; + + View[] views = mSortedHorizontalChildren; int count = views.length; for (int i = 0; i < count; i++) { View child = views[i]; @@ -351,13 +358,16 @@ public class RelativeLayout extends ViewGroup { LayoutParams params = (LayoutParams) child.getLayoutParams(); applyHorizontalSizeRules(params, myWidth); - measureChildHorizontal(child, params, myWidth); - positionChildHorizontal(child, params, myWidth); + measureChildHorizontal(child, params, myWidth, myHeight); + if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { + offsetHorizontalAxis = true; + } } } - views = mSortedHorizontalChildren; + views = mSortedVerticalChildren; count = views.length; + for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { @@ -365,12 +375,15 @@ public class RelativeLayout extends ViewGroup { applyVerticalSizeRules(params, myHeight); measureChild(child, params, myWidth, myHeight); - positionChildVertical(child, params, myHeight); + if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) { + offsetVerticalAxis = true; + } - if (widthMode != MeasureSpec.EXACTLY) { + if (isWrapContentWidth) { width = Math.max(width, params.mRight); } - if (heightMode != MeasureSpec.EXACTLY) { + + if (isWrapContentHeight) { height = Math.max(height, params.mBottom); } @@ -406,7 +419,7 @@ public class RelativeLayout extends ViewGroup { } } - if (widthMode != MeasureSpec.EXACTLY) { + if (isWrapContentWidth) { // Width already has left padding in it since it was calculated by looking at // the right of each child view width += mPaddingRight; @@ -417,8 +430,22 @@ public class RelativeLayout extends ViewGroup { width = Math.max(width, getSuggestedMinimumWidth()); width = resolveSize(width, widthMeasureSpec); + + if (offsetHorizontalAxis) { + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + final int[] rules = params.getRules(); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { + centerHorizontal(child, params, width); + } + } + } + } } - if (heightMode != MeasureSpec.EXACTLY) { + + if (isWrapContentHeight) { // Height already has top padding in it since it was calculated by looking at // the bottom of each child view height += mPaddingBottom; @@ -429,6 +456,19 @@ public class RelativeLayout extends ViewGroup { height = Math.max(height, getSuggestedMinimumHeight()); height = resolveSize(height, heightMeasureSpec); + + if (offsetVerticalAxis) { + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + final int[] rules = params.getRules(); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { + centerVertical(child, params, height); + } + } + } + } } if (horizontalGravity || verticalGravity) { @@ -510,13 +550,18 @@ public class RelativeLayout extends ViewGroup { child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } - private void measureChildHorizontal(View child, LayoutParams params, int myWidth) { + private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) { int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight, params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight, myWidth); - int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + int childHeightMeasureSpec; + if (params.width == LayoutParams.FILL_PARENT) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); + } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } @@ -599,7 +644,9 @@ public class RelativeLayout extends ViewGroup { return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); } - private void positionChildHorizontal(View child, LayoutParams params, int myWidth) { + private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth, + boolean wrapContent) { + int[] rules = params.getRules(); if (params.mLeft < 0 && params.mRight >= 0) { @@ -610,16 +657,25 @@ public class RelativeLayout extends ViewGroup { params.mRight = params.mLeft + child.getMeasuredWidth(); } else if (params.mLeft < 0 && params.mRight < 0) { // Both left and right vary - if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) { - centerHorizontal(child, params, myWidth); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { + if (!wrapContent) { + centerHorizontal(child, params, myWidth); + } else { + params.mLeft = mPaddingLeft + params.leftMargin; + params.mRight = params.mLeft + child.getMeasuredWidth(); + } + return true; } else { params.mLeft = mPaddingLeft + params.leftMargin; params.mRight = params.mLeft + child.getMeasuredWidth(); } } + return false; } - private void positionChildVertical(View child, LayoutParams params, int myHeight) { + private boolean positionChildVertical(View child, LayoutParams params, int myHeight, + boolean wrapContent) { + int[] rules = params.getRules(); if (params.mTop < 0 && params.mBottom >= 0) { @@ -630,13 +686,20 @@ public class RelativeLayout extends ViewGroup { params.mBottom = params.mTop + child.getMeasuredHeight(); } else if (params.mTop < 0 && params.mBottom < 0) { // Both top and bottom vary - if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) { - centerVertical(child, params, myHeight); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { + if (!wrapContent) { + centerVertical(child, params, myHeight); + } else { + params.mTop = mPaddingTop + params.topMargin; + params.mBottom = params.mTop + child.getMeasuredHeight(); + } + return true; } else { params.mTop = mPaddingTop + params.topMargin; params.mBottom = params.mTop + child.getMeasuredHeight(); } } + return false; } private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) { @@ -766,14 +829,14 @@ public class RelativeLayout extends ViewGroup { private View getRelatedView(int[] rules, int relation) { int id = rules[relation]; if (id != 0) { - DependencyGraph.Node node = mGraph.mNodes.get(id); + DependencyGraph.Node node = mGraph.mKeyNodes.get(id); if (node == null) return null; View v = node.view; // Find the first non-GONE view up the chain while (v.getVisibility() == View.GONE) { rules = ((LayoutParams) v.getLayoutParams()).getRules(); - node = mGraph.mNodes.get((rules[relation])); + node = mGraph.mKeyNodes.get((rules[relation])); if (node == null) return null; v = node.view; } @@ -1109,10 +1172,15 @@ public class RelativeLayout extends ViewGroup { private static class DependencyGraph { /** + * List of all views in the graph. + */ + private ArrayList<Node> mNodes = new ArrayList<Node>(); + + /** * List of nodes in the graph. Each node is identified by its * view id (see View#getId()). */ - private SparseArray<Node> mNodes = new SparseArray<Node>(); + private SparseArray<Node> mKeyNodes = new SparseArray<Node>(); /** * Temporary data structure used to build the list of roots @@ -1124,14 +1192,15 @@ public class RelativeLayout extends ViewGroup { * Clears the graph. */ void clear() { - final SparseArray<Node> nodes = mNodes; + final ArrayList<Node> nodes = mNodes; final int count = nodes.size(); for (int i = 0; i < count; i++) { - nodes.valueAt(i).release(); + nodes.get(i).release(); } nodes.clear(); + mKeyNodes.clear(); mRoots.clear(); } @@ -1141,7 +1210,14 @@ public class RelativeLayout extends ViewGroup { * @param view The view to be added as a node to the graph. */ void add(View view) { - mNodes.put(view.getId(), Node.acquire(view)); + final int id = view.getId(); + final Node node = Node.acquire(view); + + if (id != View.NO_ID) { + mKeyNodes.put(id, node); + } + + mNodes.add(node); } /** @@ -1192,20 +1268,21 @@ public class RelativeLayout extends ViewGroup { * @return A list of node, each being a root of the graph */ private LinkedList<Node> findRoots(int[] rulesFilter) { - final SparseArray<Node> nodes = mNodes; + final SparseArray<Node> keyNodes = mKeyNodes; + final ArrayList<Node> nodes = mNodes; final int count = nodes.size(); // Find roots can be invoked several times, so make sure to clear // all dependents and dependencies before running the algorithm for (int i = 0; i < count; i++) { - final Node node = nodes.valueAt(i); + final Node node = nodes.get(i); node.dependents.clear(); node.dependencies.clear(); } // Builds up the dependents and dependencies for each node of the graph for (int i = 0; i < count; i++) { - final Node node = nodes.valueAt(i); + final Node node = nodes.get(i); final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); final int[] rules = layoutParams.mRules; @@ -1217,11 +1294,14 @@ public class RelativeLayout extends ViewGroup { final int rule = rules[rulesFilter[j]]; if (rule > 0) { // The node this node depends on - final Node dependency = nodes.get(rule); + final Node dependency = keyNodes.get(rule); if (dependency == node) { throw new IllegalStateException("A view cannot have a dependency" + " on itself"); } + if (dependency == null) { + continue; + } // Add the current node as a dependent dependency.dependents.add(node); // Add a dependency to the current node @@ -1235,7 +1315,7 @@ public class RelativeLayout extends ViewGroup { // Finds all the roots in the graph: all nodes with no dependencies for (int i = 0; i < count; i++) { - final Node node = nodes.valueAt(i); + final Node node = nodes.get(i); if (node.dependencies.size() == 0) roots.add(node); } @@ -1323,7 +1403,9 @@ public class RelativeLayout extends ViewGroup { /* * START POOL IMPLEMENTATION */ - private static final int POOL_LIMIT = 12; + // The pool is static, so all nodes instances are shared across + // activities, that's why we give it a rather high limit + private static final int POOL_LIMIT = 100; private static final Pool<Node> sPool = Pools.synchronizedPool( Pools.finitePool(new PoolableManager<Node>() { public Node newInstance() { diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index ec63528..4bef265 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -41,6 +41,20 @@ interface IBackupTransport { - adb: close the file */ /** + * Ask the transport where, on local device storage, to keep backup state blobs. + * This is per-transport so that mock transports used for testing can coexist with + * "live" backup services without interfering with the live bookkeeping. The + * returned string should be a name that is expected to be unambiguous among all + * available backup transports; the name of the class implementing the transport + * is a good choice. + * + * @return A unique name, suitable for use as a file or directory name, that the + * Backup Manager could use to disambiguate state files associated with + * different backup transports. + */ + String transportDirName(); + + /** * Verify that this is a suitable time for a backup pass. This should return zero * if a backup is reasonable right now, some positive value otherwise. This method * will be called outside of the {@link #startSession}/{@link #endSession} pair. diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 0fbbb3f..c5d9d40 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -30,6 +30,9 @@ public class LocalTransport extends IBackupTransport.Stub { private static final String TAG = "LocalTransport"; private static final boolean DEBUG = true; + private static final String TRANSPORT_DIR_NAME + = "com.android.internal.backup.LocalTransport"; + private Context mContext; private PackageManager mPackageManager; private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); @@ -43,6 +46,11 @@ public class LocalTransport extends IBackupTransport.Stub { mPackageManager = context.getPackageManager(); } + + public String transportDirName() throws RemoteException { + return TRANSPORT_DIR_NAME; + } + public long requestBackupTime() throws RemoteException { // any time is a good time for local backup return 0; diff --git a/core/java/com/android/internal/backup/SystemBackupAgent.java b/core/java/com/android/internal/backup/SystemBackupAgent.java new file mode 100644 index 0000000..6b396d7 --- /dev/null +++ b/core/java/com/android/internal/backup/SystemBackupAgent.java @@ -0,0 +1,35 @@ +/* + * 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 com.android.internal.backup; + +import android.backup.AbsoluteFileBackupHelper; +import android.backup.BackupHelperAgent; + +/** + * Backup agent for various system-managed data + */ +public class SystemBackupAgent extends BackupHelperAgent { + // the set of files that we back up whole, as absolute paths + String[] mFiles = { + /* WallpaperService.WALLPAPER_FILE */ + "/data/data/com.android.settings/files/wallpaper", + }; + + public void onCreate() { + addHelper("system_files", new AbsoluteFileBackupHelper(this, mFiles)); + } +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index fc4a9c4..a03802d 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -2836,14 +2836,12 @@ public final class BatteryStatsImpl extends BatteryStats { * @param name process name * @return the statistics object for the process */ - public Uid.Proc getProcessStatsLocked(String name) { + public Uid.Proc getProcessStatsLocked(String name, int pid) { int uid; if (mUidCache.containsKey(name)) { uid = mUidCache.get(name); } else { - // TODO: Find the actual uid from /proc/pid/status. For now use the hashcode of the - // process name - uid = name.hashCode(); + uid = Process.getUidForPid(pid); mUidCache.put(name, uid); } Uid u = getUidStatsLocked(uid); diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java index 4757919..86f74f3 100644 --- a/core/java/com/android/internal/util/BitwiseInputStream.java +++ b/core/java/com/android/internal/util/BitwiseInputStream.java @@ -65,30 +65,31 @@ public class BitwiseInputStream { /** * Read some data and increment the current position. * - * @param bits the amount of data to read (gte 0, lte 8) + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. * + * @param bits the amount of data to read (gte 0, lte 8) * @return byte of read data (possibly partially filled, from lsb) */ - public byte read(int bits) throws AccessException { + public int read(int bits) throws AccessException { int index = mPos >>> 3; int offset = 16 - (mPos & 0x07) - bits; // &7==%8 if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) { throw new AccessException("illegal read " + "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")"); } - int data = (mBuf[index] & 0x00FF) << 8; - if (offset < 8) data |= (mBuf[index + 1] & 0xFF); + int data = (mBuf[index] & 0xFF) << 8; + if (offset < 8) data |= mBuf[index + 1] & 0xFF; data >>>= offset; data &= (-1 >>> (32 - bits)); mPos += bits; - return (byte)data; + return data; } /** * Read data in bulk into a byte array and increment the current position. * * @param bits the amount of data to read - * * @return newly allocated byte array of read data */ public byte[] readByteArray(int bits) throws AccessException { diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java index 1b974ce..70c0be8 100644 --- a/core/java/com/android/internal/util/BitwiseOutputStream.java +++ b/core/java/com/android/internal/util/BitwiseOutputStream.java @@ -82,6 +82,9 @@ public class BitwiseOutputStream { /** * Write some data and increment the current position. * + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. + * * @param bits the amount of data to write (gte 0, lte 8) * @param data to write, will be masked to expose only bits param from lsb */ @@ -95,8 +98,8 @@ public class BitwiseOutputStream { int offset = 16 - (mPos & 0x07) - bits; // &7==%8 data <<= offset; mPos += bits; - mBuf[index] |= (data >>> 8); - if (offset < 8) mBuf[index + 1] |= (data & 0x00FF); + mBuf[index] |= data >>> 8; + if (offset < 8) mBuf[index + 1] |= data & 0xFF; } /** diff --git a/core/jni/Android.mk b/core/jni/Android.mk index b5d3b26..e99971a 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -25,6 +25,7 @@ LOCAL_SRC_FILES:= \ ActivityManager.cpp \ AndroidRuntime.cpp \ CursorWindow.cpp \ + Time.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ android_opengl_GLES10.cpp \ @@ -123,7 +124,8 @@ LOCAL_SRC_FILES:= \ com_android_internal_graphics_NativeUtils.cpp \ android_backup_BackupDataInput.cpp \ android_backup_BackupDataOutput.cpp \ - android_backup_FileBackupHelperBase.cpp + android_backup_FileBackupHelperBase.cpp \ + android_backup_BackupHelperDispatcher.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f8a4df0..c322b17 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -156,6 +156,7 @@ extern int register_android_location_GpsLocationProvider(JNIEnv* env); extern int register_android_backup_BackupDataInput(JNIEnv *env); extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelperBase(JNIEnv *env); +extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -1241,6 +1242,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), REG_JNI(register_android_backup_FileBackupHelperBase), + REG_JNI(register_android_backup_BackupHelperDispatcher), }; /* diff --git a/core/jni/Time.cpp b/core/jni/Time.cpp new file mode 100644 index 0000000..f3037f3 --- /dev/null +++ b/core/jni/Time.cpp @@ -0,0 +1,199 @@ +#include "TimeUtils.h" +#include <stdio.h> +#include <cutils/tztime.h> + +namespace android { + +static void +dump(const Time& t) +{ + #ifdef HAVE_TM_GMTOFF + long tm_gmtoff = t.t.tm_gmtoff; + #else + long tm_gmtoff = 0; + #endif + printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n", + t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday, + t.t.tm_hour, t.t.tm_min, t.t.tm_sec, + t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday); +} + +Time::Time() +{ + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = 0; + t.tm_mon = 0; + t.tm_year = 0; + t.tm_wday = 0; + t.tm_yday = 0; + t.tm_isdst = -1; // we don't know, so let the C library determine + #ifdef HAVE_TM_GMTOFF + t.tm_gmtoff = 0; + #endif +} + + +#define COMPARE_FIELD(field) do { \ + int diff = a.t.field - b.t.field; \ + if (diff != 0) return diff; \ + } while(0) + +int +Time::compare(Time& a, Time& b) +{ + if (0 == strcmp(a.timezone, b.timezone)) { + // if the timezones are the same, we can easily compare the two + // times. Otherwise, convert to milliseconds and compare that. + // This requires that object be normalized. + COMPARE_FIELD(tm_year); + COMPARE_FIELD(tm_mon); + COMPARE_FIELD(tm_mday); + COMPARE_FIELD(tm_hour); + COMPARE_FIELD(tm_min); + COMPARE_FIELD(tm_sec); + return 0; + } else { + int64_t am = a.toMillis(false /* use isDst */); + int64_t bm = b.toMillis(false /* use isDst */); + int64_t diff = am-bm; + return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0); + } +} + +static const int DAYS_PER_MONTH[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + +static inline int days_this_month(int year, int month) +{ + int n = DAYS_PER_MONTH[month]; + if (n != 28) { + return n; + } else { + int y = year; + return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28; + } +} + +void +Time::switchTimezone(const char* timezone) +{ + time_t seconds = mktime_tz(&(this->t), this->timezone); + localtime_tz(&seconds, &(this->t), timezone); +} + +String8 +Time::format(const char *format, const struct strftime_locale *locale) const +{ + char buf[257]; + int n = strftime_tz(buf, 257, format, &(this->t), locale); + if (n > 0) { + return String8(buf); + } else { + return String8(); + } +} + +static inline short +tochar(int n) +{ + return (n >= 0 && n <= 9) ? ('0'+n) : ' '; +} + +static inline short +next_char(int *m, int k) +{ + int n = *m / k; + *m = *m % k; + return tochar(n); +} + +void +Time::format2445(short* buf, bool hasTime) const +{ + int n; + + n = t.tm_year+1900; + buf[0] = next_char(&n, 1000); + buf[1] = next_char(&n, 100); + buf[2] = next_char(&n, 10); + buf[3] = tochar(n); + + n = t.tm_mon+1; + buf[4] = next_char(&n, 10); + buf[5] = tochar(n); + + n = t.tm_mday; + buf[6] = next_char(&n, 10); + buf[7] = tochar(n); + + if (hasTime) { + buf[8] = 'T'; + + n = t.tm_hour; + buf[9] = next_char(&n, 10); + buf[10] = tochar(n); + + n = t.tm_min; + buf[11] = next_char(&n, 10); + buf[12] = tochar(n); + + n = t.tm_sec; + buf[13] = next_char(&n, 10); + buf[14] = tochar(n); + bool inUtc = strcmp("UTC", timezone) == 0; + if (inUtc) { + buf[15] = 'Z'; + } + } +} + +String8 +Time::toString() const +{ + String8 str; + char* s = str.lockBuffer(150); + #ifdef HAVE_TM_GMTOFF + long tm_gmtoff = t.tm_gmtoff; + #else + long tm_gmtoff = 0; + #endif + sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)", + t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst, + (int)(((Time*)this)->toMillis(false /* use isDst */)/1000)); + str.unlockBuffer(); + return str; +} + +void +Time::setToNow() +{ + time_t seconds; + time(&seconds); + localtime_tz(&seconds, &(this->t), this->timezone); +} + +int64_t +Time::toMillis(bool ignoreDst) +{ + if (ignoreDst) { + this->t.tm_isdst = -1; + } + int64_t r = mktime_tz(&(this->t), this->timezone); + if (r == -1) + return -1; + return r * 1000; +} + +void +Time::set(int64_t millis) +{ + time_t seconds = millis / 1000; + localtime_tz(&seconds, &(this->t), this->timezone); +} + +}; // namespace android + diff --git a/core/jni/TimeUtils.h b/core/jni/TimeUtils.h new file mode 100644 index 0000000..b19e021 --- /dev/null +++ b/core/jni/TimeUtils.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_TIME_H +#define ANDROID_TIME_H + +#include <time.h> +#include <cutils/tztime.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/time.h> +#include <utils/String8.h> +#include <utils/String16.h> + +namespace android { + +/* + * This class is the core implementation of the android.util.Time java + * class. It doesn't implement some of the methods that are implemented + * in Java. They could be done here, but it's not expected that this class + * will be used. If that assumption is incorrect, feel free to update this + * file. The reason to do it here is to not mix the implementation of this + * class and the jni glue code. + */ +class Time +{ +public: + struct tm t; + + // this object doesn't own this string + const char *timezone; + + enum { + SEC = 1, + MIN = 2, + HOUR = 3, + MDAY = 4, + MON = 5, + YEAR = 6, + WDAY = 7, + YDAY = 8 + }; + + static int compare(Time& a, Time& b); + + Time(); + + void switchTimezone(const char *timezone); + String8 format(const char *format, const struct strftime_locale *locale) const; + void format2445(short* buf, bool hasTime) const; + String8 toString() const; + void setToNow(); + int64_t toMillis(bool ignoreDst); + void set(int64_t millis); + + inline void set(int sec, int min, int hour, int mday, int mon, int year, + int isdst) + { + this->t.tm_sec = sec; + this->t.tm_min = min; + this->t.tm_hour = hour; + this->t.tm_mday = mday; + this->t.tm_mon = mon; + this->t.tm_year = year; + this->t.tm_isdst = isdst; +#ifdef HAVE_TM_GMTOFF + this->t.tm_gmtoff = 0; +#endif + this->t.tm_wday = 0; + this->t.tm_yday = 0; + } +}; + +}; // namespace android + +#endif // ANDROID_TIME_H diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp new file mode 100644 index 0000000..2e3f0b9 --- /dev/null +++ b/core/jni/android_backup_BackupHelperDispatcher.cpp @@ -0,0 +1,259 @@ +/* + * 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. + */ + +#define LOG_TAG "BackupHelperDispatcher_native" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> + + +#define VERSION_1_HEADER 0x01706c48 // 'Hlp'1 little endian + +namespace android +{ + +struct chunk_header_v1 { + int headerSize; + int version; + int dataSize; // corresponds to Header.chunkSize + int nameLength; // not including the NULL terminator, which is not written to the file +}; + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; +static jfieldID s_chunkSizeField = 0; +static jfieldID s_keyPrefixField = 0; + +static int +readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) +{ + chunk_header_v1 flattenedHeader; + int fd; + ssize_t amt; + String8 keyPrefix; + char* buf; + + fd = env->GetIntField(fdObj, s_descriptorField); + + amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize)); + if (amt != sizeof(flattenedHeader.headerSize)) { + return -1; + } + + int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize); + + if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) { + LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize); + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + // >0 means skip this chunk + return 1; + } + } + + amt = read(fd, &flattenedHeader.version, + sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize)); + if (amt <= 0) { + LOGW("Failed reading chunk header"); + return -1; + } + remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize); + + if (flattenedHeader.version != VERSION_1_HEADER) { + LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version, + flattenedHeader.headerSize); + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + // >0 means skip this chunk + return 1; + } + } + +#if 0 + LOGD("chunk header:"); + LOGD(" headerSize=%d", flattenedHeader.headerSize); + LOGD(" version=0x%08x", flattenedHeader.version); + LOGD(" dataSize=%d", flattenedHeader.dataSize); + LOGD(" nameLength=%d", flattenedHeader.nameLength); +#endif + + if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 || + remainingHeader < flattenedHeader.nameLength) { + LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader, + flattenedHeader.dataSize, flattenedHeader.nameLength); + return -1; + } + + buf = keyPrefix.lockBuffer(flattenedHeader.nameLength); + if (buf == NULL) { + LOGW("unable to allocate %d bytes", flattenedHeader.nameLength); + return -1; + } + + amt = read(fd, buf, flattenedHeader.nameLength); + buf[flattenedHeader.nameLength] = 0; + + keyPrefix.unlockBuffer(flattenedHeader.nameLength); + + remainingHeader -= flattenedHeader.nameLength; + + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + } + + env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize); + env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string())); + + return 0; +} + +static int +skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip) +{ + int fd; + + fd = env->GetIntField(fdObj, s_descriptorField); + + lseek(fd, bytesToSkip, SEEK_CUR); + + return 0; +} + +static int +padding_len(int len) +{ + len = len % 4; + return len == 0 ? len : 4 - len; +} + +static int +allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) +{ + int pos; + jstring nameObj; + int nameLength; + int namePadding; + int headerSize; + int fd; + + fd = env->GetIntField(fdObj, s_descriptorField); + + nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); + + nameLength = env->GetStringUTFLength(nameObj); + namePadding = padding_len(nameLength); + + headerSize = sizeof(chunk_header_v1) + nameLength + namePadding; + + pos = lseek(fd, 0, SEEK_CUR); + + lseek(fd, headerSize, SEEK_CUR); + + return pos; +} + +static int +writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos) +{ + int err; + chunk_header_v1 header; + int fd; + int namePadding; + int prevPos; + jstring nameObj; + const char* buf; + + fd = env->GetIntField(fdObj, s_descriptorField); + prevPos = lseek(fd, 0, SEEK_CUR); + + nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); + header.nameLength = env->GetStringUTFLength(nameObj); + namePadding = padding_len(header.nameLength); + + header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding; + header.version = VERSION_1_HEADER; + header.dataSize = prevPos - (pos + header.headerSize); + + lseek(fd, pos, SEEK_SET); + err = write(fd, &header, sizeof(chunk_header_v1)); + if (err != sizeof(chunk_header_v1)) { + return errno; + } + + buf = env->GetStringUTFChars(nameObj, NULL); + err = write(fd, buf, header.nameLength); + env->ReleaseStringUTFChars(nameObj, buf); + if (err != header.nameLength) { + return errno; + } + + if (namePadding != 0) { + int zero = 0; + err = write(fd, &zero, namePadding); + if (err != namePadding) { + return errno; + } + } + + lseek(fd, prevPos, SEEK_SET); + return 0; +} + +static const JNINativeMethod g_methods[] = { + { "readHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", + (void*)readHeader_native }, + { "skipChunk_native", + "(Ljava/io/FileDescriptor;I)I", + (void*)skipChunk_native }, + { "allocateHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", + (void*)allocateHeader_native }, + { "writeHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I", + (void*)writeHeader_native }, +}; + +int register_android_backup_BackupHelperDispatcher(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header"); + LOG_FATAL_IF(clazz == NULL, + "Unable to find class android.backup.BackupHelperDispatcher.Header"); + s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I"); + LOG_FATAL_IF(s_chunkSizeField == NULL, + "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header"); + s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;"); + LOG_FATAL_IF(s_keyPrefixField == NULL, + "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp index 8361212..343fa53 100644 --- a/core/jni/android_bluetooth_common.cpp +++ b/core/jni/android_bluetooth_common.cpp @@ -67,6 +67,11 @@ static Properties adapter_properties[] = { {"Devices", DBUS_TYPE_ARRAY}, }; +typedef union { + char *str_val; + int int_val; + char **array_val; +} property_value; jfieldID get_field(JNIEnv *env, jclass clazz, const char *member, const char *mtype) { @@ -466,258 +471,217 @@ void append_variant(DBusMessageIter *iter, int type, void *val) dbus_message_iter_close_container(iter, &value_iter); } - -//TODO(): Remove code duplication between parse_properties and -//parse_property_change -jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, - const int max_num_properties) { - DBusMessageIter dict_entry, dict, prop_val, device_val, array_val_iter; - jobjectArray strArray = NULL; - char * property; - char values[max_num_properties][256]; - char **uuid_array = NULL; - char **device_path = NULL; - char **array_elements = NULL; - char *string_val; - uint32_t int_val, bool_val; - int i, j, k, type, array_type, num_array_elements = 0; - int ret, num_properties = 0, num_uuids = 0, num_devices = 0; - - - jclass stringClass = env->FindClass("java/lang/String"); - DBusError err; - dbus_error_init(&err); - - for (i = 0; i < max_num_properties; i++) - memset(values[i], '\0', 128 * sizeof(char)); - - if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) - goto failure; - dbus_message_iter_recurse(iter, &dict); - do { - if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY) - goto failure; - dbus_message_iter_recurse(&dict, &dict_entry); - if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_STRING) - goto failure; - dbus_message_iter_get_basic(&dict_entry, &property); - if (!dbus_message_iter_next(&dict_entry)) - goto failure; - if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_VARIANT) - goto failure; - - for (i = 0; i < max_num_properties; i++) { - if (!strncmp(properties[i].name, property, strlen(property))) { - num_properties ++; - break; - } - } - if (i == max_num_properties) - goto failure; - - type = properties[i].type; - dbus_message_iter_recurse(&dict_entry, &prop_val); - if(dbus_message_iter_get_arg_type(&prop_val) != type) { - LOGE("Property type mismatch in parse_properties: %d, expected:%d", - dbus_message_iter_get_arg_type(&prop_val), type); - goto failure; - } - - switch(type) { - case DBUS_TYPE_STRING: - case DBUS_TYPE_OBJECT_PATH: - dbus_message_iter_get_basic(&prop_val, &string_val); - strcpy(values[i], string_val); - break; - case DBUS_TYPE_UINT32: - case DBUS_TYPE_INT16: - dbus_message_iter_get_basic(&prop_val, &int_val); - sprintf(values[i], "%d", int_val); - break; - case DBUS_TYPE_BOOLEAN: - dbus_message_iter_get_basic(&prop_val, &bool_val); - sprintf(values[i], "%s", bool_val ? "true" : "false"); - break; - case DBUS_TYPE_ARRAY: - dbus_message_iter_recurse(&prop_val, &array_val_iter); - array_type = dbus_message_iter_get_arg_type(&array_val_iter); - num_array_elements = 0; - if (array_type == DBUS_TYPE_OBJECT_PATH || - array_type == DBUS_TYPE_STRING){ - do { - num_array_elements++; - } while(dbus_message_iter_next(&array_val_iter)); - dbus_message_iter_recurse(&prop_val, &array_val_iter); - // Allocate an array - array_elements = (char **)malloc(sizeof(char *) * - num_array_elements); - if (!array_elements) - goto failure; - - j = 0; - do { - dbus_message_iter_get_basic(&array_val_iter, &array_elements[j]); - j++; - } while(dbus_message_iter_next(&array_val_iter)); - if (!strncmp(property, "UUIDs", strlen("UUIDs"))) { - num_uuids = num_array_elements; - uuid_array = array_elements; - } else { - num_devices = num_array_elements; - device_path = array_elements; - } - } - break; - default: - goto failure; - } - } while(dbus_message_iter_next(&dict)); - - // Convert it to a array of strings. - strArray = env->NewObjectArray((num_properties + num_array_elements) * 2, - stringClass, NULL); - - j = 0; - for (i = 0; i < max_num_properties; i++) { - if (properties[i].type == DBUS_TYPE_ARRAY) { - if (!strncmp(properties[i].name, "UUIDs", strlen("UUIDs"))) { - num_array_elements = num_uuids; - array_elements = uuid_array; - } else { - num_array_elements = num_devices; - array_elements = device_path; - } - - for (k = 0; k < num_array_elements; k++) { - set_object_array_element(env, strArray, properties[i].name, j++); - set_object_array_element(env, strArray, array_elements[k], j++); - } - } else if (values[i][0] != '\0') { - set_object_array_element(env, strArray, properties[i].name, j++); - set_object_array_element(env, strArray, values[i], j++); - } - } - - if (uuid_array) - free(uuid_array); - if (device_path) - free(device_path); - return strArray; - -failure: - if (dbus_error_is_set(&err)) - LOG_AND_FREE_DBUS_ERROR(&err); - if (uuid_array) - free(uuid_array); - if (device_path) - free(device_path); - return NULL; -} - -jobjectArray create_prop_array(JNIEnv *env, Properties *properties, - int prop_index, void *value, int len ) { - jclass stringClass= env->FindClass("java/lang/String"); - char **prop_val = NULL; - char buf[32] = {'\0'}; - int i, j; - - jobjectArray strArray = env->NewObjectArray(1 + len, stringClass, NULL); - j = 0; - set_object_array_element(env, strArray, properties[prop_index].name, j++); - - if (properties[prop_index].type == DBUS_TYPE_UINT32) { - sprintf(buf, "%d", *(int *) value); - set_object_array_element(env, strArray, buf, j++); - } else if (properties[prop_index].type == DBUS_TYPE_BOOLEAN) { - sprintf(buf, "%s", *(int *) value ? "true" : "false"); - set_object_array_element(env, strArray, buf, j++); - } else if (properties[prop_index].type == DBUS_TYPE_ARRAY) { - prop_val = (char **) value; - for (i = 0; i < len; i++) - set_object_array_element(env, strArray, prop_val[i], j++); - } else { - set_object_array_element(env, strArray, (const char *) value, j++); - } - if (prop_val) - free (prop_val); - return strArray; -} - -jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, - Properties *properties, int max_num_properties) { - DBusMessageIter iter, prop_val, array_val_iter; - DBusError err; - void *value; - char *property; +int get_property(DBusMessageIter iter, Properties *properties, + int max_num_properties, int *prop_index, property_value *value, int *len) { + DBusMessageIter prop_val, array_val_iter; + char *property = NULL; uint32_t array_type; - int i, j, type, len, prop_index; + char *str_val; + int i, j, type, int_val; - dbus_error_init(&err); - if (!dbus_message_iter_init(msg, &iter)) - goto failure; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - goto failure; + return -1; dbus_message_iter_get_basic(&iter, &property); if (!dbus_message_iter_next(&iter)) - goto failure; + return -1; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) - goto failure; + return -1; for (i = 0; i < max_num_properties; i++) { - if (!strncmp(property, properties[i].name, strlen(properties[i].name))) + if (!strncmp(property, properties[i].name, strlen(property))) break; } - prop_index = i; + *prop_index = i; if (i == max_num_properties) - goto failure; + return -1; dbus_message_iter_recurse(&iter, &prop_val); - type = properties[prop_index].type; + type = properties[*prop_index].type; if (dbus_message_iter_get_arg_type(&prop_val) != type) { - LOGE("Property type mismatch in parse_properties: %d, expected:%d", - dbus_message_iter_get_arg_type(&prop_val), type); - goto failure; + LOGE("Property type mismatch in get_property: %d, expected:%d, index:%d", + dbus_message_iter_get_arg_type(&prop_val), type, *prop_index); + return -1; } switch(type) { case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: - dbus_message_iter_get_basic(&prop_val, &value); - len = 1; + dbus_message_iter_get_basic(&prop_val, &value->str_val); + *len = 1; break; case DBUS_TYPE_UINT32: case DBUS_TYPE_INT16: case DBUS_TYPE_BOOLEAN: - uint32_t int_val; dbus_message_iter_get_basic(&prop_val, &int_val); - value = &int_val; - len = 1; + value->int_val = int_val; + *len = 1; break; case DBUS_TYPE_ARRAY: dbus_message_iter_recurse(&prop_val, &array_val_iter); array_type = dbus_message_iter_get_arg_type(&array_val_iter); - len = 0; + *len = 0; + value->array_val = NULL; if (array_type == DBUS_TYPE_OBJECT_PATH || array_type == DBUS_TYPE_STRING){ + j = 0; do { - len ++; + j ++; } while(dbus_message_iter_next(&array_val_iter)); dbus_message_iter_recurse(&prop_val, &array_val_iter); // Allocate an array of char * - char **tmp = (char **)malloc(sizeof(char *) * len); + *len = j; + char **tmp = (char **)malloc(sizeof(char *) * *len); if (!tmp) - goto failure; + return -1; j = 0; do { dbus_message_iter_get_basic(&array_val_iter, &tmp[j]); j ++; } while(dbus_message_iter_next(&array_val_iter)); - value = (char **) tmp; + value->array_val = tmp; } break; default: + return -1; + } + return 0; +} + +void create_prop_array(JNIEnv *env, jobjectArray strArray, Properties *property, + property_value *value, int len, int *array_index ) { + char **prop_val = NULL; + char buf[32] = {'\0'}, buf1[32] = {'\0'}; + int i; + + char *name = property->name; + int prop_type = property->type; + + set_object_array_element(env, strArray, name, *array_index); + *array_index += 1; + + if (prop_type == DBUS_TYPE_UINT32 || prop_type == DBUS_TYPE_INT16) { + sprintf(buf, "%d", value->int_val); + set_object_array_element(env, strArray, buf, *array_index); + *array_index += 1; + } else if (prop_type == DBUS_TYPE_BOOLEAN) { + sprintf(buf, "%s", value->int_val ? "true" : "false"); + + set_object_array_element(env, strArray, buf, *array_index); + *array_index += 1; + } else if (prop_type == DBUS_TYPE_ARRAY) { + // Write the length first + sprintf(buf1, "%d", len); + set_object_array_element(env, strArray, buf1, *array_index); + *array_index += 1; + + prop_val = value->array_val; + for (i = 0; i < len; i++) { + set_object_array_element(env, strArray, prop_val[i], *array_index); + *array_index += 1; + } + } else { + set_object_array_element(env, strArray, (const char *) value->str_val, *array_index); + *array_index += 1; + } +} + +jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, + const int max_num_properties) { + DBusMessageIter dict_entry, dict; + jobjectArray strArray = NULL; + property_value value; + int i, size = 0,array_index = 0; + int len = 0, prop_type = DBUS_TYPE_INVALID, prop_index = -1, type; + struct { + property_value value; + int len; + bool used; + } values[max_num_properties]; + int t, j; + + jclass stringClass = env->FindClass("java/lang/String"); + DBusError err; + dbus_error_init(&err); + + for (i = 0; i < max_num_properties; i++) { + values[i].used = false; + } + + if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + goto failure; + dbus_message_iter_recurse(iter, &dict); + do { + len = 0; + if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY) + goto failure; + dbus_message_iter_recurse(&dict, &dict_entry); + + if (!get_property(dict_entry, properties, max_num_properties, &prop_index, + &value, &len)) { + size += 2; + if (properties[prop_index].type == DBUS_TYPE_ARRAY) + size += len; + values[prop_index].value = value; + values[prop_index].len = len; + values[prop_index].used = true; + } else { + goto failure; + } + } while(dbus_message_iter_next(&dict)); + + strArray = env->NewObjectArray(size, stringClass, NULL); + + for (i = 0; i < max_num_properties; i++) { + if (values[i].used) { + create_prop_array(env, strArray, &properties[i], &values[i].value, values[i].len, + &array_index); + + if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used + && values[i].value.array_val != NULL) + free(values[i].value.array_val); + } + + } + return strArray; + +failure: + if (dbus_error_is_set(&err)) + LOG_AND_FREE_DBUS_ERROR(&err); + for (i = 0; i < max_num_properties; i++) + if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used == true + && values[i].value.array_val != NULL) + free(values[i].value.array_val); + return NULL; +} + +jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, + Properties *properties, int max_num_properties) { + DBusMessageIter iter; + DBusError err; + jobjectArray strArray = NULL; + jclass stringClass= env->FindClass("java/lang/String"); + int len = 0, prop_index = -1; + int array_index = 0, size = 0; + property_value value; + + dbus_error_init(&err); + if (!dbus_message_iter_init(msg, &iter)) goto failure; + + if (!get_property(iter, properties, max_num_properties, + &prop_index, &value, &len)) { + size += 2; + if (properties[prop_index].type == DBUS_TYPE_ARRAY) + size += len; + strArray = env->NewObjectArray(size, stringClass, NULL); + + create_prop_array(env, strArray, &properties[prop_index], + &value, len, &array_index); + + if (properties[prop_index].type == DBUS_TYPE_ARRAY && value.array_val != NULL) + free(value.array_val); + + return strArray; } - return create_prop_array(env, properties, prop_index, value, len); failure: LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return NULL; diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index c7cc9b3..0420918 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -125,37 +125,8 @@ void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2) return; } JNIEnv *env = AndroidRuntime::getJNIEnv(); - - // parse message - switch (msgType) { - case CAMERA_MSG_ERROR: - LOGV("errorCallback"); - int error; - switch (ext1) { - case DEAD_OBJECT: - error = kCameraErrorMediaServer; - break; - default: - error = kCameraErrorUnknown; - break; - } - env->CallStaticVoidMethod(mCameraJClass, fields.post_event, - mCameraJObjectWeak, kErrorCallback, error, 0, NULL); - break; - case CAMERA_MSG_FOCUS: - LOGV("autoFocusCallback"); - env->CallStaticVoidMethod(mCameraJClass, fields.post_event, - mCameraJObjectWeak, kAutoFocusCallback, ext1, 0, NULL); - break; - case CAMERA_MSG_SHUTTER: - LOGV("shutterCallback"); - env->CallStaticVoidMethod(mCameraJClass, fields.post_event, - mCameraJObjectWeak, kShutterCallback, 0, 0, NULL); - break; - default: - LOGV("notifyCallback(%d, %d, %d)", msgType, ext1, ext2); - break; - } + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, ext1, ext2); } void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) @@ -200,30 +171,27 @@ void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr) // VM pointer will be NULL if object is released Mutex::Autolock _l(mLock); JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (mCameraJObjectWeak == NULL) { + LOGW("callback on dead camera object"); + return; + } // return data based on callback type switch(msgType) { - case CAMERA_MSG_PREVIEW_FRAME: - LOGV("previewCallback"); - copyAndPost(env, dataPtr, kPreviewCallback); - break; case CAMERA_MSG_VIDEO_FRAME: - LOGV("recordingCallback"); + // should never happen break; + // don't return raw data to Java case CAMERA_MSG_RAW_IMAGE: LOGV("rawCallback"); env->CallStaticVoidMethod(mCameraJClass, fields.post_event, - mCameraJObjectWeak, kRawCallback, 0, 0, NULL); - break; - case CAMERA_MSG_COMPRESSED_IMAGE: - LOGV("jpegCallback"); - copyAndPost(env, dataPtr, kJpegCallback); + mCameraJObjectWeak, msgType, 0, 0, NULL); break; default: LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); + copyAndPost(env, dataPtr, msgType); break; } - } // connect to camera service @@ -298,7 +266,10 @@ static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, sp<Camera> camera = get_native_camera(env, thiz, NULL); if (camera == 0) return; - sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); + sp<Surface> surface = NULL; + if (jSurface != NULL) { + surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface)); + } if (camera->setPreviewDisplay(surface) != NO_ERROR) { jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); } diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp index 7c208e9..98f4e03 100644 --- a/core/jni/android_text_format_Time.cpp +++ b/core/jni/android_text_format_Time.cpp @@ -23,7 +23,7 @@ #include "jni.h" #include "utils/misc.h" #include "android_runtime/AndroidRuntime.h" -#include <utils/TimeUtils.h> +#include "TimeUtils.h" #include <nativehelper/JNIHelp.h> #include <cutils/tztime.h> diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index d147bcc..2d90ba4 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -535,7 +535,7 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c jint keyboard, jint keyboardHidden, jint navigation, jint screenWidth, jint screenHeight, - jint sdkVersion) + jint screenLayout, jint sdkVersion) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { @@ -557,6 +557,7 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c config.navigation = (uint8_t)navigation; config.screenWidth = (uint16_t)screenWidth; config.screenHeight = (uint16_t)screenHeight; + config.screenLayout = (uint8_t)screenLayout; config.sdkVersion = (uint16_t)sdkVersion; config.minorVersion = 0; am->setConfiguration(config, locale8); @@ -1567,7 +1568,7 @@ static JNINativeMethod gAssetManagerMethods[] = { (void*) android_content_AssetManager_setLocale }, { "getLocales", "()[Ljava/lang/String;", (void*) android_content_AssetManager_getLocales }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIII)V", + { "setConfiguration", "(IILjava/lang/String;IIIIIIIIII)V", (void*) android_content_AssetManager_setConfiguration }, { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", (void*) android_content_AssetManager_getResourceIdentifier }, diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 1c9ee7d..770c755 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -269,9 +269,9 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, jint pid, jint pri) { - if (pri == ANDROID_PRIORITY_BACKGROUND) { + if (pri >= ANDROID_PRIORITY_BACKGROUND) { add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT); - } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) { + } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) { add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT); } @@ -496,7 +496,7 @@ void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileSt const String8& field = fields[i]; if (strncmp(p, field.string(), field.length()) == 0) { p += field.length(); - while (*p == ' ') p++; + while (*p == ' ' || *p == '\t') p++; char* num = p; while (*p >= '0' && *p <= '9') p++; skipToEol = *p != '\n'; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0c90769..31b6519 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -973,7 +973,7 @@ <permission android:name="android.permission.BACKUP" android:label="@string/permlab_backup" android:description="@string/permdesc_backup" - android:protectionLevel="signature" /> + android:protectionLevel="signatureOrSystem" /> <!-- Allows an application to tell the AppWidget service which application can access AppWidget's data. The normal user flow is that a user @@ -1000,6 +1000,7 @@ android:hasCode="false" android:label="@string/android_system_label" android:allowClearUserData="false" + android:backupAgent="com.android.internal.backup.SystemBackupAgent" android:icon="@drawable/ic_launcher_android"> <activity android:name="com.android.internal.app.ChooserActivity" android:theme="@style/Theme.Dialog.Alert" diff --git a/core/res/res/drawable/progress.xml b/core/res/res/drawable/progress.xml deleted file mode 100644 index d270520..0000000 --- a/core/res/res/drawable/progress.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/progress.xml -** -** Copyright 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. -*/ ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@android:drawable/progress_circular_background" /> - <item> - <shape android:shape="ring" - android:innerRadiusRatio="3.4" - android:thicknessRatio="6.0"> - <gradient - android:useLevel="true" - android:type="sweep" - android:startColor="#ff000000" - android:endColor="#ffffffff" /> - </shape> - </item> - <item> - <rotate - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360" - android:drawable="@android:drawable/progress_particle" /> - </item> -</layer-list> diff --git a/core/res/res/drawable/progress_circular_background.png b/core/res/res/drawable/progress_circular_background.png Binary files differdeleted file mode 100644 index 7c637fd..0000000 --- a/core/res/res/drawable/progress_circular_background.png +++ /dev/null diff --git a/core/res/res/drawable/progress_circular_background_small.png b/core/res/res/drawable/progress_circular_background_small.png Binary files differdeleted file mode 100644 index 6b8ba9b..0000000 --- a/core/res/res/drawable/progress_circular_background_small.png +++ /dev/null diff --git a/core/res/res/drawable/progress_circular_indeterminate.png b/core/res/res/drawable/progress_circular_indeterminate.png Binary files differdeleted file mode 100644 index 125a264..0000000 --- a/core/res/res/drawable/progress_circular_indeterminate.png +++ /dev/null diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml index 4669104..4f016bc 100644 --- a/core/res/res/drawable/progress_large.xml +++ b/core/res/res/drawable/progress_large.xml @@ -1,45 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- 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. +<!-- +/* +** +** Copyright 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. +*/ --> - - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <shape - android:shape="ring" - android:innerRadiusRatio="3" - android:thicknessRatio="8" - android:useLevel="false"> - - <size - android:width="76dip" - android:height="76dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#4c737373" - android:centerColor="#4c737373" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> - +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_76" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_indeterminate.xml b/core/res/res/drawable/progress_large_white.xml index 1bf715e..c690ed4 100644 --- a/core/res/res/drawable/progress_indeterminate.xml +++ b/core/res/res/drawable/progress_large_white.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/drawable/progress.xml +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright 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. @@ -17,16 +17,9 @@ ** limitations under the License. */ --> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:drawable="@android:drawable/progress_circular_background" /> - - <item><rotate - android:pivotX="50%" - android:pivotY="50%" - android:fromDegrees="0" - android:toDegrees="360" - android:drawable="@android:drawable/progress_circular_indeterminate" /> - </item> -</layer-list> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_76" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml index 92aebb5..eb1bd50 100644 --- a/core/res/res/drawable/progress_medium.xml +++ b/core/res/res/drawable/progress_medium.xml @@ -1,43 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- 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. +<!-- +/* +** +** Copyright 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. +*/ --> - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <shape - android:shape="ring" - android:innerRadiusRatio="3" - android:thicknessRatio="8" - android:useLevel="false"> - - <size - android:width="48dip" - android:height="48dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#4c737373" - android:centerColor="#4c737373" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_48" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_medium_white.xml b/core/res/res/drawable/progress_medium_white.xml new file mode 100644 index 0000000..b4f9b31 --- /dev/null +++ b/core/res/res/drawable/progress_medium_white.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_48" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml index e5b0021..e0ee5e4 100644 --- a/core/res/res/drawable/progress_small.xml +++ b/core/res/res/drawable/progress_small.xml @@ -1,45 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- 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. +<!-- +/* +** +** Copyright 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. +*/ --> - - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <!-- An extra pixel is added on both ratios for stroke --> - <shape - android:shape="ring" - android:innerRadiusRatio="3.2" - android:thicknessRatio="5.333" - android:useLevel="false"> - - <size - android:width="16dip" - android:height="16dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#4c737373" - android:centerColor="#4c737373" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_16" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml index cf8e41c..8cfba86 100644 --- a/core/res/res/drawable/progress_small_titlebar.xml +++ b/core/res/res/drawable/progress_small_titlebar.xml @@ -1,45 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- 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. +<!-- +/* +** +** Copyright 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. +*/ --> - - -<rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:pivotX="50%" android:pivotY="50%" - android:fromDegrees="0" android:toDegrees="360"> - - <!-- An extra pixel is added on both ratios for stroke --> - <shape - android:shape="ring" - android:innerRadiusRatio="3.2" - android:thicknessRatio="5.333" - android:useLevel="false"> - - <size - android:width="16dip" - android:height="16dip" - /> - - <gradient - android:type="sweep" - android:useLevel="false" - android:startColor="#ff666666" - android:centerColor="#ff666666" - android:centerY="0.50" - android:endColor="#ffffd300" - /> - - </shape> - -</rotate> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_16" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/progress_small_white.xml b/core/res/res/drawable/progress_small_white.xml new file mode 100644 index 0000000..8cfba86 --- /dev/null +++ b/core/res/res/drawable/progress_small_white.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_white_16" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml index 34c163d..31a77c3 100644 --- a/core/res/res/drawable/search_spinner.xml +++ b/core/res/res/drawable/search_spinner.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2008, The Android Open Source Project +** Copyright 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. @@ -17,20 +17,9 @@ ** limitations under the License. */ --> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="false"> - <item android:drawable="@drawable/search_spinner_anim1" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim2" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim3" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim4" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim5" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim6" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim7" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim8" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim9" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim10" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim11" android:duration="150" /> - <item android:drawable="@drawable/search_spinner_anim12" android:duration="150" /> -</animation-list> - +<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/spinner_black_20" + android:pivotX="50%" + android:pivotY="50%" + android:framesCount="12" + android:frameDuration="100" /> diff --git a/core/res/res/drawable/search_spinner_anim10.png b/core/res/res/drawable/search_spinner_anim10.png Binary files differdeleted file mode 100755 index 9611d97..0000000 --- a/core/res/res/drawable/search_spinner_anim10.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim11.png b/core/res/res/drawable/search_spinner_anim11.png Binary files differdeleted file mode 100755 index 4261704..0000000 --- a/core/res/res/drawable/search_spinner_anim11.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim12.png b/core/res/res/drawable/search_spinner_anim12.png Binary files differdeleted file mode 100755 index 0602314..0000000 --- a/core/res/res/drawable/search_spinner_anim12.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim2.png b/core/res/res/drawable/search_spinner_anim2.png Binary files differdeleted file mode 100755 index 05d58e0..0000000 --- a/core/res/res/drawable/search_spinner_anim2.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim3.png b/core/res/res/drawable/search_spinner_anim3.png Binary files differdeleted file mode 100755 index 69fa9c1..0000000 --- a/core/res/res/drawable/search_spinner_anim3.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim4.png b/core/res/res/drawable/search_spinner_anim4.png Binary files differdeleted file mode 100755 index 9201bac..0000000 --- a/core/res/res/drawable/search_spinner_anim4.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim5.png b/core/res/res/drawable/search_spinner_anim5.png Binary files differdeleted file mode 100755 index f0c7101..0000000 --- a/core/res/res/drawable/search_spinner_anim5.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim6.png b/core/res/res/drawable/search_spinner_anim6.png Binary files differdeleted file mode 100755 index 99d1d4e..0000000 --- a/core/res/res/drawable/search_spinner_anim6.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim7.png b/core/res/res/drawable/search_spinner_anim7.png Binary files differdeleted file mode 100755 index 8ca3358..0000000 --- a/core/res/res/drawable/search_spinner_anim7.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim8.png b/core/res/res/drawable/search_spinner_anim8.png Binary files differdeleted file mode 100755 index 408d723..0000000 --- a/core/res/res/drawable/search_spinner_anim8.png +++ /dev/null diff --git a/core/res/res/drawable/search_spinner_anim9.png b/core/res/res/drawable/search_spinner_anim9.png Binary files differdeleted file mode 100755 index 42a2c65..0000000 --- a/core/res/res/drawable/search_spinner_anim9.png +++ /dev/null diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable/spinner_black_16.png Binary files differnew file mode 100644 index 0000000..5ee33ce --- /dev/null +++ b/core/res/res/drawable/spinner_black_16.png diff --git a/core/res/res/drawable/search_spinner_anim1.png b/core/res/res/drawable/spinner_black_20.png Binary files differindex e55b60d..e55b60d 100755 --- a/core/res/res/drawable/search_spinner_anim1.png +++ b/core/res/res/drawable/spinner_black_20.png diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable/spinner_black_48.png Binary files differnew file mode 100644 index 0000000..3a68192 --- /dev/null +++ b/core/res/res/drawable/spinner_black_48.png diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable/spinner_black_76.png Binary files differnew file mode 100644 index 0000000..ec57460 --- /dev/null +++ b/core/res/res/drawable/spinner_black_76.png diff --git a/core/res/res/drawable/progress_particle.png b/core/res/res/drawable/spinner_white_16.png Binary files differindex 9160108..dd2e1fd 100644 --- a/core/res/res/drawable/progress_particle.png +++ b/core/res/res/drawable/spinner_white_16.png diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable/spinner_white_48.png Binary files differnew file mode 100644 index 0000000..d25a33e --- /dev/null +++ b/core/res/res/drawable/spinner_white_48.png diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable/spinner_white_76.png Binary files differnew file mode 100644 index 0000000..f53e8ff --- /dev/null +++ b/core/res/res/drawable/spinner_white_76.png diff --git a/core/res/res/layout/character_picker.xml b/core/res/res/layout/character_picker.xml index bb4955a..0344849 100644 --- a/core/res/res/layout/character_picker.xml +++ b/core/res/res/layout/character_picker.xml @@ -23,8 +23,8 @@ android:id="@+id/characterPicker" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="12dp" - android:verticalSpacing="8dp" + android:padding="4dp" + android:verticalSpacing="4dp" android:horizontalSpacing="8dp" android:stretchMode="spacingWidth" android:gravity="left" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 78567fc..4c84732 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -350,6 +350,12 @@ <attr name="progressBarStyleSmallTitle" format="reference" /> <!-- Large ProgressBar style. This is a large circular progress bar. --> <attr name="progressBarStyleLarge" format="reference" /> + <!-- Inverse ProgressBar style. This is a medium circular progress bar. --> + <attr name="progressBarStyleInverse" format="reference" /> + <!-- Small inverse ProgressBar style. This is a small circular progress bar. --> + <attr name="progressBarStyleSmallInverse" format="reference" /> + <!-- Large inverse ProgressBar style. This is a large circular progress bar. --> + <attr name="progressBarStyleLargeInverse" format="reference" /> <!-- Default SeekBar style. --> <attr name="seekBarStyle" format="reference" /> <!-- Default RatingBar style. --> @@ -2327,6 +2333,15 @@ <attr name="drawable" /> </declare-styleable> + <declare-styleable name="AnimatedRotateDrawable"> + <attr name="visible" /> + <attr name="frameDuration" format="integer" /> + <attr name="framesCount" format="integer" /> + <attr name="pivotX" /> + <attr name="pivotY" /> + <attr name="drawable" /> + </declare-styleable> + <declare-styleable name="InsetDrawable"> <attr name="visible" /> <attr name="drawable" /> @@ -3326,5 +3341,18 @@ <attr name="accountType"/> </declare-styleable> + <!-- =============================== --> + <!-- Contacts meta-data attributes --> + <!-- =============================== --> + + <declare-styleable name="Icon"> + <attr name="icon" /> + <attr name="mimeType" /> + </declare-styleable> + + <declare-styleable name="IconDefault"> + <attr name="icon" /> + </declare-styleable> + </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 91cd9fd..7571e24 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -512,6 +512,9 @@ <!-- The screen orientation has changed, that is the user has rotated the device. --> <flag name="orientation" value="0x0080" /> + <!-- The screen orientation has changed, that is the user has + rotated the device. --> + <flag name="screenLayout" value="0x0100" /> <!-- The font scaling factor has changed, that is the user has selected a new global font size. --> <flag name="fontScale" value="0x40000000" /> @@ -829,8 +832,59 @@ <p>This appears as a child tag of the {@link #AndroidManifest manifest} tag. --> <declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifest"> - <!-- Required value of the density in dip (device independent pixel). --> - <attr name="density" format="integer" /> + <!-- Required value of the density in dip (device independent pixel). + You should use one of the pre-defined constants for the standard + screen densities defined here. + --> + <attr name="density" format="integer"> + <!-- A low density screen, such as a QVGA or WQVGA screen in a + typical hand-held phone. The constant for this is 120. --> + <enum name="low" value="120" /> + <!-- A medium density screen, such as an HVGA screen in a + typical hand-held phone. The constant for this is 160. --> + <enum name="medium" value="160" /> + <!-- A high density screen, such as a VGA or WVGA screen in a + typical hand-held phone. The constant for this is 240. --> + <enum name="high" value="240" /> + </attr> + </declare-styleable> + + <!-- The <code>supports-screens</code> specifies the screen dimensions an + application supports. By default a modern application supports all + screen sizes and must explicitly disable certain screen sizes here; + older applications are assumed to only support the traditional normal + (HVGA) screen size. Note that screen size is a separate axis from + density, and is determined as the available pixels to an application + after density scaling has been applied. + + <p>This appears as a child tag of the + {@link #AndroidManifest manifest} tag. --> + <declare-styleable name="AndroidManifestSupportsScreens" parent="AndroidManifest"> + <!-- Indicates whether the application supports smaller screen form-factors. + A small screen is defined as one with a smaller aspect ratio than + the traditional HVGA screen; that is, for a portrait screen, less + tall than an HVGA screen. In practice, this means a QVGA low + density or VGA high density screen. An application that does + not support small screens <em>will not be available</em> for + small screen devices, since there is little the platform can do + to make such an application work on a smaller screen. --> + <attr name="smallScreens" format="boolean" /> + <!-- Indicates whether an application supports the normal screen + form-factors. Traditionally this is an HVGA normal density + screen, but WQVGA low density and WVGA high density are also + considered to be normal. This attribute is true by default, + and applications currently should leave it that way. --> + <attr name="normalScreens" format="boolean" /> + <!-- Indicates whether the application supports larger screen form-factors. + A large screen is defined as a screen that is significantly larger + than a normal phone screen, and thus may require some special care + on the application's part to make good use of it. An example would + be a VGA <em>normal density</em> screen, though even larger screens + are certainly possible. An application that does not support + large screens will be placed as a postage stamp on such a + screen, so that it retains the dimensions it was originally + designed for. --> + <attr name="largeScreens" format="boolean" /> </declare-styleable> <!-- The <code>expandable</code> specifies if this package supports screen metrics diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 079baf7..414382d 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1117,11 +1117,20 @@ <public type="attr" name="glEsVersion" /> <public type="attr" name="queryAfterZeroResults" /> <public type="attr" name="dropDownHeight" /> + <public type="attr" name="smallScreens" /> + <public type="attr" name="normalScreens" /> + <public type="attr" name="largeScreens" /> + <public type="attr" name="progressBarStyleInverse" /> + <public type="attr" name="progressBarStyleSmallInverse" /> + <public type="attr" name="progressBarStyleLargeInverse" /> <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" /> <public-padding type="id" name="donut_resource_pad" end="0x01020040" /> + <public type="style" name="Widget.ProgressBar.Inverse" id="0x0103005b" /> + <public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" /> + <public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" /> <public-padding type="style" name="donut_resource_pad" end="0x01030070" /> <public-padding type="string" name="donut_resource_pad" end="0x01040030" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 88464f7..612e6f4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1325,7 +1325,7 @@ <!-- Do not translate. WebView User Agent string --> <string name="web_user_agent"><xliff:g id="x">Mozilla/5.0 (Linux; U; Android %s) - AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1</xliff:g></string> + AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17</xliff:g></string> <!-- Title for a JavaScript dialog. "The page at <url of current page> says:" --> <string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 648a7dd..7d235ec 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -243,7 +243,7 @@ <style name="Widget.ProgressBar"> <item name="android:indeterminateOnly">true</item> - <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item> + <item name="android:indeterminateDrawable">@android:drawable/progress_medium_white</item> <item name="android:indeterminateBehavior">repeat</item> <item name="android:indeterminateDuration">3500</item> <item name="android:minWidth">48dip</item> @@ -253,7 +253,7 @@ </style> <style name="Widget.ProgressBar.Large"> - <item name="android:indeterminateDrawable">@android:drawable/progress_large</item> + <item name="android:indeterminateDrawable">@android:drawable/progress_large_white</item> <item name="android:minWidth">76dip</item> <item name="android:maxWidth">76dip</item> <item name="android:minHeight">76dip</item> @@ -261,13 +261,25 @@ </style> <style name="Widget.ProgressBar.Small"> - <item name="android:indeterminateDrawable">@android:drawable/progress_small</item> + <item name="android:indeterminateDrawable">@android:drawable/progress_small_white</item> <item name="android:minWidth">16dip</item> <item name="android:maxWidth">16dip</item> <item name="android:minHeight">16dip</item> <item name="android:maxHeight">16dip</item> </style> + <style name="Widget.ProgressBar.Inverse"> + <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item> + </style> + + <style name="Widget.ProgressBar.Large.Inverse"> + <item name="android:indeterminateDrawable">@android:drawable/progress_large</item> + </style> + + <style name="Widget.ProgressBar.Small.Inverse"> + <item name="android:indeterminateDrawable">@android:drawable/progress_small</item> + </style> + <style name="Widget.ProgressBar.Small.Title"> <item name="android:indeterminateDrawable">@android:drawable/progress_small_titlebar</item> </style> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index f37d514..bd6e1df 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -154,6 +154,9 @@ <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item> <item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item> <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item> + <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item> + <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item> + <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item> <item name="seekBarStyle">@android:style/Widget.SeekBar</item> <item name="ratingBarStyle">@android:style/Widget.RatingBar</item> <item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item> @@ -233,6 +236,13 @@ <item name="listViewStyle">@android:style/Widget.ListView.White</item> <item name="listDivider">@drawable/divider_horizontal_bright</item> <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item> + + <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item> + <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item> + <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item> + <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item> + <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item> + <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item> </style> <!-- Variant of the light theme with no title bar --> |