summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorMathias Agopian <mathias@google.com>2009-07-01 18:33:18 -0700
committerMathias Agopian <mathias@google.com>2009-07-01 18:33:18 -0700
commitdfe983bd7979ccb1602f29b8f9804c98411d9cd6 (patch)
tree8a5547078b72cb262e54f0640dd4ed746b5805ef /core
parent3a6b160a3b52cd96fb383d5ee93c22e5e938e0e2 (diff)
parent7f32b426cd6a865ac5e6e3e9fa833e9327fb415a (diff)
downloadframeworks_base-dfe983bd7979ccb1602f29b8f9804c98411d9cd6.zip
frameworks_base-dfe983bd7979ccb1602f29b8f9804c98411d9cd6.tar.gz
frameworks_base-dfe983bd7979ccb1602f29b8f9804c98411d9cd6.tar.bz2
Merge commit 'goog/master' into merge_master
Diffstat (limited to 'core')
-rw-r--r--core/java/android/accounts/Constants.java1
-rw-r--r--core/java/android/app/ActivityThread.java7
-rw-r--r--core/java/android/app/BackupAgent.java5
-rw-r--r--core/java/android/app/LauncherActivity.java28
-rw-r--r--core/java/android/app/SearchDialog.java12
-rw-r--r--core/java/android/backup/AbsoluteFileBackupHelper.java66
-rw-r--r--core/java/android/backup/BackupHelperAgent.java2
-rw-r--r--core/java/android/backup/BackupHelperDispatcher.java76
-rw-r--r--core/java/android/backup/BackupManager.java18
-rw-r--r--core/java/android/backup/IBackupManager.aidl39
-rw-r--r--core/java/android/backup/IRestoreObserver.aidl50
-rw-r--r--core/java/android/backup/IRestoreSession.aidl5
-rw-r--r--core/java/android/backup/SharedPreferencesBackupHelper.java2
-rw-r--r--core/java/android/content/pm/ActivityInfo.java10
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java29
-rw-r--r--core/java/android/content/pm/PackageParser.java40
-rw-r--r--core/java/android/content/res/AssetManager.java2
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java33
-rw-r--r--core/java/android/content/res/Configuration.java37
-rw-r--r--core/java/android/content/res/Resources.java5
-rw-r--r--core/java/android/hardware/Camera.java36
-rw-r--r--core/java/android/net/http/RequestHandle.java6
-rw-r--r--core/java/android/os/AsyncTask.java6
-rw-r--r--core/java/android/os/Process.java16
-rw-r--r--core/java/android/preference/PreferenceScreen.java2
-rw-r--r--core/java/android/provider/Browser.java11
-rw-r--r--core/java/android/provider/ContactsContract.java80
-rw-r--r--core/java/android/provider/MediaStore.java5
-rw-r--r--core/java/android/provider/Settings.java56
-rw-r--r--core/java/android/server/BluetoothDeviceService.java55
-rw-r--r--core/java/android/server/BluetoothEventLoop.java25
-rwxr-xr-xcore/java/android/speech/tts/ITts.aidl10
-rwxr-xr-xcore/java/android/speech/tts/TextToSpeech.java391
-rw-r--r--core/java/android/text/format/DateUtils.java58
-rw-r--r--core/java/android/util/DisplayMetrics.java61
-rw-r--r--core/java/android/view/View.java107
-rw-r--r--core/java/android/view/ViewGroup.java6
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java13
-rw-r--r--core/java/android/webkit/CacheLoader.java16
-rw-r--r--core/java/android/webkit/CacheManager.java32
-rw-r--r--core/java/android/webkit/CallbackProxy.java10
-rw-r--r--core/java/android/webkit/DebugFlags.java1
-rw-r--r--core/java/android/webkit/FrameLoader.java2
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java128
-rw-r--r--core/java/android/webkit/LoadListener.java6
-rw-r--r--core/java/android/webkit/WebChromeClient.java18
-rw-r--r--core/java/android/webkit/WebSettings.java62
-rw-r--r--core/java/android/webkit/WebTextView.java59
-rw-r--r--core/java/android/webkit/WebView.java247
-rw-r--r--core/java/android/webkit/WebViewCore.java75
-rw-r--r--core/java/android/webkit/WebViewDatabase.java38
-rw-r--r--core/java/android/webkit/gears/ApacheHttpRequestAndroid.java11
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java6
-rw-r--r--core/java/android/widget/ProgressBar.java11
-rw-r--r--core/java/android/widget/RelativeLayout.java140
-rw-r--r--core/java/com/android/internal/backup/IBackupTransport.aidl14
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java8
-rw-r--r--core/java/com/android/internal/backup/SystemBackupAgent.java35
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java6
-rw-r--r--core/java/com/android/internal/util/BitwiseInputStream.java13
-rw-r--r--core/java/com/android/internal/util/BitwiseOutputStream.java7
-rw-r--r--core/jni/Android.mk4
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/Time.cpp199
-rw-r--r--core/jni/TimeUtils.h89
-rw-r--r--core/jni/android_backup_BackupHelperDispatcher.cpp259
-rw-r--r--core/jni/android_bluetooth_common.cpp380
-rw-r--r--core/jni/android_hardware_Camera.cpp57
-rw-r--r--core/jni/android_text_format_Time.cpp2
-rw-r--r--core/jni/android_util_AssetManager.cpp5
-rw-r--r--core/jni/android_util_Process.cpp6
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/res/drawable/progress.xml40
-rw-r--r--core/res/res/drawable/progress_circular_background.pngbin2044 -> 0 bytes
-rw-r--r--core/res/res/drawable/progress_circular_background_small.pngbin484 -> 0 bytes
-rw-r--r--core/res/res/drawable/progress_circular_indeterminate.pngbin2371 -> 0 bytes
-rw-r--r--core/res/res/drawable/progress_large.xml66
-rw-r--r--core/res/res/drawable/progress_large_white.xml (renamed from core/res/res/drawable/progress_indeterminate.xml)23
-rw-r--r--core/res/res/drawable/progress_medium.xml64
-rw-r--r--core/res/res/drawable/progress_medium_white.xml25
-rw-r--r--core/res/res/drawable/progress_small.xml66
-rw-r--r--core/res/res/drawable/progress_small_titlebar.xml66
-rw-r--r--core/res/res/drawable/progress_small_white.xml25
-rw-r--r--core/res/res/drawable/search_spinner.xml25
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim10.pngbin529 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim11.pngbin525 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim12.pngbin527 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim2.pngbin525 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim3.pngbin522 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim4.pngbin519 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim5.pngbin521 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim6.pngbin509 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim7.pngbin517 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim8.pngbin533 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/search_spinner_anim9.pngbin534 -> 0 bytes
-rw-r--r--core/res/res/drawable/spinner_black_16.pngbin0 -> 291 bytes
-rwxr-xr-xcore/res/res/drawable/spinner_black_20.png (renamed from core/res/res/drawable/search_spinner_anim1.png)bin523 -> 523 bytes
-rw-r--r--core/res/res/drawable/spinner_black_48.pngbin0 -> 1022 bytes
-rw-r--r--core/res/res/drawable/spinner_black_76.pngbin0 -> 1086 bytes
-rw-r--r--core/res/res/drawable/spinner_white_16.png (renamed from core/res/res/drawable/progress_particle.png)bin3058 -> 2968 bytes
-rw-r--r--core/res/res/drawable/spinner_white_48.pngbin0 -> 782 bytes
-rw-r--r--core/res/res/drawable/spinner_white_76.pngbin0 -> 3745 bytes
-rw-r--r--core/res/res/layout/character_picker.xml4
-rw-r--r--core/res/res/values/attrs.xml28
-rw-r--r--core/res/res/values/attrs_manifest.xml58
-rw-r--r--core/res/res/values/public.xml9
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/styles.xml18
-rw-r--r--core/res/res/values/themes.xml10
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
deleted file mode 100644
index 7c637fd..0000000
--- a/core/res/res/drawable/progress_circular_background.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_circular_background_small.png b/core/res/res/drawable/progress_circular_background_small.png
deleted file mode 100644
index 6b8ba9b..0000000
--- a/core/res/res/drawable/progress_circular_background_small.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/progress_circular_indeterminate.png b/core/res/res/drawable/progress_circular_indeterminate.png
deleted file mode 100644
index 125a264..0000000
--- a/core/res/res/drawable/progress_circular_indeterminate.png
+++ /dev/null
Binary files differ
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
deleted file mode 100755
index 9611d97..0000000
--- a/core/res/res/drawable/search_spinner_anim10.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim11.png b/core/res/res/drawable/search_spinner_anim11.png
deleted file mode 100755
index 4261704..0000000
--- a/core/res/res/drawable/search_spinner_anim11.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim12.png b/core/res/res/drawable/search_spinner_anim12.png
deleted file mode 100755
index 0602314..0000000
--- a/core/res/res/drawable/search_spinner_anim12.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim2.png b/core/res/res/drawable/search_spinner_anim2.png
deleted file mode 100755
index 05d58e0..0000000
--- a/core/res/res/drawable/search_spinner_anim2.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim3.png b/core/res/res/drawable/search_spinner_anim3.png
deleted file mode 100755
index 69fa9c1..0000000
--- a/core/res/res/drawable/search_spinner_anim3.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim4.png b/core/res/res/drawable/search_spinner_anim4.png
deleted file mode 100755
index 9201bac..0000000
--- a/core/res/res/drawable/search_spinner_anim4.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim5.png b/core/res/res/drawable/search_spinner_anim5.png
deleted file mode 100755
index f0c7101..0000000
--- a/core/res/res/drawable/search_spinner_anim5.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim6.png b/core/res/res/drawable/search_spinner_anim6.png
deleted file mode 100755
index 99d1d4e..0000000
--- a/core/res/res/drawable/search_spinner_anim6.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim7.png b/core/res/res/drawable/search_spinner_anim7.png
deleted file mode 100755
index 8ca3358..0000000
--- a/core/res/res/drawable/search_spinner_anim7.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim8.png b/core/res/res/drawable/search_spinner_anim8.png
deleted file mode 100755
index 408d723..0000000
--- a/core/res/res/drawable/search_spinner_anim8.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim9.png b/core/res/res/drawable/search_spinner_anim9.png
deleted file mode 100755
index 42a2c65..0000000
--- a/core/res/res/drawable/search_spinner_anim9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable/spinner_black_16.png
new file mode 100644
index 0000000..5ee33ce
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable/search_spinner_anim1.png b/core/res/res/drawable/spinner_black_20.png
index e55b60d..e55b60d 100755
--- a/core/res/res/drawable/search_spinner_anim1.png
+++ b/core/res/res/drawable/spinner_black_20.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable/spinner_black_48.png
new file mode 100644
index 0000000..3a68192
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable/spinner_black_76.png
new file mode 100644
index 0000000..ec57460
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable/progress_particle.png b/core/res/res/drawable/spinner_white_16.png
index 9160108..dd2e1fd 100644
--- a/core/res/res/drawable/progress_particle.png
+++ b/core/res/res/drawable/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable/spinner_white_48.png
new file mode 100644
index 0000000..d25a33e
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable/spinner_white_76.png
new file mode 100644
index 0000000..f53e8ff
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_76.png
Binary files differ
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 -->