diff --git a/Android.mk b/Android.mk
index 41a75c76511..6f1d32be7a8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -171,6 +171,7 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/IUsageStats.aidl \
core/java/com/android/internal/app/IMediaContainerService.aidl \
+ core/java/com/android/internal/app/IAssetRedirectionManager.aidl \
core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
core/java/com/android/internal/backup/IBackupTransport.aidl \
diff --git a/api/current.txt b/api/current.txt
index 086c8914c1d..027efcafaab 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1153,6 +1153,7 @@ package android {
field public static final int holo_purple = 17170458; // 0x106001a
field public static final int holo_red_dark = 17170455; // 0x1060017
field public static final int holo_red_light = 17170454; // 0x1060016
+ field public static final int holo_white_light = 17170460; // 0x106001c
field public static final int primary_text_dark = 17170433; // 0x1060001
field public static final int primary_text_dark_nodisable = 17170434; // 0x1060002
field public static final int primary_text_light = 17170435; // 0x1060003
@@ -1355,6 +1356,7 @@ package android {
field public static final int title_bar = 17301653; // 0x1080095
field public static final int title_bar_tall = 17301670; // 0x10800a6
field public static final int toast_frame = 17301654; // 0x1080096
+ field public static final int weather_condition = 17301684; // 0x10800b4
field public static final int zoom_plate = 17301655; // 0x1080097
}
@@ -1507,6 +1509,7 @@ package android {
field public static final int status_bar_notification_info_overflow = 17039383; // 0x1040017
field public static final int unknownName = 17039374; // 0x104000e
field public static final int untitled = 17039375; // 0x104000f
+ field public static final int weatherpanel_slash = 17039384; // 0x1040018
field public static final int yes = 17039379; // 0x1040013
}
@@ -9644,6 +9647,7 @@ package android.hardware {
method public int getJpegQuality();
method public int getJpegThumbnailQuality();
method public android.hardware.Camera.Size getJpegThumbnailSize();
+ method public java.lang.String getPowerMode();
method public int getMaxExposureCompensation();
method public int getMaxNumDetectedFaces();
method public int getMaxNumFocusAreas();
@@ -9680,6 +9684,7 @@ package android.hardware {
method public java.util.List
<service android:name=".MyAccessibilityService"
- * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE>
+ * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
* <intent-filter>
* <action android:name="android.accessibilityservice.AccessibilityService" />
* </intent-filter>
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 079b9bd2847..22e454f9dc2 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -220,8 +220,6 @@ public AccountManagerService(Context context, PackageManager packageManager,
sThis.set(this);
- UserAccounts accounts = initUser(0);
-
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
@@ -242,6 +240,11 @@ public void onReceive(Context context, Intent intent) {
}, userFilter);
}
+ public void systemReady() {
+ mAuthenticatorCache.generateServicesMap();
+ initUser(0);
+ }
+
private UserAccounts initUser(int userId) {
synchronized (mUsers) {
UserAccounts accounts = mUsers.get(userId);
diff --git a/core/java/android/accounts/ChooseAccountTypeActivity.java b/core/java/android/accounts/ChooseAccountTypeActivity.java
index acc85496ba2..5b3b553fa78 100644
--- a/core/java/android/accounts/ChooseAccountTypeActivity.java
+++ b/core/java/android/accounts/ChooseAccountTypeActivity.java
@@ -134,7 +134,6 @@ private void buildTypeToAuthDescriptionMap() {
if (sequence != null) {
name = sequence.toString();
}
- name = sequence.toString();
} catch (PackageManager.NameNotFoundException e) {
// Nothing we can do much here, just log
if (Log.isLoggable(TAG, Log.WARN)) {
diff --git a/core/java/android/accounts/IAccountAuthenticatorCache.java b/core/java/android/accounts/IAccountAuthenticatorCache.java
index 618771f3541..20dd585a45a 100644
--- a/core/java/android/accounts/IAccountAuthenticatorCache.java
+++ b/core/java/android/accounts/IAccountAuthenticatorCache.java
@@ -60,4 +60,9 @@ RegisteredServicesCache.ServiceInfo getServiceInfo(
*/
void setListener(RegisteredServicesCacheListener listener,
Handler handler);
+
+ /**
+ * Refreshes the authenticator cache.
+ */
+ void generateServicesMap();
}
\ No newline at end of file
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
old mode 100755
new mode 100644
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 92b6f7216c0..14ab59697e5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +28,7 @@
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
@@ -1559,6 +1561,16 @@ public List getRunningExternalApplications() {
return null;
}
}
+ /**
+ * @hide
+ */
+ public Configuration getConfiguration() {
+ try {
+ return ActivityManagerNative.getDefault().getConfiguration();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
/**
* Returns a list of application processes that are running on the device.
@@ -1869,4 +1881,17 @@ public boolean switchUser(int userid) {
return false;
}
}
+
+ /**
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not hold the {@link android.Manifest.permission#CHANGE_CONFIGURATION} permission.
+ *
+ * @hide
+ */
+ public void updateConfiguration(Configuration values) throws SecurityException {
+ try {
+ ActivityManagerNative.getDefault().updateConfiguration(values);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e12fa19f447..f6dff3d4149 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -3692,7 +3692,7 @@ public UserInfo getCurrentUser() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+ mRemote.transact(GET_CURRENT_USER_TRANSACTION, data, reply, 0);
reply.readException();
UserInfo userInfo = UserInfo.CREATOR.createFromParcel(reply);
reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7242029a7c0..8260c685496 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,11 +23,13 @@
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.IIntentReceiver;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageManager;
@@ -36,6 +39,8 @@
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.CustomTheme;
+import android.content.res.PackageRedirectionMap;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDebug;
@@ -47,6 +52,7 @@
import android.net.ProxyProperties;
import android.opengl.GLUtils;
import android.os.AsyncTask;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
@@ -61,6 +67,7 @@
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.os.Trace;
import android.os.UserId;
import android.util.AndroidRuntimeException;
@@ -72,6 +79,7 @@
import android.util.Slog;
import android.view.Display;
import android.view.HardwareRenderer;
+import android.view.InflateException;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewManager;
@@ -81,6 +89,7 @@
import android.view.WindowManagerImpl;
import android.renderscript.RenderScript;
+import com.android.internal.app.IAssetRedirectionManager;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SamplingProfilerIntegration;
@@ -150,6 +159,7 @@ public final class ActivityThread {
static ContextImpl mSystemContext = null;
static IPackageManager sPackageManager;
+ static IAssetRedirectionManager sAssetRedirectionManager;
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
@@ -1468,12 +1478,14 @@ public final boolean queueIdle() {
private static class ResourcesKey {
final private String mResDir;
final private float mScale;
+ final private boolean mIsThemeable;
final private int mHash;
- ResourcesKey(String resDir, float scale) {
+ ResourcesKey(String resDir, float scale, boolean isThemeable) {
mResDir = resDir;
mScale = scale;
- mHash = mResDir.hashCode() << 2 + (int) (mScale * 2);
+ mIsThemeable = isThemeable;
+ mHash = mResDir.hashCode() << 3 + ((mIsThemeable ? 1 : 0) << 2) + (int) (mScale * 2);
}
@Override
@@ -1487,7 +1499,8 @@ public boolean equals(Object obj) {
return false;
}
ResourcesKey peer = (ResourcesKey) obj;
- return mResDir.equals(peer.mResDir) && mScale == peer.mScale;
+ return mResDir.equals(peer.mResDir) && mScale == peer.mScale &&
+ mIsThemeable == peer.mIsThemeable;
}
}
@@ -1518,6 +1531,18 @@ public static IPackageManager getPackageManager() {
return sPackageManager;
}
+ // NOTE: this method can return null if the SystemServer is still
+ // initializing (for example, of another SystemServer component is accessing
+ // a resources object)
+ public static IAssetRedirectionManager getAssetRedirectionManager() {
+ if (sAssetRedirectionManager != null) {
+ return sAssetRedirectionManager;
+ }
+ IBinder b = ServiceManager.getService("assetredirection");
+ sAssetRedirectionManager = IAssetRedirectionManager.Stub.asInterface(b);
+ return sAssetRedirectionManager;
+ }
+
DisplayMetrics getDisplayMetricsLocked(CompatibilityInfo ci, boolean forceUpdate) {
DisplayMetrics dm = mDisplayMetrics.get(ci);
if (dm != null && !forceUpdate) {
@@ -1556,7 +1581,7 @@ Configuration applyConfigCompatMainThread(Configuration config, CompatibilityInf
* null.
*/
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
- ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
+ ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale, compInfo.isThemeable);
Resources r;
synchronized (mPackages) {
// Resources is app scale dependent.
@@ -1582,10 +1607,23 @@ Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
//}
AssetManager assets = new AssetManager();
+ assets.setThemeSupport(compInfo.isThemeable);
if (assets.addAssetPath(resDir) == 0) {
return null;
}
+ /* Attach theme information to the resulting AssetManager when appropriate. */
+ Configuration config = getConfiguration();
+ if (compInfo.isThemeable && config != null) {
+ if (config.customTheme == null) {
+ config.customTheme = CustomTheme.getBootTheme();
+ }
+
+ if (!TextUtils.isEmpty(config.customTheme.getThemePackageName())) {
+ attachThemeAssets(assets, config.customTheme);
+ }
+ }
+
//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
r = new Resources(assets, metrics, getConfiguration(), compInfo);
@@ -1611,6 +1649,81 @@ Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
}
}
+ private void detachThemeAssets(AssetManager assets) {
+ String themePackageName = assets.getThemePackageName();
+ int themeCookie = assets.getThemeCookie();
+ if (!TextUtils.isEmpty(themePackageName) && themeCookie != 0) {
+ assets.detachThemePath(themePackageName, themeCookie);
+ assets.setThemePackageName(null);
+ assets.setThemeCookie(0);
+ assets.clearRedirections();
+ }
+ }
+
+ /**
+ * Attach the necessary theme asset paths and meta information to convert an
+ * AssetManager to being globally "theme-aware".
+ *
+ * @param assets
+ * @param theme
+ * @return true if the AssetManager is now theme-aware; false otherwise
+ * this can fail, for example, if the theme package has been
+ * removed and the theme manager has yet to revert formally back to
+ * the framework default.
+ */
+ private boolean attachThemeAssets(AssetManager assets, CustomTheme theme) {
+ IAssetRedirectionManager rm = getAssetRedirectionManager();
+ if (rm == null) {
+ return false;
+ }
+ PackageInfo pi = null;
+ try {
+ pi = getPackageManager().getPackageInfo(theme.getThemePackageName(), 0, 0);
+ } catch (RemoteException e) {
+ }
+ if (pi != null && pi.applicationInfo != null && pi.themeInfos != null) {
+ String themeResDir = pi.applicationInfo.publicSourceDir;
+ int cookie = assets.attachThemePath(themeResDir);
+ if (cookie != 0) {
+ String themePackageName = theme.getThemePackageName();
+ String themeId = theme.getThemeId();
+ int N = assets.getBasePackageCount();
+ for (int i = 0; i < N; i++) {
+ String packageName = assets.getBasePackageName(i);
+ int packageId = assets.getBasePackageId(i);
+
+ /*
+ * For now, we only consider redirections coming from the
+ * framework or regular android packages. This excludes
+ * themes and other specialty APKs we are not aware of.
+ */
+ if (packageId != 0x01 && packageId != 0x7f) {
+ continue;
+ }
+
+ try {
+ PackageRedirectionMap map = rm.getPackageRedirectionMap(themePackageName, themeId,
+ packageName);
+ if (map != null) {
+ assets.addRedirections(map);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure accessing package redirection map, removing theme support.");
+ assets.detachThemePath(themePackageName, cookie);
+ return false;
+ }
+ }
+
+ assets.setThemePackageName(theme.getThemePackageName());
+ assets.setThemeCookie(cookie);
+ return true;
+ } else {
+ Log.e(TAG, "Unable to attach theme assets at " + themeResDir);
+ }
+ }
+ return false;
+ }
+
/**
* Creates the top level resources for the given package.
*/
@@ -2056,6 +2169,16 @@ private Activity performLaunchActivity(ActivityClientRecord r, Intent customInte
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
+ if (e instanceof InflateException) {
+ Log.e(TAG, "Failed to inflate", e);
+ String pkg = null;
+ if (r.packageInfo != null && !TextUtils.isEmpty(r.packageInfo.getPackageName())) {
+ pkg = r.packageInfo.getPackageName();
+ }
+ Intent intent = new Intent(Intent.ACTION_APP_LAUNCH_FAILURE,
+ (pkg != null)? Uri.fromParts("package", pkg, null) : null);
+ getSystemContext().sendBroadcast(intent);
+ }
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
@@ -3635,7 +3758,7 @@ public final void applyConfigurationToResources(Configuration config) {
}
}
- final boolean applyConfigurationToResourcesLocked(Configuration config,
+ final int applyConfigurationToResourcesLocked(Configuration config,
CompatibilityInfo compat) {
if (mResConfiguration == null) {
mResConfiguration = new Configuration();
@@ -3643,7 +3766,7 @@ final boolean applyConfigurationToResourcesLocked(Configuration config,
if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+ mResConfiguration.seq + ", newSeq=" + config.seq);
- return false;
+ return 0;
}
int changes = mResConfiguration.updateFrom(config);
DisplayMetrics dm = getDisplayMetricsLocked(null, true);
@@ -3676,7 +3799,20 @@ final boolean applyConfigurationToResourcesLocked(Configuration config,
if (r != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
+ boolean themeChanged = (changes & ActivityInfo.CONFIG_THEME_RESOURCE) != 0;
+ if (themeChanged) {
+ AssetManager am = r.getAssets();
+ if (am.hasThemeSupport()) {
+ detachThemeAssets(am);
+ if (!TextUtils.isEmpty(config.customTheme.getThemePackageName())) {
+ attachThemeAssets(am, config.customTheme);
+ }
+ }
+ }
r.updateConfiguration(config, dm, compat);
+ if (themeChanged) {
+ r.updateStringCache();
+ }
//Slog.i(TAG, "Updated app resources " + v.getKey()
// + " " + r + ": " + r.getConfiguration());
} else {
@@ -3685,7 +3821,7 @@ final boolean applyConfigurationToResourcesLocked(Configuration config,
}
}
- return changes != 0;
+ return changes;
}
final Configuration applyCompatConfiguration() {
@@ -3706,6 +3842,8 @@ final void handleConfigurationChanged(Configuration config, CompatibilityInfo co
ArrayList callbacks = null;
int configDiff = 0;
+ int diff = 0;
+
synchronized (mPackages) {
if (mPendingConfiguration != null) {
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
@@ -3721,7 +3859,7 @@ final void handleConfigurationChanged(Configuration config, CompatibilityInfo co
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
+ config);
- applyConfigurationToResourcesLocked(config, compat);
+ diff = applyConfigurationToResourcesLocked(config, compat);
if (mConfiguration == null) {
mConfiguration = new Configuration();
@@ -3743,7 +3881,20 @@ final void handleConfigurationChanged(Configuration config, CompatibilityInfo co
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i");
Looper.prepareMainLooper();
- if (sMainThreadHandler == null) {
- sMainThreadHandler = new Handler();
- }
ActivityThread thread = new ActivityThread();
thread.attach(false);
+ if (sMainThreadHandler == null) {
+ sMainThreadHandler = thread.getHandler();
+ }
+
AsyncTask.init();
if (false) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 191a69612a1..17f82d78c3a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -421,6 +421,16 @@ public List getInstalledPackages(int flags) {
}
}
+ @SuppressWarnings("unchecked")
+ @Override
+ public List getInstalledThemePackages() {
+ try {
+ return mPM.getInstalledThemePackages();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
@SuppressWarnings("unchecked")
@Override
public List getInstalledApplications(int flags) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b902550d145..50fe4ed4fda 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,15 +19,17 @@
import com.android.internal.policy.PolicyManager;
+import android.accounts.AccountManager;
+import android.accounts.IAccountManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.IContentProvider;
+import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.IIntentReceiver;
import android.content.IntentSender;
import android.content.ReceiverCallNotAllowedException;
import android.content.ServiceConnection;
@@ -36,6 +39,8 @@
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.content.res.CustomTheme;
import android.content.res.Resources;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
@@ -361,10 +366,10 @@ public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
- registerService(LOCATION_SERVICE, new StaticServiceFetcher() {
- public Object createStaticService() {
+ registerService(LOCATION_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(LOCATION_SERVICE);
- return new LocationManager(ILocationManager.Stub.asInterface(b));
+ return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}});
registerService(NETWORK_POLICY_SERVICE, new ServiceFetcher() {
@@ -511,6 +516,20 @@ public Resources getResources() {
return mResources;
}
+ /**
+ * Refresh resources object which may have been changed by a theme
+ * configuration change.
+ */
+ /* package */ void refreshResourcesIfNecessary() {
+ if (mResources == Resources.getSystem()) {
+ return;
+ }
+
+ if (mPackageInfo.mCompatibilityInfo.get().isThemeable) {
+ mTheme = null;
+ }
+ }
+
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 3f0b4d4add9..d1688004e52 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -33,8 +33,8 @@
/**
* A simple dialog containing an {@link android.widget.DatePicker}.
*
- * See the Date Picker
- * tutorial.
+ * See the Pickers
+ * guide.
*/
public class DatePickerDialog extends AlertDialog implements OnClickListener,
OnDateChangedListener {
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 17700f91213..0b1c5240b3b 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -344,6 +344,13 @@ public static class Request {
*/
public static final int NETWORK_WIFI = 1 << 1;
+ /**
+ * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
+ * {@link ConnectivityManager#TYPE_BLUETOOTH}.
+ * @hide
+ */
+ public static final int NETWORK_BLUETOOTH = 1 << 2;
+
private Uri mUri;
private Uri mDestinationUri;
private List> mRequestHeaders = new ArrayList>();
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/app/IThumbnailReceiver.aidl b/core/java/android/app/IThumbnailReceiver.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index c34c1639dcc..b0bfe743fd8 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -60,7 +60,7 @@ public MediaRouteButton(Context context) {
}
public MediaRouteButton(Context context, AttributeSet attrs) {
- this(context, null, com.android.internal.R.attr.mediaRouteButtonStyle);
+ this(context, attrs, com.android.internal.R.attr.mediaRouteButtonStyle);
}
public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ceb8cde5087..cb83dc2cdcb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -897,12 +897,16 @@ public String toString() {
* Builder class for {@link Notification} objects.
*
* Provides a convenient way to set the various fields of a {@link Notification} and generate
- * content views using the platform's notification layout template.
+ * content views using the platform's notification layout template. If your app supports
+ * versions of Android as old as API level 4, you can instead use
+ * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
+ * available in the Android Support
+ * library.
*
- * Example:
+ * Example:
*
*
- * Notification noti = new Notification.Builder()
+ * Notification noti = new Notification.Builder(mContext)
* .setContentTitle("New mail from " + sender.toString())
* .setContentText(subject)
* .setSmallIcon(R.drawable.new_mail)
@@ -1731,6 +1735,9 @@ public BigPictureStyle setSummaryText(CharSequence cs) {
return this;
}
+ /**
+ * Provide the bitmap to be used as the payload for the BigPicture notification.
+ */
public BigPictureStyle bigPicture(Bitmap b) {
mPicture = b;
return this;
@@ -1809,6 +1816,10 @@ public BigTextStyle setSummaryText(CharSequence cs) {
return this;
}
+ /**
+ * Provide the longer text to be displayed in the big form of the
+ * template in place of the content text.
+ */
public BigTextStyle bigText(CharSequence cs) {
mBigText = cs;
return this;
@@ -1889,6 +1900,9 @@ public InboxStyle setSummaryText(CharSequence cs) {
return this;
}
+ /**
+ * Append a line to the digest section of the Inbox notification.
+ */
public InboxStyle addLine(CharSequence cs) {
mTexts.add(cs);
return this;
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index cb43d4c527f..02cf3aa1333 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -142,7 +142,7 @@
* to the service. The service will remain running as long as the connection
* is established (whether or not the client retains a reference on the
* service's IBinder). Usually the IBinder returned is for a complex
- * interface that has been written
+ * interface that has been written
* in aidl.
*
* A service can be both started and have connections bound to it. In such
@@ -473,7 +473,7 @@ public void onTrimMemory(int level) {
* Return the communication channel to the service. May return null if
* clients can not bind to the service. The returned
* {@link android.os.IBinder} is usually for a complex interface
- * that has been described using
+ * that has been described using
* aidl.
*
*
Note that unlike other application components, calls on to the
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index d773bc8a444..952227fe0c5 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -30,8 +30,8 @@
/**
* A dialog that prompts the user for the time of day using a {@link TimePicker}.
*
- * See the Time Picker
- * tutorial.
+ * See the Pickers
+ * guide.
*/
public class TimePickerDialog extends AlertDialog
implements OnClickListener, OnTimeChangedListener {
diff --git a/core/java/android/app/VibrationPickerDialog.java b/core/java/android/app/VibrationPickerDialog.java
new file mode 100644
index 00000000000..d88d2b2987e
--- /dev/null
+++ b/core/java/android/app/VibrationPickerDialog.java
@@ -0,0 +1,190 @@
+
+package android.app;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.media.VibrationPattern;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Vibrator;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+
+import java.io.Serializable;
+
+import com.android.internal.R;
+
+public class VibrationPickerDialog extends DialogFragment {
+
+ private static final String TAG = "VibrationPickerDialog";
+
+ private final int VIB_OK = 10;
+ private final int VIB_CANCEL = 11;
+ private final int VIB_DEL = 12;
+
+ private boolean mIsDel;
+ private Context mContext;
+ private Handler mHandler;
+ private Vibrator mVibrator;
+ private AlertDialog.Builder mBuilder;
+ private VibrationPattern mPattern;
+
+ public static VibrationPickerDialog newInstance(Handler handler, boolean isDel,
+ String selectedUri) {
+ VibrationPickerDialog vpd = new VibrationPickerDialog();
+
+ Bundle args = new Bundle();
+ args.putBoolean("isdel", isDel);
+ if (selectedUri == null) {
+ args.putString("uri", "");
+ } else {
+ args.putString("uri", selectedUri);
+ }
+ args.putSerializable("handler", new HandlerHolder(handler));
+ vpd.setArguments(args);
+ return vpd;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mIsDel = getArguments().getBoolean("isdel");
+ String mUriString = getArguments().getString("uri");
+ mHandler = ((HandlerHolder) getArguments().getSerializable("handler")).getHandler();
+ mContext = getActivity();
+ mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+
+ final Uri allVibrations = Uri.parse(VibrationPattern.URI);
+ final Cursor vibrations = mContext.getContentResolver().
+ query(allVibrations, null, null, null, null);
+
+ vibrations.moveToFirst();
+ int ID = -1;
+ if (!mUriString.isEmpty()) {
+ Uri mUri = Uri.parse(mUriString);
+ do {
+ try {
+ if (Integer.parseInt(mUri.getLastPathSegment()) == vibrations.getInt(0)) {
+ ID = vibrations.getPosition();
+ }
+ } catch (Exception ex) {
+ // nothing to do here
+ }
+ } while (vibrations.moveToNext());
+ }
+ final int selectedID = ID;
+ final SimpleCursorAdapter adapter = new SimpleCursorAdapter(mContext,
+ android.R.layout.simple_list_item_single_choice,
+ vibrations,
+ new String[] {
+ "name"
+ },
+ new int[] {
+ android.R.id.text1
+ }, 0) {
+
+ @Override
+ public Object getItem(int pos) {
+ vibrations.moveToPosition(pos);
+ int id = vibrations.getInt(0);
+ setSelectedVibration(new VibrationPattern(
+ Uri.parse(VibrationPattern.URI + "/" + id), mContext));
+ return getSelectedVibration();
+ }
+ };
+
+ LayoutInflater factory = LayoutInflater.from(mContext);
+ final View vibListView = factory.inflate(R.layout.vibration_picker_dialog, null);
+
+ return new AlertDialog.Builder(mContext)
+ .setTitle(
+ mIsDel ? R.string.vibration_picker_del_title
+ : R.string.vibration_picker_title)
+ .setIcon(
+ mIsDel ? R.drawable.ic_dialog_alert
+ : 0)
+ .setView(vibListView)
+ .setSingleChoiceItems(adapter, selectedID,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ ((VibrationPattern) adapter.getItem(which)).play();
+ }
+ })
+ .setPositiveButton(mIsDel ? R.string.delete : R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ if (mIsDel) {
+ delVib(getSelectedVibration());
+ adapter.notifyDataSetChanged();
+ } else {
+ selectVib(getSelectedVibration());
+ }
+ stopAllVibrations();
+ }
+ })
+ .setNegativeButton(R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ stopAllVibrations();
+ sendCancel();
+ }
+ })
+ .create();
+ }
+
+ private void selectVib(VibrationPattern vib) {
+ final Message m = new Message();
+ m.obj = vib;
+ m.what = VIB_OK;
+ mHandler.sendMessage(m);
+ }
+
+ private void sendCancel() {
+ final Message m = new Message();
+ m.what = VIB_CANCEL;
+ mHandler.sendMessage(m);
+ }
+
+ private void delVib(VibrationPattern vib) {
+ final Message m = new Message();
+ m.obj = vib;
+ m.what = VIB_DEL;
+ mHandler.sendMessage(m);
+ }
+
+ public void stopAllVibrations() {
+ if (mVibrator != null) {
+ mVibrator.cancel();
+ }
+ }
+
+ private void setSelectedVibration(VibrationPattern pattern) {
+ mPattern = pattern;
+ }
+
+ private VibrationPattern getSelectedVibration() {
+ return mPattern;
+ }
+
+ static class HandlerHolder implements Serializable {
+ Handler tHandler;
+
+ public HandlerHolder(Handler handler) {
+ tHandler = handler;
+ }
+
+ public Handler getHandler() {
+ return tHandler;
+ }
+ }
+}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index d7f1c9f0fab..f859599e18c 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -64,7 +64,9 @@ static public native int backupToTar(String packageName, String domain,
/**
* Copy data from a socket to the given File location on permanent storage. The
- * modification time and access mode of the resulting file will be set if desired.
+ * modification time and access mode of the resulting file will be set if desired,
+ * although group/all rwx modes will be stripped: the restored file will not be
+ * accessible from outside the target application even if the original file was.
* If the {@code type} parameter indicates that the result should be a directory,
* the socket parameter may be {@code null}; even if it is valid, no data will be
* read from it in this case.
@@ -79,8 +81,9 @@ static public native int backupToTar(String packageName, String domain,
* @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data
* or {@link BackupAgent#TYPE_DIRECTORY} for a directory.
* @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on
- * the output file or directory. If this parameter is negative then neither
- * the mode nor the mtime parameters will be used.
+ * the output file or directory. group/all rwx modes are stripped even if set
+ * in this parameter. If this parameter is negative then neither
+ * the mode nor the mtime values will be applied to the restored file.
* @param mtime A timestamp in the standard Unix epoch that will be imposed as the
* last modification time of the output file. if the {@code mode} parameter is
* negative then this parameter will be ignored.
@@ -105,8 +108,6 @@ static public void restoreFile(ParcelFileDescriptor data,
if (!parent.exists()) {
// in practice this will only be for the default semantic directories,
// and using the default mode for those is appropriate.
- // TODO: support the edge case of apps that have adjusted the
- // permissions on these core directories
parent.mkdirs();
}
out = new FileOutputStream(outFile);
@@ -146,6 +147,8 @@ static public void restoreFile(ParcelFileDescriptor data,
// Now twiddle the state to match the backup, assuming all went well
if (mode >= 0 && outFile != null) {
try {
+ // explicitly prevent emplacement of files accessible by outside apps
+ mode &= 0700;
Libcore.os.chmod(outFile.getPath(), (int)mode);
} catch (ErrnoException e) {
e.rethrowAsIOException();
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
old mode 100755
new mode 100644
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 56e17354cca..41e0184bc1b 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -321,6 +321,9 @@ public final class BluetoothDevice implements Parcelable {
/**@hide*/
public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2;
+ /**@hide*/
+ public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3;
+
/**
* Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
* Contains package name to return reply intent to.
@@ -622,9 +625,9 @@ public boolean setAlias(String alias) {
* @hide
*/
public String getAliasName() {
- String name = getAlias();
+ String name = getName();
if (name == null) {
- name = getName();
+ name = getAlias();
}
return name;
}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 59622351814..1a0bd0202b1 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -56,6 +56,10 @@ public final class BluetoothUuid {
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
public static final ParcelUuid Hid =
ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
+ public static final ParcelUuid MessageAccessServer =
+ ParcelUuid.fromString("00001132-0000-1000-8000-00805f9b34fb");
+ public static final ParcelUuid MessageNotificationServer =
+ ParcelUuid.fromString("00001133-0000-1000-8000-00805f9b34fb");
public static final ParcelUuid PANU =
ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid NAP =
@@ -67,7 +71,7 @@ public final class BluetoothUuid {
public static final ParcelUuid[] RESERVED_UUIDS = {
AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
- ObexObjectPush, PANU, NAP};
+ ObexObjectPush, MessageAccessServer, MessageNotificationServer, PANU, NAP};
public static boolean isAudioSource(ParcelUuid uuid) {
return uuid.equals(AudioSource);
@@ -131,6 +135,14 @@ public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
return false;
}
+ public static boolean isMessageAccessServer(ParcelUuid uuid) {
+ return uuid.equals(MessageAccessServer);
+ }
+
+ public static boolean isMessageNotificationServer(ParcelUuid uuid) {
+ return uuid.equals(MessageNotificationServer);
+ }
+
/**
* Returns true if there any common ParcelUuids in uuidA and uuidB.
*
diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java
index 9ef2eb5f3ce..54ed5eda04f 100644
--- a/core/java/android/bluetooth/HeadsetBase.java
+++ b/core/java/android/bluetooth/HeadsetBase.java
@@ -109,6 +109,12 @@ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter,
private native void initializeNativeDataNative(int socketFd);
+ // interface for dealing with special input situations
+ public interface SpecialPDUInputHandler {
+ void handleInput(String input);
+ }
+ public SpecialPDUInputHandler specialPDUInputHandler = null;
+
/* Process an incoming AT command line
*/
protected void handleInput(String input) {
@@ -158,10 +164,20 @@ public void startEventThread() {
public void run() {
int last_read_error;
while (!mEventThreadInterrupted) {
- String input = readNative(500);
- if (input != null) {
- handleInput(input);
+
+ String input;
+ if (null == specialPDUInputHandler) {
+ input = readNative(500);
+ if (input != null) {
+ handleInput(input);
+ }
} else {
+ input = readNativePDUStream((500));
+ if (input != null) {
+ specialPDUInputHandler.handleInput(input);
+ }
+ }
+ if (null == input) {
last_read_error = getLastReadStatusNative();
if (last_read_error != 0) {
Log.i(TAG, "headset read error " + last_read_error);
@@ -181,6 +197,7 @@ public void run() {
}
private native String readNative(int timeout_ms);
+ private native String readNativePDUStream(int timeout_ms);
private native int getLastReadStatusNative();
private void stopEventThread() {
@@ -275,6 +292,15 @@ public synchronized boolean sendURC(String urc) {
return true;
}
private native boolean sendURCNative(String urc);
+ private native boolean sendURCNativeChars(String urc);
+
+ public synchronized boolean sendURCChars(String urc) {
+ if (urc.length() > 0) {
+ boolean ret = sendURCNativeChars(urc);
+ return ret;
+ }
+ return true;
+ }
private synchronized void acquireWakeLock() {
if (!mWakeLock.isHeld()) {
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 9ddb2a6f7bd..446f1af4117 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -735,7 +735,7 @@ public final PendingResult getPendingResult() {
/**
* Control inclusion of debugging help for mismatched
- * calls to {@ Context#registerReceiver(BroadcastReceiver, IntentFilter)
+ * calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver()}.
* If called with true, before given to registerReceiver(), then the
* callstack of the following {@link Context#unregisterReceiver(BroadcastReceiver)
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0a5a26a979d..0da80f3530f 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -516,7 +516,7 @@ public final OutputStream openOutputStream(Uri uri, String mode)
* ContentProvider.openFile}.
* @return Returns a new ParcelFileDescriptor pointing to the file. You
* own this descriptor and are responsible for closing it when done.
- * @throws FileNotFoundException Throws FileNotFoundException of no
+ * @throws FileNotFoundException Throws FileNotFoundException if no
* file exists under the URI or the mode is invalid.
* @see #openAssetFileDescriptor(Uri, String)
*/
@@ -1047,9 +1047,9 @@ public final IContentProvider acquireProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
- String auth = uri.getAuthority();
+ final String auth = uri.getAuthority();
if (auth != null) {
- return acquireProvider(mContext, uri.getAuthority());
+ return acquireProvider(mContext, auth);
}
return null;
}
@@ -1066,9 +1066,9 @@ public final IContentProvider acquireExistingProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
- String auth = uri.getAuthority();
+ final String auth = uri.getAuthority();
if (auth != null) {
- return acquireExistingProvider(mContext, uri.getAuthority());
+ return acquireExistingProvider(mContext, auth);
}
return null;
}
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index f827c3de43c..1a07504f2fb 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -132,6 +132,9 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
/*package*/ ContentService(Context context, boolean factoryTest) {
mContext = context;
mFactoryTest = factoryTest;
+ }
+
+ public void systemReady() {
getSyncManager();
}
@@ -524,7 +527,7 @@ public void removeStatusChangeListener(ISyncStatusObserver callback) {
}
}
- public static IContentService main(Context context, boolean factoryTest) {
+ public static ContentService main(Context context, boolean factoryTest) {
ContentService service = new ContentService(context, factoryTest);
ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
return service;
diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3fdf451dcff..58683034c23 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2284,6 +2285,19 @@ public static Intent createChooser(Intent target, CharSequence title) {
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
+ /**
+ * Broadcast Action: Indicate that unrecoverable error happened during app launch.
+ * Could indicate that curently applied theme is malicious.
+ * @hide
+ */
+ public static final String ACTION_APP_LAUNCH_FAILURE = "com.tmobile.intent.action.APP_LAUNCH_FAILURE";
+
+ /**
+ * Broadcast Action: Request to reset the unrecoverable errors count to 0.
+ * @hide
+ */
+ public static final String ACTION_APP_LAUNCH_FAILURE_RESET = "com.tmobile.intent.action.APP_LAUNCH_FAILURE_RESET";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -2452,6 +2466,14 @@ public static Intent createChooser(Intent target, CharSequence title) {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
+ /**
+ * Used to indicate that a theme package has been installed or un-installed.
+ *
+ * @hide
+ */
+ public static final String CATEGORY_THEME_PACKAGE_INSTALLED_STATE_CHANGE =
+ "com.tmobile.intent.category.THEME_PACKAGE_INSTALL_STATE_CHANGE";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Application launch intent categories (see addCategory()).
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index badcb03a0e4..9771cd9f095 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -17,6 +17,7 @@
package android.content;
import com.android.internal.R;
+import com.android.internal.app.ThemeUtils;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -136,6 +137,7 @@ public class SyncManager implements OnAccountsUpdateListener {
private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS;
private Context mContext;
+ private Context mUiContext;
private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
@@ -191,6 +193,12 @@ public void onReceive(Context context, Intent intent) {
}
};
+ private BroadcastReceiver mThemeChangeReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ mUiContext = null;
+ }
+ };
+
private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (getConnectivityManager().getBackgroundDataSetting()) {
@@ -398,6 +406,8 @@ public void onServiceChanged(SyncAdapterType type, boolean removed) {
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mUserIntentReceiver, intentFilter);
+ ThemeUtils.registerThemeChangeReceiver(mContext, mThemeChangeReceiver);
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -901,6 +911,13 @@ private void onUserRemoved(Intent intent) {
}
}
+ private Context getUiContext() {
+ if (mUiContext == null) {
+ mUiContext = ThemeUtils.createUiContext(mContext);
+ }
+ return mUiContext != null ? mUiContext : mContext;
+ }
+
/**
* @hide
*/
@@ -2497,7 +2514,7 @@ private void installHandleTooManyDeletesNotification(Account account, String aut
new Notification(R.drawable.stat_notify_sync_error,
mContext.getString(R.string.contentServiceSync),
System.currentTimeMillis());
- notification.setLatestEventInfo(mContext,
+ notification.setLatestEventInfo(getUiContext(),
mContext.getString(R.string.contentServiceSyncNotificationTitle),
String.format(tooManyDeletesDescFormat.toString(), authorityName),
pendingIntent);
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 6b16e7487fe..e057e39d939 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -320,6 +321,10 @@ public class ActivityInfo extends ComponentInfo
* {@link android.R.attr#configChanges} attribute.
*/
public static final int CONFIG_ORIENTATION = 0x0080;
+ /**
+ * @hide
+ */
+ public static final int CONFIG_THEME_RESOURCE = 0x008000;
/**
* Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the screen layout. Set from the
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e1434b3bad6..e55ffacd5e6 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -431,6 +432,30 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* @hide
*/
public int enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ /**
+ * Is given application theme agnostic, i.e. behaves properly when default theme is changed.
+ * {@hide}
+ */
+ public boolean isThemeable = false;
+
+ private static final String PLUTO_SCHEMA = "http://www.w3.org/2001/pluto.html";
+
+ /**
+ * @hide
+ */
+ public static final String PLUTO_ISTHEMEABLE_ATTRIBUTE_NAME = "isThemeable";
+
+ /**
+ * @hide
+ */
+ public static final String PLUTO_HANDLE_THEME_CONFIG_CHANGES_ATTRIBUTE_NAME = "handleThemeConfigChanges";
+
+ /**
+ * @hide
+ */
+ public static boolean isPlutoNamespace(String namespace) {
+ return namespace != null && namespace.equalsIgnoreCase(PLUTO_SCHEMA);
+ }
/**
* For convenient access to package's install location.
@@ -541,6 +566,7 @@ public ApplicationInfo(ApplicationInfo orig) {
manageSpaceActivityName = orig.manageSpaceActivityName;
descriptionRes = orig.descriptionRes;
uiOptions = orig.uiOptions;
+ isThemeable = orig.isThemeable;
}
@@ -580,6 +606,7 @@ public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(backupAgentName);
dest.writeInt(descriptionRes);
dest.writeInt(uiOptions);
+ dest.writeInt(isThemeable? 1 : 0);
}
public static final Parcelable.Creator CREATOR
@@ -618,6 +645,7 @@ private ApplicationInfo(Parcel source) {
backupAgentName = source.readString();
descriptionRes = source.readInt();
uiOptions = source.readInt();
+ isThemeable = source.readInt() != 0;
}
/**
diff --git a/core/java/android/content/pm/BaseThemeInfo.java b/core/java/android/content/pm/BaseThemeInfo.java
new file mode 100644
index 00000000000..0171137bac1
--- /dev/null
+++ b/core/java/android/content/pm/BaseThemeInfo.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2010, T-Mobile USA, Inc.
+ *
+ * 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.content.pm;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+import android.util.AttributeSet;
+import android.content.res.Resources;
+
+/**
+ * @hide
+ */
+public class BaseThemeInfo implements Parcelable {
+
+ /**
+ * Wallpaper drawable.
+ *
+ * @see wallpaperImage attribute
+ */
+ public int wallpaperResourceId;
+
+ /**
+ * The resource id of theme thumbnail.
+ * Specifies a theme thumbnail image resource as @drawable/foo.
+ *
+ * @see thumbnail attribute
+ *
+ */
+ public int thumbnailResourceId;
+
+ /**
+ * The theme id, which does not change when the theme is modified.
+ * Specifies an Android UI Style using style name.
+ *
+ * @see themeId attribute
+ *
+ */
+ public String themeId;
+
+ /**
+ * The style resource id of Android UI Style, supplied by the resource commpiler.
+ * Specifies an Android UI Style id.
+ *
+ * @see styleId attribute
+ *
+ */
+ public int styleResourceId = 0;
+
+ /**
+ * The name of the theme (as displayed by UI).
+ *
+ * @see name attribute
+ *
+ */
+ public String name;
+
+ /**
+ * The name of the call ringtone audio file.
+ * Specifies a relative path in assets subfolder.
+ * If the parent's name is "locked" - DRM protected.
+ *
+ * @see ringtoneFileName attribute
+ *
+ */
+ public String ringtoneFileName;
+
+ /**
+ * The name of the call ringtone as shown to user.
+ *
+ * @see ringtoneName attribute
+ *
+ */
+ public String ringtoneName;
+
+ /**
+ * The name of the notification ringtone audio file.
+ * Specifies a relative path in assets subfolder.
+ * If the parent's name is "locked" - DRM protected.
+ *
+ * @see notificationRingtoneFileName attribute
+ *
+ */
+ public String notificationRingtoneFileName;
+
+ /**
+ * The name of the notification ringtone as shown to user.
+ *
+ * @see notificationRingtoneName attribute
+ *
+ */
+ public String notificationRingtoneName;
+
+ /**
+ * The author name of the theme package.
+ *
+ * @see author attribute
+ *
+ */
+ public String author;
+
+ /**
+ * The copyright text.
+ *
+ * @see copyright attribute
+ *
+ */
+ public String copyright;
+
+ /**
+ * {@hide}
+ */
+ // There is no corresposponding flag in manifest file
+ // This flag is set to true iff any media resource is DRM protected
+ public boolean isDrmProtected = false;
+
+ /**
+ * The name of the "main" theme style (as displayed by UI).
+ *
+ * @see themeStyleName attribute
+ *
+ */
+ public String themeStyleName;
+
+ /**
+ * Preview image drawable.
+ *
+ * @see preview attribute
+ */
+ public int previewResourceId;
+
+ /**
+ * The name of a sound pack.
+ *
+ * @see soundpack attribute
+ *
+ */
+ public String soundPackName;
+
+
+ private static final String LOCKED_NAME = "locked/";
+
+ /*
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshalled
+ * by the Parcelable.
+ *
+ * @see android.os.Parcelable#describeContents()
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /*
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ *
+ * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(wallpaperResourceId);
+ dest.writeInt(thumbnailResourceId);
+ dest.writeString(themeId);
+ dest.writeInt(styleResourceId);
+ dest.writeString(name);
+ dest.writeString(ringtoneFileName);
+ dest.writeString(notificationRingtoneFileName);
+ dest.writeString(ringtoneName);
+ dest.writeString(notificationRingtoneName);
+ dest.writeString(author);
+ dest.writeString(copyright);
+ dest.writeInt(isDrmProtected? 1 : 0);
+ dest.writeString(soundPackName);
+ dest.writeString(themeStyleName);
+ dest.writeInt(previewResourceId);
+ }
+
+ /** @hide */
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public BaseThemeInfo createFromParcel(Parcel source) {
+ return new BaseThemeInfo(source);
+ }
+
+ public BaseThemeInfo[] newArray(int size) {
+ return new BaseThemeInfo[size];
+ }
+ };
+
+ /** @hide */
+ public final String getResolvedString(Resources res, AttributeSet attrs, int index) {
+ int resId = attrs.getAttributeResourceValue(index, 0);
+ if (resId !=0 ) {
+ return res.getString(resId);
+ }
+ return attrs.getAttributeValue(index);
+ }
+
+ protected BaseThemeInfo() {
+ }
+
+ protected BaseThemeInfo(Parcel source) {
+ wallpaperResourceId = source.readInt();
+ thumbnailResourceId = source.readInt();
+ themeId = source.readString();
+ styleResourceId = source.readInt();
+ name = source.readString();
+ ringtoneFileName = source.readString();
+ notificationRingtoneFileName = source.readString();
+ ringtoneName = source.readString();
+ notificationRingtoneName = source.readString();
+ author = source.readString();
+ copyright = source.readString();
+ isDrmProtected = (source.readInt() != 0);
+ soundPackName = source.readString();
+ themeStyleName = source.readString();
+ previewResourceId = source.readInt();
+ }
+
+ protected void changeDrmFlagIfNeeded(String resourcePath) {
+ if (resourcePath != null && resourcePath.contains(LOCKED_NAME)) {
+ isDrmProtected = true;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 90b42472481..aae2d6ecf31 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -40,6 +40,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.ThemeInfo;
import android.net.Uri;
import android.content.IntentSender;
@@ -126,6 +127,8 @@ interface IPackageManager {
*/
ParceledListSlice getInstalledPackages(int flags, in String lastRead);
+ List getInstalledThemePackages();
+
/**
* This implements getInstalledApplications via a "last returned row"
* mechanism that is not exposed in the API. This is to get around the IPC
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 85f7aa5113a..79cc52878a6 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -218,9 +219,69 @@ public class PackageInfo implements Parcelable {
*/
public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
+ // Is Theme Apk
+ /**
+ * {@hide}
+ */
+ public boolean isThemeApk = false;
+
+ // ThemeInfo
+ /**
+ * {@hide}
+ */
+ public ThemeInfo [] themeInfos;
+
public PackageInfo() {
}
+ /*
+ * Is Theme Apk is DRM protected (contains DRM-protected resources)
+ *
+ */
+ private boolean drmProtectedThemeApk = false;
+
+ /**
+ * @hide
+ *
+ * @return Is Theme Apk is DRM protected (contains DRM-protected resources)
+ */
+ public boolean isDrmProtectedThemeApk() {
+ return drmProtectedThemeApk;
+ }
+
+ /**
+ * @hide
+ *
+ * @param value if Theme Apk is DRM protected (contains DRM-protected resources)
+ */
+ public void setDrmProtectedThemeApk(boolean value) {
+ drmProtectedThemeApk = value;
+ }
+
+ /*
+ * If isThemeApk and isDrmProtectedThemeApk are true - path to hidden locked zip file
+ *
+ */
+ private String lockedZipFilePath;
+
+ /**
+ * @hide
+ *
+ * @return path for hidden locked zip file
+ */
+ public String getLockedZipFilePath() {
+ return lockedZipFilePath;
+ }
+
+ /**
+ * @hide
+ *
+ * @param value path for hidden locked zip file
+ */
+ public void setLockedZipFilePath(String value) {
+ lockedZipFilePath = value;
+ }
+
public String toString() {
return "PackageInfo{"
+ Integer.toHexString(System.identityHashCode(this))
@@ -258,6 +319,12 @@ public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
dest.writeInt(installLocation);
+
+ /* Theme-specific. */
+ dest.writeInt((isThemeApk)? 1 : 0);
+ dest.writeInt((drmProtectedThemeApk)? 1 : 0);
+ dest.writeTypedArray(themeInfos, parcelableFlags);
+ dest.writeString(lockedZipFilePath);
}
public static final Parcelable.Creator CREATOR
@@ -296,5 +363,11 @@ private PackageInfo(Parcel source) {
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
installLocation = source.readInt();
+
+ /* Theme-specific. */
+ isThemeApk = (source.readInt() != 0);
+ drmProtectedThemeApk = (source.readInt() != 0);
+ themeInfos = source.createTypedArray(ThemeInfo.CREATOR);
+ lockedZipFilePath = source.readString();
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6de69b034ac..235f060b36b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1418,6 +1419,17 @@ public abstract ProviderInfo getProviderInfo(ComponentName component,
*/
public abstract List getInstalledPackages(int flags);
+ /**
+ * Return a List of all theme packages that are installed
+ * on the device.
+ *
+ * @return A List of PackageInfo objects, one for each theme package
+ * that is installed on the device.
+ *
+ * @hide
+ */
+ public abstract List getInstalledThemePackages();
+
/**
* Check whether a particular package has been granted a particular
* permission.
@@ -1610,7 +1622,7 @@ public abstract int getUidForSharedUser(String sharedUserName)
*
* @param flags Additional option flags. Use any combination of
* {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
- * {link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
+ * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
*
* @return A List of ApplicationInfo objects, one for each application that
* is installed on the device. In the unlikely case of there being
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f8898c14add..f0f0de10eff 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -252,6 +253,17 @@ public static PackageInfo generatePackageInfo(PackageParser.Package p,
UserId.getCallingUserId());
}
+ public static String getLockedZipFilePath(String path) {
+ if (path == null) {
+ return null;
+ }
+ if (isPackageFilename(path)) {
+ return path.substring(0, path.length() - 4) + ".locked.zip";
+ } else {
+ return path + ".locked.zip";
+ }
+ }
+
/**
* Generate and return the {@link PackageInfo} for a parsed package.
*
@@ -276,6 +288,21 @@ public static PackageInfo generatePackageInfo(PackageParser.Package p,
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
+ pi.isThemeApk = p.mIsThemeApk;
+ pi.setDrmProtectedThemeApk(false);
+ if (pi.isThemeApk) {
+ int N = p.mThemeInfos.size();
+ if (N > 0) {
+ pi.themeInfos = new ThemeInfo[N];
+ for (int i = 0; i < N; i++) {
+ pi.themeInfos[i] = p.mThemeInfos.get(i);
+ pi.setDrmProtectedThemeApk(pi.isDrmProtectedThemeApk() || pi.themeInfos[i].isDrmProtected);
+ }
+ if (pi.isDrmProtectedThemeApk()) {
+ pi.setLockedZipFilePath(PackageParser.getLockedZipFilePath(p.mPath));
+ }
+ }
+ }
pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
@@ -1268,7 +1295,10 @@ private Package parsePackage(
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
-
+ } else if (tagName.equals("theme")) {
+ // this is a theme apk.
+ pkg.mIsThemeApk = true;
+ pkg.mThemeInfos.add(new ThemeInfo(parser, res, attrs));
} else if (RIGID_PARSER) {
outError[0] = "Bad element under : "
+ parser.getName();
@@ -1359,6 +1389,9 @@ private Package parsePackage(
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
+ if (pkg.mIsThemeApk) {
+ pkg.applicationInfo.isThemeable = false;
+ }
return pkg;
}
@@ -1660,12 +1693,43 @@ private Instrumentation parseInstrumentation(Package owner, Resources res,
return a;
}
+ private void parseApplicationThemeAttributes(XmlPullParser parser, AttributeSet attrs,
+ ApplicationInfo appInfo) {
+ for (int i = 0; i < attrs.getAttributeCount(); i++) {
+ if (!ApplicationInfo.isPlutoNamespace(parser.getAttributeNamespace(i))) {
+ continue;
+ }
+ String attrName = attrs.getAttributeName(i);
+ if (attrName.equalsIgnoreCase(ApplicationInfo.PLUTO_ISTHEMEABLE_ATTRIBUTE_NAME)) {
+ appInfo.isThemeable = attrs.getAttributeBooleanValue(i, false);
+ return;
+ }
+ }
+ }
+
+ private void parseActivityThemeAttributes(XmlPullParser parser, AttributeSet attrs,
+ ActivityInfo ai) {
+ for (int i = 0; i < attrs.getAttributeCount(); i++) {
+ if (!ApplicationInfo.isPlutoNamespace(parser.getAttributeNamespace(i))) {
+ continue;
+ }
+ String attrName = attrs.getAttributeName(i);
+ if (attrName.equalsIgnoreCase(ApplicationInfo.PLUTO_HANDLE_THEME_CONFIG_CHANGES_ATTRIBUTE_NAME)) {
+ ai.configChanges |= ActivityInfo.CONFIG_THEME_RESOURCE;
+ }
+ }
+ }
+
private boolean parseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
+ // assume that this package is themeable unless explicitly set to false.
+ ai.isThemeable = true;
+ parseApplicationThemeAttributes(parser, attrs, ai);
+
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestApplication);
@@ -2180,6 +2244,8 @@ private Activity parseActivity(Package owner, Resources res,
return null;
}
+ parseActivityThemeAttributes(parser, attrs, a.info);
+
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3137,6 +3203,12 @@ public final static class Package {
// For use by package manager to keep track of where it has done dexopt.
public boolean mDidDexOpt;
+
+ // Is Theme Apk
+ public boolean mIsThemeApk = false;
+
+ // Theme info
+ public final ArrayList mThemeInfos = new ArrayList(0);
// // User set enabled state.
// public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index b1fc788c16a..d8f920478aa 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -251,7 +251,7 @@ private boolean inSystemImage(int callerUid) {
return false;
}
- void generateServicesMap() {
+ public void generateServicesMap() {
PackageManager pm = mContext.getPackageManager();
ArrayList> serviceInfos = new ArrayList>();
List resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName),
diff --git a/core/java/android/content/pm/ThemeInfo.aidl b/core/java/android/content/pm/ThemeInfo.aidl
new file mode 100755
index 00000000000..acbc85e9c8b
--- /dev/null
+++ b/core/java/android/content/pm/ThemeInfo.aidl
@@ -0,0 +1,3 @@
+package android.content.pm;
+
+parcelable ThemeInfo;
diff --git a/core/java/android/content/pm/ThemeInfo.java b/core/java/android/content/pm/ThemeInfo.java
new file mode 100644
index 00000000000..e51dbb6ae8c
--- /dev/null
+++ b/core/java/android/content/pm/ThemeInfo.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010, T-Mobile USA, Inc.
+ *
+ * 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.content.pm;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParser;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.content.res.Resources;
+
+/**
+ * Overall information about "theme" package. This corresponds
+ * to the information collected from AndroidManifest.xml (theme tag).
+ *
+ * Below is an example of theme tag
+ *
+ *
+ * @hide
+ */
+public final class ThemeInfo extends BaseThemeInfo {
+ private enum AttributeIndex {
+ THEME_PACKAGE_INDEX,
+ PREVIEW_INDEX,
+ AUTHOR_INDEX,
+ THEME_INDEX,
+ THEME_STYLE_NAME_INDEX,
+ THUMBNAIL_INDEX,
+ RINGTONE_FILE_NAME_INDEX,
+ NOTIFICATION_RINGTONE_FILE_NAME_INDEX,
+ WALLPAPER_IMAGE_INDEX,
+ COPYRIGHT_INDEX,
+ RINGTONE_NAME_INDEX,
+ NOTIFICATION_RINGTONE_NAME_INDEX,
+ STYLE_INDEX;
+
+ public static AttributeIndex get(int ordinal) {
+ return values()[ordinal];
+ }
+ };
+
+ private static final String [] compulsoryAttributes = new String [] {
+ "name",
+ "preview",
+ "author",
+ "themeId",
+ "styleName",
+ };
+
+ private static final String [] optionalAttributes = new String [] {
+ "thumbnail",
+ "ringtoneFileName",
+ "notificationRingtoneFileName",
+ "wallpaperImage",
+ "copyright",
+ "ringtoneName",
+ "notificationRingtoneName",
+ "styleId",
+ };
+
+ private static final Map sAttributesLookupTable;
+
+ static {
+ sAttributesLookupTable = new HashMap();
+ for (int i = 0; i < compulsoryAttributes.length; i++) {
+ sAttributesLookupTable.put(compulsoryAttributes[i], AttributeIndex.get(i));
+ }
+
+ for (int i = 0; i < optionalAttributes.length; i++) {
+ sAttributesLookupTable.put(optionalAttributes[i],
+ AttributeIndex.get(compulsoryAttributes.length + i));
+ }
+ }
+
+ public ThemeInfo(XmlPullParser parser, Resources res, AttributeSet attrs) throws XmlPullParserException {
+ super();
+
+ Map tempMap =
+ new HashMap(sAttributesLookupTable);
+ int numberOfCompulsoryAttributes = 0;
+ for (int i = 0; i < attrs.getAttributeCount(); i++) {
+ if (!ApplicationInfo.isPlutoNamespace(parser.getAttributeNamespace(i))) {
+ continue;
+ }
+ String key = attrs.getAttributeName(i);
+ if (tempMap.containsKey(key)) {
+ AttributeIndex index = tempMap.get(key);
+ tempMap.remove(key);
+
+ if (index.ordinal() < compulsoryAttributes.length) {
+ numberOfCompulsoryAttributes++;
+ }
+ switch (index) {
+ case THEME_PACKAGE_INDEX:
+ // theme name
+ name = getResolvedString(res, attrs, i);
+ break;
+
+ case THUMBNAIL_INDEX:
+ // theme thumbprint
+ thumbnailResourceId = attrs.getAttributeResourceValue(i, 0);
+ break;
+
+ case AUTHOR_INDEX:
+ // theme author
+ author = getResolvedString(res, attrs, i);
+ break;
+
+ case THEME_INDEX:
+ // androidUiStyle attribute
+ themeId = attrs.getAttributeValue(i);
+ break;
+
+ case THEME_STYLE_NAME_INDEX:
+ themeStyleName = getResolvedString(res, attrs, i);
+ break;
+
+ case RINGTONE_FILE_NAME_INDEX:
+ // ringtone
+ ringtoneFileName = attrs.getAttributeValue(i);
+ changeDrmFlagIfNeeded(ringtoneFileName);
+ break;
+
+ case NOTIFICATION_RINGTONE_FILE_NAME_INDEX:
+ // notification ringtone
+ notificationRingtoneFileName = attrs.getAttributeValue(i);
+ changeDrmFlagIfNeeded(notificationRingtoneFileName);
+ break;
+
+ case WALLPAPER_IMAGE_INDEX:
+ // wallpaperImage attribute
+ wallpaperResourceId = attrs.getAttributeResourceValue(i, 0);
+ break;
+
+ case COPYRIGHT_INDEX:
+ // themeCopyright attribute
+ copyright = getResolvedString(res, attrs, i);
+ break;
+
+ case RINGTONE_NAME_INDEX:
+ // ringtone UI name
+ ringtoneName = attrs.getAttributeValue(i);
+ break;
+
+ case NOTIFICATION_RINGTONE_NAME_INDEX:
+ // notification ringtone UI name
+ notificationRingtoneName = attrs.getAttributeValue(i);
+ break;
+
+ case STYLE_INDEX:
+ styleResourceId = attrs.getAttributeResourceValue(i, 0);
+ break;
+
+ case PREVIEW_INDEX:
+ // theme thumbprint
+ previewResourceId = attrs.getAttributeResourceValue(i, 0);
+ break;
+ }
+ }
+ }
+ if (numberOfCompulsoryAttributes < compulsoryAttributes.length) {
+ throw new XmlPullParserException("Not all compulsory attributes are specified in ");
+ }
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public ThemeInfo createFromParcel(Parcel source) {
+ return new ThemeInfo(source);
+ }
+
+ public ThemeInfo[] newArray(int size) {
+ return new ThemeInfo[size];
+ }
+ };
+
+ private ThemeInfo(Parcel source) {
+ super(source);
+ }
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index ffefaa27a85..80d09466af1 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +19,7 @@
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import android.util.SparseArray;
import android.util.TypedValue;
import java.io.FileNotFoundException;
@@ -77,6 +79,20 @@ public final class AssetManager {
private boolean mOpen = true;
private HashMap mRefStacks;
+ private String mAssetDir;
+ private String mAppName;
+
+ private boolean mThemeSupport;
+ private String mThemePackageName;
+ private int mThemeCookie;
+
+ /**
+ * Organize all added redirection maps using Java strong references to keep
+ * the native layer cleanup simple (that is, finalize() in Java will be
+ * responsible for delete in C++).
+ */
+ private SparseArray mRedirections;
+
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
@@ -252,6 +268,12 @@ public void close() {
}
}
+ /*package*/ final void recreateStringBlocks() {
+ synchronized (this) {
+ makeStringBlocks(true);
+ }
+ }
+
/*package*/ final void makeStringBlocks(boolean copyFromSystem) {
final int sysNum = copyFromSystem ? sSystem.mStringBlocks.length : 0;
final int num = getStringBlockCount();
@@ -458,6 +480,18 @@ public final XmlResourceParser openXmlResourceParser(int cookie,
return rp;
}
+ /**
+ * {@hide}
+ * Split a theme package with DRM-protected resources into two files.
+ *
+ * @param packageFileName Original theme package file name.
+ * @param lockedFileName Name of the new "locked" file with DRM resources.
+ * @param drmProtectedresources Array of names of DRM-protected assets.
+ */
+ public final int splitDrmProtectedThemePackage(String packageFileName, String lockedFileName, String [] drmProtectedresources) {
+ return splitThemePackage(packageFileName, lockedFileName, drmProtectedresources);
+ }
+
/**
* {@hide}
* Retrieve a non-asset as a compiled XML file. Not for use by
@@ -624,6 +658,110 @@ public final int[] addAssetPaths(String[] paths) {
return cookies;
}
+ /**
+ * Delete a set of theme assets from the asset manager. Not for use by
+ * applications. Returns true if succeeded or false on failure.
+ *
+ * @hide
+ */
+ public native final boolean detachThemePath(String packageName, int cookie);
+
+ /**
+ * Attach a set of theme assets to the asset manager. If necessary, this
+ * method will forcefully update the internal ResTable data structure.
+ *
+ * @return Cookie of the added asset or 0 on failure.
+ * @hide
+ */
+ public native final int attachThemePath(String path);
+
+ /**
+ * Sets a flag indicating that this AssetManager should have themes
+ * attached, according to the initial request to create it by the
+ * ApplicationContext.
+ *
+ * {@hide}
+ */
+ public final void setThemeSupport(boolean themeSupport) {
+ mThemeSupport = themeSupport;
+ }
+
+ /**
+ * Should this AssetManager have themes attached, according to the initial
+ * request to create it by the ApplicationContext?
+ *
+ * {@hide}
+ */
+ public final boolean hasThemeSupport() {
+ return mThemeSupport;
+ }
+
+ /**
+ * Apply a heuristic to match-up all attributes from the source style with
+ * attributes in the destination style. For each match, an entry in the
+ * package redirection map will be inserted.
+ *
+ * {@hide}
+ */
+ public native final boolean generateStyleRedirections(int resMapNative, int sourceStyle,
+ int destStyle);
+
+ /**
+ * Get package name of current theme (may return null).
+ * {@hide}
+ */
+ public String getThemePackageName() {
+ return mThemePackageName;
+ }
+
+ /**
+ * Sets package name and highest level style id for current theme (null, 0 is allowed).
+ * {@hide}
+ */
+ public void setThemePackageName(String packageName) {
+ mThemePackageName = packageName;
+ }
+
+ /**
+ * Get asset cookie for current theme (may return 0).
+ * {@hide}
+ */
+ public int getThemeCookie() {
+ return mThemeCookie;
+ }
+
+ /**
+ * Sets asset cookie for current theme (0 if not a themed asset manager).
+ * {@hide}
+ */
+ public void setThemeCookie(int cookie) {
+ mThemeCookie = cookie;
+ }
+
+ /**
+ * Add a redirection map to the asset manager. All future resource lookups
+ * will consult this map.
+ * {@hide}
+ */
+ public void addRedirections(PackageRedirectionMap map) {
+ if (mRedirections == null) {
+ mRedirections = new SparseArray(2);
+ }
+ mRedirections.append(map.getPackageId(), map);
+ addRedirectionsNative(map.getNativePointer());
+ }
+
+ /**
+ * Clear redirection map for the asset manager.
+ * {@hide}
+ */
+ public void clearRedirections() {
+ if (mRedirections != null) {
+ mRedirections.clear();
+ }
+ clearRedirectionsNative();
+ }
+
/**
* Determine whether the state in this asset manager is up-to-date with
* the files on the filesystem. If false is returned, you need to
@@ -741,6 +879,26 @@ private native final int loadResourceBagValue(int ident, int bagEntryId, TypedVa
private native final int[] getArrayStringInfo(int arrayRes);
/*package*/ native final int[] getArrayIntResource(int arrayRes);
+ private native final int splitThemePackage(String srcFileName, String dstFileName, String [] drmProtectedAssetNames);
+
+ /**
+ * {@hide}
+ */
+ public native final int getBasePackageCount();
+
+ /**
+ * {@hide}
+ */
+ public native final String getBasePackageName(int index);
+
+ /**
+ * {@hide}
+ */
+ public native final int getBasePackageId(int index);
+
+ private native final void addRedirectionsNative(int redirectionMapNativePointer);
+ private native final void clearRedirectionsNative();
+
private native final void init();
private native final void destroy();
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 1c9285e7a9a..d6856c98b96 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -92,9 +92,15 @@ public class CompatibilityInfo implements Parcelable {
*/
public final float applicationInvertedScale;
+ /**
+ * Whether the application supports third-party theming.
+ */
+ public final boolean isThemeable;
+
public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
boolean forceCompat) {
int compatFlags = 0;
+ isThemeable = appInfo.isThemeable;
if (appInfo.requiresSmallestWidthDp != 0 || appInfo.compatibleWidthLimitDp != 0
|| appInfo.largestWidthLimitDp != 0) {
@@ -242,17 +248,19 @@ public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
}
private CompatibilityInfo(int compFlags,
- int dens, float scale, float invertedScale) {
+ int dens, float scale, float invertedScale, boolean isThemeable) {
mCompatibilityFlags = compFlags;
applicationDensity = dens;
applicationScale = scale;
applicationInvertedScale = invertedScale;
+ this.isThemeable = isThemeable;
}
private CompatibilityInfo() {
this(NEVER_NEEDS_COMPAT, DisplayMetrics.DENSITY_DEVICE,
1.0f,
- 1.0f);
+ 1.0f,
+ true);
}
/**
@@ -519,6 +527,7 @@ public boolean equals(Object o) {
if (applicationDensity != oc.applicationDensity) return false;
if (applicationScale != oc.applicationScale) return false;
if (applicationInvertedScale != oc.applicationInvertedScale) return false;
+ if (isThemeable != oc.isThemeable) return false;
return true;
} catch (ClassCastException e) {
return false;
@@ -556,6 +565,7 @@ public int hashCode() {
result = 31 * result + applicationDensity;
result = 31 * result + Float.floatToIntBits(applicationScale);
result = 31 * result + Float.floatToIntBits(applicationInvertedScale);
+ result = 31 * result + (isThemeable ? 1 : 0);
return result;
}
@@ -570,6 +580,7 @@ public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(applicationDensity);
dest.writeFloat(applicationScale);
dest.writeFloat(applicationInvertedScale);
+ dest.writeInt(isThemeable ? 1 : 0);
}
public static final Parcelable.Creator CREATOR
@@ -588,5 +599,6 @@ private CompatibilityInfo(Parcel source) {
applicationDensity = source.readInt();
applicationScale = source.readFloat();
applicationInvertedScale = source.readFloat();
+ isThemeable = source.readInt() == 1 ? true : false;
}
}
diff --git a/core/java/android/content/res/Configuration.aidl b/core/java/android/content/res/Configuration.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 423b9afa45a..830ebe3e633 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +22,9 @@
import android.os.Parcelable;
import android.util.LocaleUtil;
import android.view.View;
+import android.util.Log;
+import android.os.SystemProperties;
+import android.text.TextUtils;
import java.util.Locale;
@@ -62,6 +66,11 @@ public final class Configuration implements Parcelable, Comparable CREATOR
+ = new Parcelable.Creator() {
+ public PackageRedirectionMap createFromParcel(Parcel in) {
+ return new PackageRedirectionMap(in);
+ }
+
+ public PackageRedirectionMap[] newArray(int size) {
+ return new PackageRedirectionMap[size];
+ }
+ };
+
+ public PackageRedirectionMap() {
+ this(nativeConstructor());
+ }
+
+ private PackageRedirectionMap(Parcel in) {
+ this(nativeCreateFromParcel(in));
+ }
+
+ private PackageRedirectionMap(int nativePointer) {
+ if (nativePointer == 0) {
+ throw new RuntimeException();
+ }
+ mNativePointer = nativePointer;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDestructor(mNativePointer);
+ }
+
+ public int getNativePointer() {
+ return mNativePointer;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (!nativeWriteToParcel(mNativePointer, dest)) {
+ throw new RuntimeException();
+ }
+ }
+
+ public int getPackageId() {
+ return nativeGetPackageId(mNativePointer);
+ }
+
+ public void addRedirection(int fromIdent, int toIdent) {
+ nativeAddRedirection(mNativePointer, fromIdent, toIdent);
+ }
+
+ // Used for debugging purposes only.
+ public int[] getRedirectionKeys() {
+ return nativeGetRedirectionKeys(mNativePointer);
+ }
+
+ // Used for debugging purposes only.
+ public int lookupRedirection(int fromIdent) {
+ return nativeLookupRedirection(mNativePointer, fromIdent);
+ }
+
+ private static native int nativeConstructor();
+ private static native void nativeDestructor(int nativePointer);
+
+ private static native int nativeCreateFromParcel(Parcel p);
+ private static native boolean nativeWriteToParcel(int nativePointer, Parcel p);
+
+ private native void nativeAddRedirection(int nativePointer, int fromIdent, int toIdent);
+ private native int nativeGetPackageId(int nativePointer);
+ private native int[] nativeGetRedirectionKeys(int nativePointer);
+ private native int nativeLookupRedirection(int nativePointer, int fromIdent);
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
old mode 100755
new mode 100644
index c630bb51c96..0f73b44599d
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1120,8 +1120,8 @@ public void setTo(Theme other) {
* Return a StyledAttributes holding the values defined by
* Theme which are listed in attrs.
*
- * Be sure to call StyledAttributes.recycle() when you are done with
- * the array.
+ *
Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+ * with the array.
*
* @param attrs The desired attributes.
*
@@ -1148,8 +1148,8 @@ public TypedArray obtainStyledAttributes(int[] attrs) {
* Return a StyledAttributes holding the values defined by the style
* resource resid which are listed in attrs.
*
- *
Be sure to call StyledAttributes.recycle() when you are done with
- * the array.
+ *
Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+ * with the array.
*
* @param resid The desired style resource.
* @param attrs The desired attributes in the style.
@@ -1208,8 +1208,8 @@ public TypedArray obtainStyledAttributes(int resid, int[] attrs)
* AttributeSet specifies a style class (through the "style" attribute),
* that style will be applied on top of the base attributes it defines.
*
- *
Be sure to call StyledAttributes.recycle() when you are done with
- * the array.
+ *
Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+ * with the array.
*
*
When determining the final value of a particular attribute, there
* are four inputs that come into play:
@@ -1441,7 +1441,15 @@ public void updateConfiguration(Configuration config,
mTmpConfig.locale = Locale.getDefault();
}
configChanges = mConfiguration.updateFrom(mTmpConfig);
- configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
+
+ /* This is ugly, but modifying the activityInfoConfigToNative
+ * adapter would be messier */
+ if ((configChanges & ActivityInfo.CONFIG_THEME_RESOURCE) != 0) {
+ configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
+ configChanges |= ActivityInfo.CONFIG_THEME_RESOURCE;
+ } else {
+ configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
+ }
}
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
@@ -1503,6 +1511,18 @@ public void updateConfiguration(Configuration config,
private void clearDrawableCache(
LongSparseArray> cache,
int configChanges) {
+ /*
+ * Quick test to find out if the config change that occurred should
+ * trigger a full cache wipe.
+ */
+ if (Configuration.needNewResources(configChanges, 0)) {
+ if (DEBUG_CONFIG) {
+ Log.d(TAG, "Clear drawable cache from config changes: 0x"
+ + Integer.toHexString(configChanges));
+ }
+ cache.clear();
+ return;
+ }
int N = cache.size();
if (DEBUG_CONFIG) {
Log.d(TAG, "Cleaning up drawables config changes: 0x"
@@ -1854,7 +1874,16 @@ public final void finishPreloading() {
flushLayoutCache();
}
}
-
+
+ /**
+ * @hide
+ */
+ public final void updateStringCache() {
+ synchronized (mTmpValue) {
+ mAssets.recreateStringBlocks();
+ }
+ }
+
/*package*/ Drawable loadDrawable(TypedValue value, int id)
throws NotFoundException {
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 2df492e7461..2968fbb5090 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -684,7 +684,7 @@ public String getPositionDescription() {
}
/**
- * Give back a previously retrieved StyledAttributes, for later re-use.
+ * Give back a previously retrieved array, for later re-use.
*/
public void recycle() {
synchronized (mResources.mTmpValue) {
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index fb04817f86e..e7ff92d0b3e 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -424,6 +424,9 @@ protected void finalize() {
if (mSelfObserver != null && mSelfObserverRegistered == true) {
mContentResolver.unregisterContentObserver(mSelfObserver);
}
+ try {
+ if (!mClosed) close();
+ } catch(Exception e) { }
}
/**
diff --git a/core/java/android/database/IContentObserver.aidl b/core/java/android/database/IContentObserver.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 91884abaf99..e4dd2a9a3b5 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -372,7 +372,7 @@ public Cursor query(SQLiteDatabase db, String[] projectionIn,
public Cursor query(SQLiteDatabase db, String[] projectionIn,
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
- if (mTables == null) {
+ if (db == null || mTables == null) {
return null;
}
diff --git a/core/java/android/ddm/package.html b/core/java/android/ddm/package.html
old mode 100755
new mode 100644
diff --git a/core/java/android/debug/package.html b/core/java/android/debug/package.html
old mode 100755
new mode 100644
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
old mode 100755
new mode 100644
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
old mode 100755
new mode 100644
diff --git a/core/java/android/gesture/GestureUtils.java b/core/java/android/gesture/GestureUtils.java
old mode 100755
new mode 100644
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
old mode 100755
new mode 100644
diff --git a/core/java/android/gesture/Learner.java b/core/java/android/gesture/Learner.java
old mode 100755
new mode 100644
diff --git a/core/java/android/gesture/Prediction.java b/core/java/android/gesture/Prediction.java
old mode 100755
new mode 100644
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4efde56f5c7..37d15c820f8 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1806,6 +1806,7 @@ public class Parameters {
private static final String KEY_RECORDING_HINT = "recording-hint";
private static final String KEY_VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported";
private static final String KEY_FULL_VIDEO_SNAP_SUPPORTED = "full-video-snap-supported";
+ private static final String KEY_POWER_MODE_SUPPORTED = "power-mode-supported";
private static final String KEY_VIDEO_STABILIZATION = "video-stabilization";
private static final String KEY_VIDEO_STABILIZATION_SUPPORTED = "video-stabilization-supported";
private static final String KEY_SHARPNESS = "sharpness";
@@ -1819,11 +1820,13 @@ public class Parameters {
private static final String KEY_SELECTABLE_ZONE_AF = "selectable-zone-af";
private static final String KEY_FACE_DETECTION = "face-detection";
private static final String KEY_MEMORY_COLOR_ENHANCEMENT = "mce";
- private static final String KEY_REDEYE_REDUCTION = "redeye-reduction";
+ private static final String KEY_REDEYE_REDUCTION = "redeye-reduction";
private static final String KEY_ZSL = "zsl";
private static final String KEY_CAMERA_MODE = "camera-mode";
private static final String KEY_VIDEO_HIGH_FRAME_RATE = "video-hfr";
+ private static final String KEY_POWER_MODE = "power-mode";
+
// Parameter key suffix for supported values.
private static final String SUPPORTED_VALUES_SUFFIX = "-values";
@@ -1923,6 +1926,10 @@ public class Parameters {
/** @hide */
public static final String AE_BRACKET = "AE-Bracket";
+ // Values for POWER MODE
+ public static final String LOW_POWER = "Low_Power";
+ public static final String NORMAL_POWER = "Normal_Power";
+
// Values for HFR settings.
/** @hide */
public static final String VIDEO_HFR_OFF = "off";
@@ -3457,6 +3464,28 @@ public void setSceneMode(String value) {
set(KEY_SCENE_MODE, value);
}
+ /**
+ * Sets the Power mode.
+ *
+ * @param value Power mode.
+ * @see #getPowerMode()
+ */
+ public void setPowerMode(String value) {
+ set(KEY_POWER_MODE, value);
+ }
+
+ /**
+ * Gets the current power mode setting.
+ *
+ * @return current power mode. null if power mode setting is not
+ * supported.
+ * @see #POWER_MODE_LOW
+ * @see #POWER_MODE_NORMAL
+ */
+ public String getPowerMode() {
+ return get(KEY_POWER_MODE);
+ }
+
/**
* Gets the supported scene modes.
*
@@ -4493,6 +4522,14 @@ public boolean isFullsizeVideoSnapSupported() {
return TRUE.equals(str);
}
+ /**
+ * @return true if full size video snapshot is supported.
+ */
+ public boolean isPowerModeSupported() {
+ String str = get(KEY_POWER_MODE_SUPPORTED);
+ return TRUE.equals(str);
+ }
+
/**
* @hide
* Gets the current face detection setting.
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 0204e94df6b..7375e7d743d 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -373,7 +373,8 @@ protected void unregisterListenerImpl(SensorEventListener listener, Sensor senso
for (Sensor s : l.getSensors()) {
disableSensorLocked(s);
}
- } else if (l.removeSensor(sensor) == 0) {
+ // Check if the ListenerDelegate has the sensor it is trying to unregister.
+ } else if (l.hasSensor(sensor) && l.removeSensor(sensor) == 0) {
// if we have no more sensors enabled on this listener,
// take it off the list.
sListeners.remove(i);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
old mode 100755
new mode 100644
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index c40504a7039..f64ef87b3b4 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -257,7 +257,7 @@ public HashMap getDeviceList() {
* data using {@link android.hardware.usb.UsbRequest}.
*
* @param device the device to open
- * @return true if we successfully opened the device
+ * @return a {@link UsbDeviceConnection}, or {@code null} if open failed
*/
public UsbDeviceConnection openDevice(UsbDevice device) {
try {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 46153e7634b..38f550964d4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -247,6 +247,20 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public static final int IME_VISIBLE = 0x2;
+ int mVolumeKeyCursorControl = 0;
+ /**
+ * @hide
+ */
+ public static final int VOLUME_CURSOR_OFF = 0;
+ /**
+ * @hide
+ */
+ public static final int VOLUME_CURSOR_ON = 1;
+ /**
+ * @hide
+ */
+ public static final int VOLUME_CURSOR_ON_REVERSE = 2;
+
InputMethodManager mImm;
int mTheme = 0;
@@ -1735,6 +1749,26 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
}
return false;
}
+ if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) {
+ mVolumeKeyCursorControl = Settings.System.getInt(getContentResolver(),
+ Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0);
+ if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) {
+ sendDownUpKeyEvents((mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE)
+ ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT);
+ return true;
+ }
+ return false;
+ }
+ if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ mVolumeKeyCursorControl = Settings.System.getInt(getContentResolver(),
+ Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0);
+ if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) {
+ sendDownUpKeyEvents((mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE)
+ ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT);
+ return true;
+ }
+ return false;
+ }
return doMovementKey(keyCode, event, MOVEMENT_DOWN);
}
@@ -1780,7 +1814,15 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
&& !event.isCanceled()) {
return handleBack(true);
}
-
+ if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ mVolumeKeyCursorControl = Settings.System.getInt(getContentResolver(),
+ Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0);
+ if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) {
+ return true;
+ }
+ return false;
+ }
return doMovementKey(keyCode, event, MOVEMENT_UP);
}
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index 9f0f9cd3f52..ccd96ff3c0c 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -123,7 +123,7 @@ public boolean teardown() {
* Record the detailed state of a network, and if it is a
* change from the previous state, send a notification to
* any listeners.
- * @param state the new @{code DetailedState}
+ * @param state the new {@code DetailedState}
* @param reason a {@code String} indicating a reason for the state change,
* if one was supplied. May be {@code null}.
* @param extraInfo optional {@code String} providing extra information about the state change
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 54a89ad61dc..d5a49dc002b 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -189,14 +189,24 @@ public void onReceive(Context context, Intent intent) {
if (!TextUtils.equals(apnType, mApnType)) {
return;
}
- mNetworkInfo.setSubtype(TelephonyManager.getDefault().getNetworkType(),
- TelephonyManager.getDefault().getNetworkTypeName());
+
+
+ int oldSubtype = mNetworkInfo.getSubtype();
+ int newSubType = TelephonyManager.getDefault().getNetworkType();
+ String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
+ mNetworkInfo.setSubtype(newSubType, subTypeName);
+ if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
+ Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
+ oldSubtype, 0, mNetworkInfo);
+ msg.sendToTarget();
+ }
+
Phone.DataState state = Enum.valueOf(Phone.DataState.class,
intent.getStringExtra(Phone.STATE_KEY));
String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
- mNetworkInfo.setRoaming(intent.getBooleanExtra(Phone.DATA_NETWORK_ROAMING_KEY,
- false));
+ mNetworkInfo.setRoaming(intent.getBooleanExtra(
+ Phone.DATA_NETWORK_ROAMING_KEY, false));
if (VDBG) {
log(mApnType + " setting isAvailable to " +
intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,false));
@@ -332,6 +342,9 @@ public String getTcpBufferSizesPropName() {
case TelephonyManager.NETWORK_TYPE_HSPA:
networkTypeStr = "hspa";
break;
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ networkTypeStr = "hspap";
+ break;
case TelephonyManager.NETWORK_TYPE_CDMA:
networkTypeStr = "cdma";
break;
@@ -345,7 +358,7 @@ public String getTcpBufferSizesPropName() {
networkTypeStr = "evdo";
break;
case TelephonyManager.NETWORK_TYPE_EVDO_B:
- networkTypeStr = "evdo";
+ networkTypeStr = "evdo_b";
break;
case TelephonyManager.NETWORK_TYPE_IDEN:
networkTypeStr = "iden";
@@ -376,7 +389,7 @@ public boolean teardown() {
* Record the detailed state of a network, and if it is a
* change from the previous state, send a notification to
* any listeners.
- * @param state the new @{code DetailedState}
+ * @param state the new {@code DetailedState}
* @param reason a {@code String} indicating a reason for the state change,
* if one was supplied. May be {@code null}.
* @param extraInfo optional {@code String} providing extra information about the state change
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 7df0193c8b1..0d6dcd6f342 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -68,6 +68,12 @@ public interface NetworkStateTracker {
*/
public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
+ /**
+ * msg.what = EVENT_NETWORK_SUBTYPE_CHANGED
+ * msg.obj = NetworkInfo object
+ */
+ public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 7;
+
/**
* -------------------------------------------------------------
* Control Interface
diff --git a/core/java/android/net/Uri.aidl b/core/java/android/net/Uri.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index fb5263d9f13..65d3f2b2767 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -329,7 +329,7 @@ public Builder addAddress(InetAddress address, int prefixLength) {
throw new IllegalArgumentException("Bad address");
}
- mAddresses.append(String.format(" %s/%d", address.getHostAddress(), prefixLength));
+ mAddresses.append(' ' + address.getHostAddress() + '/' + prefixLength);
return this;
}
diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java
index 8e666bc275d..6ba1e7c5705 100644
--- a/core/java/android/net/arp/ArpPeer.java
+++ b/core/java/android/net/arp/ArpPeer.java
@@ -53,9 +53,11 @@ public ArpPeer(String interfaceName, InetAddress myAddr, String mac,
mInterfaceName = interfaceName;
mMyAddr = myAddr;
- for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
- mMyMac[i] = (byte) Integer.parseInt(mac.substring(
- i*3, (i*3) + 2), 16);
+ if (mac != null) {
+ for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
+ mMyMac[i] = (byte) Integer.parseInt(mac.substring(
+ i*3, (i*3) + 2), 16);
+ }
}
if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) {
diff --git a/core/java/android/net/http/package.html b/core/java/android/net/http/package.html
old mode 100755
new mode 100644
diff --git a/core/java/android/net/package.html b/core/java/android/net/package.html
old mode 100755
new mode 100644
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 887233523ca..ed1c5b3e0f6 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -70,7 +70,7 @@
* indicate location with an NDEF message, provide support for chunking of
* NDEF records, and to pack optional fields. This class does not expose
* those details. To write an NDEF Record as binary you must first put it
- * into an @{link NdefMessage}, then call {@link NdefMessage#toByteArray()}.
+ * into an {@link NdefMessage}, then call {@link NdefMessage#toByteArray()}.
*
* {@link NdefMessage} and {@link NdefRecord} implementations are
* always available, even on Android devices that do not have NFC hardware.
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index c62715b858a..c8af750d2de 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -26,6 +26,12 @@ public class BatteryManager {
* integer containing the current status constant.
*/
public static final String EXTRA_STATUS = "status";
+
+ /**
+ * Integer containing the current status constant for the dock battery.
+ * @hide
+ */
+ public static final String EXTRA_DOCK_STATUS = "dock_status";
/**
* Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
@@ -45,7 +51,19 @@ public class BatteryManager {
* {@link #EXTRA_SCALE}.
*/
public static final String EXTRA_LEVEL = "level";
-
+
+ /**
+ * Integer field containing the current dock battery level.
+ * @hide
+ */
+ public static final String EXTRA_DOCK_LEVEL = "dock_level";
+
+ /**
+ * Boolean field containing the current dock battery AC status.
+ * @hide
+ */
+ public static final String EXTRA_DOCK_AC_ONLINE = "dock_ac_online";
+
/**
* Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
* integer containing the maximum battery level.
@@ -109,6 +127,21 @@ public class BatteryManager {
public static final int BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6;
public static final int BATTERY_HEALTH_COLD = 7;
+ /** @hide */
+ public static final int DOCK_STATE_UNKNOWN = 0;
+
+ /** @hide */
+ public static final int DOCK_STATE_UNDOCKED = 1;
+
+ /** @hide */
+ public static final int DOCK_STATE_CHARGING = 2;
+
+ /** @hide */
+ public static final int DOCK_STATE_DOCKED = 3;
+
+ /** @hide */
+ public static final int DOCK_STATE_DISCHARGING = 4;
+
// values of the "plugged" field in the ACTION_BATTERY_CHANGED intent.
// These must be powers of 2.
/** Power source is an AC charger. */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 438c5366b2b..f666ca01aa9 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -822,12 +822,13 @@ public abstract long getPhoneSignalScanningTime(
public static final int DATA_CONNECTION_EVDO_B = 12;
public static final int DATA_CONNECTION_LTE = 13;
public static final int DATA_CONNECTION_EHRPD = 14;
- public static final int DATA_CONNECTION_OTHER = 15;
+ public static final int DATA_CONNECTION_HSPAP = 15;
+ public static final int DATA_CONNECTION_OTHER = 16;
static final String[] DATA_CONNECTION_NAMES = {
"none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
"1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
- "ehrpd", "other"
+ "ehrpd", "hspap", "other"
};
public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 577fc434836..7b51119c165 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -32,7 +32,7 @@
* the standard support creating a local implementation of such an object.
*
*
Most developers will not implement this class directly, instead using the
- * aidl tool to describe the desired
+ * aidl tool to describe the desired
* interface, having it generate the appropriate Binder subclass. You can,
* however, derive directly from Binder to implement your own custom RPC
* protocol or simply instantiate a raw Binder object directly to use as a
diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 270e9be1da6..e9a6068b428 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -46,4 +46,13 @@ interface IPowerManager
void setBacklightBrightness(int brightness);
void setAttentionLight(boolean on, int color);
void setAutoBrightnessAdjustment(float adj);
+
+ // custom backlight things
+ int getLightSensorValue();
+ int getRawLightSensorValue();
+ int getLightSensorScreenBrightness();
+ int getLightSensorButtonBrightness();
+ int getLightSensorKeyboardBrightness();
+
+ void cpuBoost(int duration);
}
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/os/LocalPowerManager.java b/core/java/android/os/LocalPowerManager.java
index 52df1f8f91d..8bbea61ab95 100644
--- a/core/java/android/os/LocalPowerManager.java
+++ b/core/java/android/os/LocalPowerManager.java
@@ -45,4 +45,6 @@ public interface LocalPowerManager {
void setScreenBrightnessOverride(int brightness);
void setButtonBrightnessOverride(int brightness);
+
+ void cpuBoost(int duration);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 903c8b3cde1..50ca0aad1b5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -559,6 +559,24 @@ public void reboot(String reason)
}
}
+ /**
+ * Boost the CPU. Boosts the cpu for the given duration in microseconds.
+ * Requires the {@link android.Manifest.permission#CPU_BOOST} permission.
+ *
+ * @param duration in microseconds to boost the CPU
+ *
+ * @hide
+ */
+ public void cpuBoost(int duration)
+ {
+ try {
+ if (mService != null) {
+ mService.cpuBoost(duration);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
private PowerManager()
{
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8eaeb1d5f9d..10a639b6811 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -962,4 +962,29 @@ public static final class ProcessStartResult {
*/
public boolean usingWrapper;
}
+
+ private static final int[] PROCESS_STATE_FORMAT = new int[] {
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_PARENS, // 1: name
+ PROC_SPACE_TERM|PROC_OUT_STRING, // 2: state
+ };
+
+ /**
+ * Returns true if the process can be found and is not a zombie
+ * @param pid the process id
+ * @hide
+ */
+ public static final boolean isAlive(int pid) {
+ boolean ret = false;
+ String[] processStateString = new String[1];
+ if (Process.readProcFile("/proc/" + pid + "/stat",
+ PROCESS_STATE_FORMAT, processStateString, null, null)) {
+ ret = true;
+ // Log.i(LOG_TAG,"State of process " + pid + " is " + processStateString[0]);
+ if (processStateString[0].equals("Z")) {
+ ret = false;
+ }
+ }
+ return ret;
+ }
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index ce213fb0ef0..f682abefcab 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -407,17 +407,17 @@ public Builder permitDiskReads() {
}
/**
- * Enable detection of disk reads.
+ * Enable detection of slow calls.
*/
public Builder detectCustomSlowCalls() {
return enable(DETECT_CUSTOM);
}
/**
- * Enable detection of disk reads.
+ * Disable detection of slow calls.
*/
public Builder permitCustomSlowCalls() {
- return enable(DETECT_CUSTOM);
+ return disable(DETECT_CUSTOM);
}
/**
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 156600e378e..a9584d05ba1 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +33,8 @@ public class SystemProperties
public static final int PROP_NAME_MAX = 31;
public static final int PROP_VALUE_MAX = 91;
+ public static final boolean QCOM_HARDWARE = native_get_boolean("com.qc.hardware", false);
+
private static final ArrayList sChangeCallbacks = new ArrayList();
private static native String native_get(String key);
@@ -153,4 +156,49 @@ static void callChangeCallbacks() {
}
}
}
+
+ /**
+ * Get the value for the given key.
+ * @return def string if the key isn't found
+ */
+ public static String getLongString(String key, String def) {
+ if (key.length() + 1 > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+ }
+ int chunks = getInt(key + '0', 0);
+ if (chunks == 0) {
+ return def;
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 1; i <= chunks; i++) {
+ sb.append(native_get(key + Integer.toString(i)));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Set the value for the given key.
+ * @throws IllegalArgumentException if the key exceeds 32 characters
+ */
+ public static void setLongString(String key, String val) {
+ if (key.length() + 1 > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+ }
+ int chunks = 0;
+ if (val != null && val.length() > 0) {
+ chunks = 1 + val.length() / (PROP_VALUE_MAX + 1);
+ }
+ native_set(key + '0', Integer.toString(chunks));
+ if (chunks > 0) {
+ for (int i = 1, start = 0; i <= chunks; i++) {
+ int end = start + PROP_VALUE_MAX;
+ if (end > val.length()) {
+ end = val.length();
+ }
+ native_set(key + Integer.toString(i), val.substring(start, end));
+ start = end;
+ }
+ }
+ }
+
}
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
old mode 100755
new mode 100644
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index fbf512c843b..8a20a6ea863 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -56,7 +56,7 @@ public class StorageManager
/*
* Our internal MountService binder reference
*/
- private IMountService mMountService;
+ final private IMountService mMountService;
/*
* The looper target for callbacks
@@ -289,7 +289,7 @@ void sendStorageStateChanged(String path, String oldState, String newState) {
* Constructs a StorageManager object through which an application can
* can communicate with the systems mount service.
*
- * @param tgtLooper The {@android.os.Looper} which events will be received on.
+ * @param tgtLooper The {@link android.os.Looper} which events will be received on.
*
* Applications can get instance of this class by calling
* {@link android.content.Context#getSystemService(java.lang.String)} with an argument
@@ -304,8 +304,6 @@ public StorageManager(Looper tgtLooper) throws RemoteException {
return;
}
mTgtLooper = tgtLooper;
- mBinderListener = new MountServiceBinderListener();
- mMountService.registerListener(mBinderListener);
}
@@ -322,6 +320,15 @@ public void registerListener(StorageEventListener listener) {
}
synchronized (mListeners) {
+ if (mBinderListener == null ) {
+ try {
+ mBinderListener = new MountServiceBinderListener();
+ mMountService.registerListener(mBinderListener);
+ } catch (RemoteException rex) {
+ Log.e(TAG, "Register mBinderListener failed");
+ return;
+ }
+ }
mListeners.add(new ListenerDelegate(listener));
}
}
@@ -347,7 +354,15 @@ public void unregisterListener(StorageEventListener listener) {
break;
}
}
- }
+ if (mListeners.size() == 0 && mBinderListener != null) {
+ try {
+ mMountService.unregisterListener(mBinderListener);
+ } catch (RemoteException rex) {
+ Log.e(TAG, "Unregister mBinderListener failed");
+ return;
+ }
+ }
+ }
}
/**
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 8df4339f374..336960e234e 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -55,6 +55,13 @@
* {@link SharedPreferences}. It is up to the subclass to decide how to store
* the value.
*
+ *
+ * Developer Guides
+ * For information about building a settings UI with Preferences,
+ * read the Settings
+ * guide.
+ *
+ *
* @attr ref android.R.styleable#Preference_icon
* @attr ref android.R.styleable#Preference_key
* @attr ref android.R.styleable#Preference_title
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 140bff04cc3..8fcaf2dd865 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -21,6 +21,7 @@
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ListActivity;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -31,6 +32,7 @@
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -84,6 +86,13 @@
* items. Doing this implicitly switches the class into its new "headers
* + fragments" mode rather than the old style of just showing a single
* preferences list.
+ *
+ *
+ * Developer Guides
+ * For information about using {@code PreferenceActivity},
+ * read the Settings
+ * guide.
+ *
*
*
* Sample Code
@@ -671,10 +680,16 @@ public boolean isMultiPane() {
* The default implementation returns true if the screen is large
* enough.
*/
+ //public boolean onIsMultiPane() {
+ //boolean preferMultiPane = getResources().getBoolean(
+ //com.android.internal.R.bool.preferences_prefer_dual_pane);
+ //return preferMultiPane;
+ //}
public boolean onIsMultiPane() {
- boolean preferMultiPane = getResources().getBoolean(
- com.android.internal.R.bool.preferences_prefer_dual_pane);
- return preferMultiPane;
+ boolean preferMultiPane = Settings.System.getBoolean(
+ getContentResolver(), Settings.System.FORCE_DUAL_PANEL, getResources().getBoolean(
+ com.android.internal.R.bool.preferences_prefer_dual_pane));
+ return preferMultiPane;
}
/**
diff --git a/core/java/android/preference/PreferenceCategory.java b/core/java/android/preference/PreferenceCategory.java
index 237c5ce8c84..d8af3248f7d 100644
--- a/core/java/android/preference/PreferenceCategory.java
+++ b/core/java/android/preference/PreferenceCategory.java
@@ -24,6 +24,13 @@
/**
* Used to group {@link Preference} objects
* and provide a disabled title above the group.
+ *
+ *
+ * Developer Guides
+ * For information about building a settings UI with Preferences,
+ * read the Settings
+ * guide.
+ *
*/
public class PreferenceCategory extends PreferenceGroup {
private static final String TAG = "PreferenceCategory";
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index bdd858ba6ef..11d8878274c 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -74,6 +74,13 @@
* As a convenience, this fragment implements a click listener for any
* preference in the current hierarchy, see
* {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}.
+ *
+ *
+ * Developer Guides
+ * For information about using {@code PreferenceFragment},
+ * read the Settings
+ * guide.
+ *
*
*
* Sample Code
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index d008fd699ed..f33a6be9ff5 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -33,6 +33,13 @@
* {@link Preference} objects. It is a base class for Preference objects that are
* parents, such as {@link PreferenceCategory} and {@link PreferenceScreen}.
*
+ *
+ * Developer Guides
+ * For information about building a settings UI with Preferences,
+ * read the Settings
+ * guide.
+ *
+ *
* @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
*/
public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent {
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index 6562de90a3c..5ca7d79c139 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -415,19 +415,20 @@ public Preference findPreference(CharSequence key) {
}
/**
- * Sets the default values from a preference hierarchy in XML. This should
+ * Sets the default values from an XML preference file by reading the values defined
+ * by each {@link Preference} item's {@code android:defaultValue} attribute. This should
* be called by the application's main activity.
*
- * If {@code readAgain} is false, this will only set the default values if this
- * method has never been called in the past (or the
- * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
- * preferences file is false). To attempt to set the default values again
- * bypassing this check, set {@code readAgain} to true.
*
* @param context The context of the shared preferences.
- * @param resId The resource ID of the preference hierarchy XML file.
+ * @param resId The resource ID of the preference XML file.
* @param readAgain Whether to re-read the default values.
- *
+ * If false, this method sets the default values only if this
+ * method has never been called in the past (or if the
+ * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
+ * preferences file is false). To attempt to set the default values again
+ * bypassing this check, set {@code readAgain} to true.
+ *
* Note: this will NOT reset preferences back to their default
* values. For that functionality, use
* {@link PreferenceManager#getDefaultSharedPreferences(Context)}
@@ -445,6 +446,25 @@ public static void setDefaultValues(Context context, int resId, boolean readAgai
* Similar to {@link #setDefaultValues(Context, int, boolean)} but allows
* the client to provide the filename and mode of the shared preferences
* file.
+ *
+ * @param context The context of the shared preferences.
+ * @param sharedPreferencesName A custom name for the shared preferences file.
+ * @param sharedPreferencesMode The file creation mode for the shared preferences file, such
+ * as {@link android.content.Context#MODE_PRIVATE} or {@link
+ * android.content.Context#MODE_PRIVATE}
+ * @param resId The resource ID of the preference XML file.
+ * @param readAgain Whether to re-read the default values.
+ * If false, this method will set the default values only if this
+ * method has never been called in the past (or if the
+ * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
+ * preferences file is false). To attempt to set the default values again
+ * bypassing this check, set {@code readAgain} to true.
+ *
+ * Note: this will NOT reset preferences back to their default
+ * values. For that functionality, use
+ * {@link PreferenceManager#getDefaultSharedPreferences(Context)}
+ * and clear it followed by a call to this method with this
+ * parameter set to true.
*
* @see #setDefaultValues(Context, int, boolean)
* @see #setSharedPreferencesName(String)
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index c17111ab8dc..db806760fb6 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -75,6 +75,13 @@
* clicked will show another screen of preferences such as "Prefer WiFi" (and
* the other preferences that are children of the "second_preferencescreen" tag).
*
+ *
+ * Developer Guides
+ * For information about building a settings UI with Preferences,
+ * read the Settings
+ * guide.
+ *
+ *
* @see PreferenceCategory
*/
public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener,
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index caf55d70226..b7630225c18 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -146,6 +146,11 @@ protected void onSampleStarting(SeekBarVolumizer volumizer) {
}
}
+ /** @hide */
+ protected boolean onVolumeChange(SeekBarVolumizer volumizer, int value) {
+ return true;
+ }
+
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
@@ -305,10 +310,14 @@ public void onProgressChanged(SeekBar seekBar, int progress,
}
void postSetVolume(int progress) {
- // Do the volume changing separately to give responsive UI
- mLastProgress = progress;
- mHandler.removeCallbacks(this);
- mHandler.post(this);
+ if (onVolumeChange(this, progress)) {
+ // Do the volume changing separately to give responsive UI
+ mLastProgress = progress;
+ mHandler.removeCallbacks(this);
+ mHandler.post(this);
+ } else {
+ mSeekBar.setProgress(mLastProgress);
+ }
}
public void onStartTrackingTouch(SeekBar seekBar) {
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 1ef0916e001..a28585cb3aa 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -1442,9 +1442,9 @@ public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException
attendeeValues.put(Attendees.ATTENDEE_STATUS,
subCursor.getInt(COLUMN_ATTENDEE_STATUS));
attendeeValues.put(Attendees.ATTENDEE_IDENTITY,
- subCursor.getInt(COLUMN_ATTENDEE_IDENTITY));
+ subCursor.getString(COLUMN_ATTENDEE_IDENTITY));
attendeeValues.put(Attendees.ATTENDEE_ID_NAMESPACE,
- subCursor.getInt(COLUMN_ATTENDEE_ID_NAMESPACE));
+ subCursor.getString(COLUMN_ATTENDEE_ID_NAMESPACE));
entity.addSubValue(Attendees.CONTENT_URI, attendeeValues);
}
} finally {
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index c7e3c084da8..355cebd052b 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -307,6 +307,14 @@ public interface PeopleColumns {
@Deprecated
public static final String CUSTOM_RINGTONE = "custom_ringtone";
+ /**
+ * A custom vibration associated with a person. Not always present.
+ * Type: TEXT (URI to the vibration)
+ * @deprecated see {@link android.provider.ContactsContract}
+ */
+ @Deprecated
+ public static final String CUSTOM_VIBRATION = "custom_vibration";
+
/**
* Whether the person should always be sent to voicemail. Not always
* present.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8e123ac63f0..52fa32bbf12 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -832,6 +832,13 @@ protected interface ContactOptionsColumns {
*/
public static final String CUSTOM_RINGTONE = "custom_ringtone";
+ /**
+ * URI for a custom vibration associated with the contact. If null or missing,
+ * the default vibration is used.
+ * Type: TEXT (URI to the vibration)
+ */
+ public static final String CUSTOM_VIBRATION = "custom_vibration";
+
/**
* Whether the contact should always be sent to voicemail. If missing,
* defaults to false.
@@ -8362,7 +8369,7 @@ public static String snippetize(String content, String displayName, String query
// Line contains the query string - now search for it at the start of tokens.
List lineTokens = new ArrayList();
List tokenOffsets = new ArrayList();
- split(contentLine.trim(), lineTokens, tokenOffsets);
+ split(contentLine, lineTokens, tokenOffsets);
// As we find matches against the query, we'll populate this list with the marked
// (or unchanged) tokens.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9f90371a82c..66276349be5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -979,7 +979,7 @@ public static boolean putInt(ContentResolver cr, String name, int value) {
* with that name. Note that internally setting values are always
* stored as strings, so this function converts the given value to a
* string (1 or 0) before storing it.
- *
+ *
* @param cr The ContentResolver to access.
* @param name The name of the setting to modify.
* @param value The new value for the setting.
@@ -1357,6 +1357,23 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2";
+ /**
+ * Allows automatic retrieval of mms contents
+ * Type: INT
+ * 0 -- false
+ * 1 -- true
+ * @hide
+ */
+ public static final String MMS_AUTO_RETRIEVAL = "mms_auto_retrieval";
+
+ /**
+ * Allows automatic retrieval of mms contents during roaming
+ * Type: INT
+ * 0 -- false
+ * 1 -- true
+ * @hide
+ */
+ public static final String MMS_AUTO_RETRIEVAL_ON_ROAMING = "mms_auto_on_roaming";
/**
* Determines whether remote devices may discover and/or connect to
@@ -1468,6 +1485,136 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
+ /**
+ * Indicates that custom light sensor settings has changed.
+ * The value is random and changes reloads light settings.
+ *
+ * @hide
+ */
+ public static final String LIGHTS_CHANGED = "lights_changed";
+
+ /**
+ * Whether custom light sensor levels & values are enabled. The value is
+ * boolean (1 or 0).
+ *
+ * @hide
+ */
+ public static final String LIGHT_SENSOR_CUSTOM = "light_sensor_custom";
+
+ /**
+ * Screen dim value to use if LIGHT_SENSOR_CUSTOM is set. The value is int.
+ * Default is android.os.BRIGHTNESS_DIM.
+ *
+ * @hide
+ */
+ public static final String LIGHT_SCREEN_DIM = "light_screen_dim";
+
+ /**
+ * Custom light sensor levels. The value is a comma separated int array
+ * with length N.
+ * Example: "100,300,3000".
+ *
+ * @hide
+ */
+ public static final String LIGHT_SENSOR_LEVELS = "light_sensor_levels";
+
+ /**
+ * Custom light sensor lcd values. The value is a comma separated int array
+ * with length N+1.
+ * Example: "10,50,100,255".
+ *
+ * @hide
+ */
+ public static final String LIGHT_SENSOR_LCD_VALUES = "light_sensor_lcd_values";
+
+ /**
+ * Custom light sensor lcd values. The value is a comma separated int array
+ * with length N+1.
+ * Example: "10,50,100,255".
+ *
+ * @hide
+ */
+ public static final String LIGHT_SENSOR_BUTTON_VALUES = "light_sensor_button_values";
+
+ /**
+ * Custom light sensor lcd values. The value is a comma separated int array
+ * with length N+1.
+ * Example: "10,50,100,255".
+ *
+ * @hide
+ */
+ public static final String LIGHT_SENSOR_KEYBOARD_VALUES = "light_sensor_keyboard_values";
+
+ /**
+ * Whether light sensor is allowed to decrease when calculating automatic
+ * backlight. The value is boolean (1 or 0).
+ *
+ * @hide
+ */
+ public static final String LIGHT_DECREASE = "light_decrease";
+
+ /**
+ * Light sensor hysteresis for decreasing backlight. The value is
+ * int (0-99) representing % (0-0.99 as float). Example:
+ *
+ * Levels Output
+ * 0 - 100 50
+ * 100 - 200 100
+ * 200 - Inf 255
+ *
+ * Current sensor value is 150 which gives light value 100. Hysteresis is 50.
+ * Current level lower bound is 100 and previous lower bound is 0.
+ * Sensor value must drop below 100-(100-0)*(50/100)=50 for output to become 50
+ * (corresponding to the 0 - 100 level).
+ * @hide
+ */
+ public static final String LIGHT_HYSTERESIS = "light_hysteresis";
+
+ /**
+ * Whether light sensor used when calculating automatic backlight should
+ * be filtered through an moving average filter.
+ * The value is boolean (1 or 0).
+ *
+ * @hide
+ */
+ public static final String LIGHT_FILTER = "light_filter";
+
+ /**
+ * Window length of filter used when calculating automatic backlight.
+ * One minute means that the average sensor value last minute is used.
+ * The value is integer (milliseconds)
+ *
+ * @hide
+ */
+ public static final String LIGHT_FILTER_WINDOW = "light_filter_window";
+
+ /**
+ * Reset threshold of filter used when calculating automatic backlight.
+ * Sudden large jumps in sensor value resets the filter. This is used
+ * to make the filter respond quickly to large enough changes in input
+ * while still filtering small changes. Example:
+ *
+ * Current filter value (average) is 100 and sensor value is changing to
+ * 10, 150, 100, 30, 50. The filter is continously taking the average of
+ * the samples. Now the user goes outside and the value jumps over 1000.
+ * The difference between current average and new sample is larger than
+ * the reset threshold and filter is reset. It begins calculating a new
+ * average on samples around 1000 (say, 800, 1200, 1000, 1100 etc.)
+ *
+ * The value is integer (lux)
+ *
+ * @hide
+ */
+ public static final String LIGHT_FILTER_RESET = "light_filter_reset";
+
+ /**
+ * Sample interval of filter used when calculating automatic backlight.
+ * The value is integer (milliseconds)
+ *
+ * @hide
+ */
+ public static final String LIGHT_FILTER_INTERVAL = "light_filter_interval";
+
/**
* Control whether the process CPU usage meter should be shown.
*/
@@ -1482,12 +1629,37 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
"always_finish_activities";
+ /** Volume Adjust Sounds Enable, This is the noise made when using volume hard buttons
+ * Defaults to 1 - sounds enabled
+ * @hide
+ */
+ public static final String VOLUME_ADJUST_SOUNDS_ENABLED = "volume_adjust_sounds_enabled";
+
/**
* Ringer mode. This is used internally, changing this value will not
* change the ringer mode. See AudioManager.
*/
public static final String MODE_RINGER = "mode_ringer";
+ /**
+ * User interface mode. This is used to change the UI mode forcing it to
+ * Change into tablet mode. Default is disabled.
+ */
+ public static final String MODE_TABLET_UI = "mode_tabletui";
+
+ /**
+ * If checked hide extra system bar stuff
+ * ie compatmode button and extra ime switcher.
+ */
+ public static final String HIDE_EXTRAS_SYSTEM_BAR = "hide_extras_system_bar";
+
+ /**
+ * User interface mode. This is used to change from singlepane mode forcing it to
+ * Change into multipane mode. Default is disabled.
+ */
+ public static final String FORCE_DUAL_PANEL = "force_dualpanel";
+
+
/**
* Determines which streams are affected by ringer mode changes. The
* stream type's bit should be set to 1 if it should be muted when going
@@ -1564,6 +1736,30 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
+ /**
+ * Whether the phone ringtone should be played in an increasing manner
+ * @hide
+ */
+ public static final String INCREASING_RING = "increasing_ring";
+
+ /**
+ * Minimum volume index for increasing ring volume
+ * @hide
+ */
+ public static final String INCREASING_RING_MIN_VOLUME = "increasing_ring_min_vol";
+
+ /**
+ * Time (in ms) between ringtone volume increases
+ * @hide
+ */
+ public static final String INCREASING_RING_INTERVAL = "increasing_ring_interval";
+
+ /**
+ * Whether to prevent loud volume levels when headset is first plugged in.
+ * @hide
+ */
+ public static final String SAFE_HEADSET_VOLUME_RESTORE = "safe_headset_volume_restore";
+
/**
* Master volume (float in the range 0.0f to 1.0f).
* @hide
@@ -1645,6 +1841,10 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE);
+ public static final Uri DEFAULT_VIBRATION_URI = Uri.parse("content://com.aokp.romcontrol.Vibrations/vibrations/0");
+
+ public static final String PHONE_VIBRATION = "phone_vibration";
+
/**
* Persistent store for the system-wide default notification sound.
*
@@ -1783,6 +1983,19 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation";
+ /**
+ * Control the type of rotation which can be performed using the accelerometer
+ * if ACCELEROMETER_ROTATION is enabled.
+ * Value is a bitwise combination of
+ * 1 = 0 degrees (portrait)
+ * 2 = 90 degrees (left)
+ * 4 = 180 degrees (inverted portrait)
+ * 8 = 270 degrees (right)
+ * Setting to 0 is effectively orientation lock
+ * @hide
+ */
+ public static final String ACCELEROMETER_ROTATION_ANGLES = "accelerometer_rotation_angles";
+
/**
* Default screen rotation when no other policy applies.
* When {@link #ACCELEROMETER_ROTATION} is zero and no on-screen Activity expresses a
@@ -1871,6 +2084,13 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String TTY_MODE = "tty_mode";
+ /**
+ * Whether noise suppression is enabled. The value is
+ * boolean (1 or 0).
+ * @hide
+ */
+ public static final String NOISE_SUPPRESSION = "noise_suppression";
+
/**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
* boolean (1 or 0).
@@ -1883,6 +2103,12 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
+ /**
+ * Whether the haptic feedback are enabled for statusbar toggles. The value is
+ * boolean (1 or 0).
+ */
+ public static final String HAPTIC_FEEDBACK_TOGGLES_ENABLED = "haptic_feedback_toggles_enabled";
+
/**
* @deprecated Each application that shows web suggestions should have its own
* setting for this.
@@ -1897,6 +2123,18 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
+ /** Sprint MWI Quirk: Show message wait indicator notifications
+ * @hide
+ */
+ public static final String ENABLE_MWI_NOTIFICATION = "enable_mwi_notification";
+
+ /**
+ * Whether the button backlights should be turned on when a notification came in.
+ * The value is boolean (1 or 0).
+ * @hide
+ */
+ public static final String NOTIFICATION_USE_BUTTON_BACKLIGHT = "notification_use_button_backlight";
+
/**
* Show pointer location on screen?
* 0 = no
@@ -1905,6 +2143,14 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String POINTER_LOCATION = "pointer_location";
+ /**
+ * Show icon when stylus is used?
+ * 0 = no
+ * 1 = yes
+ * @hide
+ */
+ public static final String STYLUS_ICON_ENABLED = "stylus_icon_enabled";
+
/**
* Show touch positions on screen?
* 0 = no
@@ -1913,6 +2159,13 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String SHOW_TOUCHES = "show_touches";
+ /**
+ * The keylayout that will be used by EventHub instead of the default
+ * one.
+ * @hide
+ */
+ public static final String KEYLAYOUT_OVERRIDES = "keylayout";
+
/**
* Log raw orientation data from {@link WindowOrientationListener} for use with the
* orientationplot.py tool.
@@ -1947,6 +2200,24 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled";
+ /**
+ * Stores values for custom lockscreen targets
+ * @hide
+ */
+ public static final String LOCKSCREEN_TARGETS = "lockscreen_targets";
+
+ /**
+ * Number of custom lockscreen targets
+ * @hide
+ */
+ public static final String LOCKSCREEN_TARGET_AMOUNT = "lockscreen_target_amount";
+
+ /**
+ * Whether to enable lockscreen rotation
+ * @hide
+ */
+ public static final String LOCKSCREEN_AUTO_ROTATE = "com.android.internal.R.config_enableLockScreenRotation";
+
/**
* URI for the low battery sound file.
* @hide
@@ -1989,6 +2260,13 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String UNLOCK_SOUND = "unlock_sound";
+ /**
+ * Stores values for custom lockscreen targets
+ * Whether the screen will be locked if a call ends and the screen is off.
+ * @hide
+ */
+ public static final String LOCKSCREEN_IF_CALL_ENDS_WITH_SCREENOFF = "lockscreen_if_call_ends_with_screenoff";
+
/**
* Receive incoming SIP calls?
* 0 = no
@@ -2034,6 +2312,81 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
*/
public static final String POINTER_SPEED = "pointer_speed";
+ /**
+ * Whether to enable custom rebindings of the actions performed on
+ * certain key press events.
+ * @hide
+ */
+ public static final String HARDWARE_KEY_REBINDING = "hardware_key_rebinding";
+
+ /**
+ * Action to perform when the home key is long-pressed. (Default is 2)
+ * 0 - Nothing
+ * 1 - Menu
+ * 2 - App-switch
+ * 3 - Search
+ * 4 - Voice search
+ * 5 - In-app search
+ * @hide
+ */
+ public static final String KEY_HOME_LONG_PRESS_ACTION = "key_home_long_press_action";
+
+ /**
+ * Action to perform when the menu key is pressed. (Default is 1)
+ * (See KEY_HOME_LONG_PRESS_ACTION for valid values)
+ * @hide
+ */
+ public static final String KEY_MENU_ACTION = "key_menu_action";
+
+ /**
+ * Action to perform when the menu key is long-pressed.
+ * (Default is 0 on devices with a search key, 3 on devices without)
+ * (See KEY_HOME_LONG_PRESS_ACTION for valid values)
+ * @hide
+ */
+ public static final String KEY_MENU_LONG_PRESS_ACTION = "key_menu_long_press_action";
+
+ /**
+ * Action to perform when the assistant (search) key is pressed. (Default is 3)
+ * (See KEY_HOME_LONG_PRESS_ACTION for valid values)
+ * @hide
+ */
+ public static final String KEY_ASSIST_ACTION = "key_assist_action";
+
+ /**
+ * Action to perform when the assistant (search) key is long-pressed. (Default is 4)
+ * (See KEY_HOME_LONG_PRESS_ACTION for valid values)
+ * @hide
+ */
+ public static final String KEY_ASSIST_LONG_PRESS_ACTION = "key_assist_long_press_action";
+
+ /**
+ * Action to perform when the app switch key is pressed. (Default is 2)
+ * (See KEY_HOME_LONG_PRESS_ACTION for valid values)
+ * @hide
+ */
+ public static final String KEY_APP_SWITCH_ACTION = "key_app_switch_action";
+
+ /**
+ * Action to perform when the app switch key is long-pressed. (Default is 0)
+ * (See KEY_HOME_LONG_PRESS_ACTION for valid values)
+ * @hide
+ */
+ public static final String KEY_APP_SWITCH_LONG_PRESS_ACTION = "key_app_switch_long_press_action";
+
+ /**
+ * Control the display of the action overflow button within app UI.
+ * 0 = use system default
+ * 1 = force on
+ */
+ public static final String UI_FORCE_OVERFLOW_BUTTON = "ui_force_overflow_button";
+
+ /**
+ * Whether national data roaming should be used.
+ * @hide
+ */
+ public static final String MVNO_ROAMING = "mvno_roaming";
+
/**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
@@ -2052,6 +2405,8 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
WIFI_STATIC_NETMASK,
WIFI_STATIC_DNS1,
WIFI_STATIC_DNS2,
+ MMS_AUTO_RETRIEVAL,
+ MMS_AUTO_RETRIEVAL_ON_ROAMING,
BLUETOOTH_DISCOVERABILITY,
BLUETOOTH_DISCOVERABILITY_TIMEOUT,
DIM_SCREEN,
@@ -2091,6 +2446,7 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
CALL_AUTO_RETRY,
HEARING_AID,
TTY_MODE,
+ NOISE_SUPPRESSION,
SOUND_EFFECTS_ENABLED,
HAPTIC_FEEDBACK_ENABLED,
POWER_SOUNDS_ENABLED,
@@ -2157,270 +2513,1111 @@ public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
public static final String LOCATION_PROVIDERS_ALLOWED = Secure.LOCATION_PROVIDERS_ALLOWED;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#LOGGING_ID} instead
+ * @deprecated Use {@link android.provider.Settings.Secure#LOGGING_ID} instead
+ */
+ @Deprecated
+ public static final String LOGGING_ID = Secure.LOGGING_ID;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#NETWORK_PREFERENCE} instead
+ */
+ @Deprecated
+ public static final String NETWORK_PREFERENCE = Secure.NETWORK_PREFERENCE;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#PARENTAL_CONTROL_ENABLED}
+ * instead
+ */
+ @Deprecated
+ public static final String PARENTAL_CONTROL_ENABLED = Secure.PARENTAL_CONTROL_ENABLED;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#PARENTAL_CONTROL_LAST_UPDATE}
+ * instead
+ */
+ @Deprecated
+ public static final String PARENTAL_CONTROL_LAST_UPDATE = Secure.PARENTAL_CONTROL_LAST_UPDATE;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#PARENTAL_CONTROL_REDIRECT_URL}
+ * instead
+ */
+ @Deprecated
+ public static final String PARENTAL_CONTROL_REDIRECT_URL =
+ Secure.PARENTAL_CONTROL_REDIRECT_URL;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#SETTINGS_CLASSNAME} instead
+ */
+ @Deprecated
+ public static final String SETTINGS_CLASSNAME = Secure.SETTINGS_CLASSNAME;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#USB_MASS_STORAGE_ENABLED} instead
+ */
+ @Deprecated
+ public static final String USB_MASS_STORAGE_ENABLED = Secure.USB_MASS_STORAGE_ENABLED;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#USE_GOOGLE_MAIL} instead
+ */
+ @Deprecated
+ public static final String USE_GOOGLE_MAIL = Secure.USE_GOOGLE_MAIL;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT} instead
+ */
+ @Deprecated
+ public static final String WIFI_MAX_DHCP_RETRY_COUNT = Secure.WIFI_MAX_DHCP_RETRY_COUNT;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS} instead
+ */
+ @Deprecated
+ public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
+ Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON} instead
+ */
+ @Deprecated
+ public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+ Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} instead
+ */
+ @Deprecated
+ public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
+ Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_NUM_OPEN_NETWORKS_KEPT}
+ * instead
+ */
+ @Deprecated
+ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Secure.WIFI_NUM_OPEN_NETWORKS_KEPT;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_ON} instead
+ */
+ @Deprecated
+ public static final String WIFI_ON = Secure.WIFI_ON;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE}
+ * instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
+ Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_AP_COUNT} instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_AP_COUNT = Secure.WIFI_WATCHDOG_AP_COUNT;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS} instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
+ Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED =
+ Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS}
+ * instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
+ Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS;
+
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT =
+ Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS}
+ * instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = Secure.WIFI_WATCHDOG_MAX_AP_CHECKS;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_ON} instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_ON = Secure.WIFI_WATCHDOG_ON;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_COUNT} instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_PING_COUNT = Secure.WIFI_WATCHDOG_PING_COUNT;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_DELAY_MS}
+ * instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_PING_DELAY_MS = Secure.WIFI_WATCHDOG_PING_DELAY_MS;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_TIMEOUT_MS}
+ * instead
+ */
+ @Deprecated
+ public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS =
+ Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS;
+
+ /**
+ * Setting to enable volume options.
+ *
+ * @hide
+ */
+ public static final String ENABLE_VOLUME_OPTIONS = "enable_volume_options";
+
+ /**
+ * Whether to use the menu key to unlock the screen
+ * @hide
+ */
+ public static final String LOCKSCREEN_MENU_UNLOCK = "lockscreen_menu_unlock";
+
+ /**
+ * Whether to use the custom quick unlock screen control
+ * @hide
+ */
+ public static final String LOCKSCREEN_QUICK_UNLOCK_CONTROL = "lockscreen_quick_unlock_control";
+
+ /**
+ * Setting to Link Ringtone and Notification.
+ *
+ * @hide
+ */
+ public static final String VOLUME_LINK_NOTIFICATION = "volume_link_notification";
+
+ /**
+ * Whether the UI is initiated in tablet UI (false = phone UI)
+ * @hide
+ * 0 = Phone UI
+ * 1 = Tablet UI
+ * 2 = Phablet UI
+ */
+ public static final String TABLET_UI = "tablet_ui";
+
+ /**
+ * Show the NavBar dialog in Power menu
+ * @hide
+ */
+ public static final String POWER_DIALOG_SHOW_NAVBAR_HIDE = "power_dialog_show_navbar_hide";
+
+ /**
+ * @hide
+ */
+ public static final String POWER_DIALOG_SHOW_SCREENSHOT = "power_dialog_show_screenshot";
+
+ /**
+ * @hide
+ */
+ public static final String POWER_DIALOG_SHOW_AIRPLANE_TOGGLE = "power_dialog_show_airplane_toggle";
+
+ /**
+ * Used to determine if the NavBar should be enabled on devices that do not
+ * otherwise have a NavBar
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_SHOW = "navigation_bar_show";
+
+ /**
+ * Used to determine if NavBar is currently shown or hidden as a user choice
+ *
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_SHOW_NOW = "navigation_bar_show_now";
+
+ /**
+ * Navigation bar height in portrait
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_HEIGHT = "navigation_bar_height";
+
+ /**
+ * Navigation bar height in landscape
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_HEIGHT_LANDSCAPE = "navigation_bar_height_landscape";
+
+ /**
+ * Navigation bar height in landscape if the bar is along the side of the device
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_WIDTH = "navigation_bar_width";
+
+ /**
+ * Display NavBar on left side of screen (Boolean)
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_LEFTY_MODE = "navigation_bar_lefty_mode";
+
+ /**
+ *
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_WIDTH_PORT = "navigation_bar_width_port";
+
+ /**
+ *
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_WIDTH_LAND = "navigation_bar_width_land";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_1 = "systemui_navring_1";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_2 = "systemui_navring_2";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_3 = "systemui_navring_3";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_4 = "systemui_navring_4";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_5 = "systemui_navring_5";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_LONG_1 = "systemui_navring_long_1";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_LONG_2 = "systemui_navring_long_2";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_LONG_3 = "systemui_navring_long_3";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_LONG_4 = "systemui_navring_long_4";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_LONG_5 = "systemui_navring_long_5";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_LONG_ENABLE = "systemui_navring_long_enable";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_AMOUNT = "systemui_navring_amount";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_ASSIST = "assist";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_REBOOT = "reboot";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_SCREENSHOT = "screenshot";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_SCREENOFF = "screenoff";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_IME_SWITCHER = "ime_switcher";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_RING_VIB = "ring_vib";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_RING_SILENT = "ring_silent";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_RING_VIB_SILENT = "ring_vib_silent";
+
+ /**
+ * hide
+ */
+ public static final String SYSTEMUI_SOFTKEY_KILL_PROCESS = "killcurrent";
+
+ /**
+ * @hide
+ */
+ public static final String SYSTEMUI_NAVRING_OVERRIDE_HOME = "systemui_navring_override_home";
+
+ /**
+ * @hide
+ */
+ public static final int SYSTEMUI_NAVRING_OVERRIDE_HOME_DEF = 0;
+
+ /**
+ * whether volume keys wake the screen. boolean value
+ *
+ * @hide
+ */
+ public static final String VOLUME_WAKE_SCREEN = "volume_wake_screen";
+
+ /**
+ * Whether volume up/down can be long pressed to skip tracks
+ * @hide
+ */
+ public static final String VOLUME_MUSIC_CONTROLS = "volume_music_controls";
+
+ /**
+ * NFC polling mode configuration key
+ *
+ * @hide
+ */
+ public static final String NFC_POLLING_MODE = "nfc_polling_mode";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_USE_BUTTONS = "statusbar_toggles_use_buttons";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_SETTINGS_BEHAVIOR = "statusbar_settings_behavior";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_AUTOHIDE = "statusbar_toggles_autohide";
+
+ /**
+ * @hide
+ */
+ public static final String STATUS_BAR_BRIGHTNESS_SLIDER = "statusbar_brightness_slider";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES = "statusbar_toggles";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_STYLE = "statusbar_toggles_style";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_BRIGHTNESS_LOC = "statusbar_toggles_brightness_loc";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_NUMBER_PER_ROW = "statusbar_toggles_number_per_row";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_ENABLED_COLOR = "statusbar_toggles_enabled_color";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_DISABLED_COLOR = "statusbar_toggles_disabled_color";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_TEXT_COLOR = "statusbar_toggles_text_color";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_ALPHA = "statusbar_toggles_alpha";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_TOGGLES_BACKGROUND = "statusbar_toggles_background";
+
+ /**
+ * @hide
+ */
+ public static final String STATUS_BAR_LAYOUT = "statusbar_layout";
+
+ /**
+ * Show the pending notification counts as overlays on the status bar
+ * @hide
+ */
+ public static final String STATUS_BAR_NOTIF_COUNT = "status_bar_notif_count";
+
+ /**
+ * @hide
+ * AM/PM Style for clock options
+ * 0 - Normal AM/PM
+ * 1 - Small AM/PM
+ * 2 - No AM/PM
+ * 3 - ProTekk 9999999999999999999
+ */
+ public static final String STATUSBAR_CLOCK_AM_PM_STYLE = "statusbar_clock_am_pm_style";
+
+ /**
+ * @hide
+ * Style of clock
+ * 0 - Hide Clock
+ * 1 - Right Clock
+ * 2 - Center Clock
+ */
+ public static final String STATUSBAR_CLOCK_STYLE = "statusbar_clock_style";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_CLOCK_COLOR = "statusbar_clock_color";
+
+ /**
+ * @hide
+ * Shows weekday before clock time
+ * 0 - No Day
+ * 1 - Small Day
+ * 2 - Normal Day
+ */
+ public static final String STATUSBAR_CLOCK_WEEKDAY = "statusbar_clock_weekday";
+
+ /**
+ * @hide
+ * Vibrate when expanding notifications
+ * 0 - no vibrate
+ * 1 - vibrate
+ */
+ public static final String VIBRATE_NOTIF_EXPAND = "vibrate_notif_expand";
+
+ /**
+ * @hide
+ * Show Wifi network name in notification shade
+ * 0 - don't show
+ * 1 - show
+ */
+ public static final String NOTIFICATION_SHOW_WIFI_SSID = "notification_show_wifi_ssid";
+
+ /**
+ * @hide
+ * Style of Battery
+ * 0 - Icon Only
+ * 1 - Text Only
+ * 2 - Icon Text
+ * 3 - Icon Centered Text
+ * 4 - Icon Circle
+ * 5 - Hide
+ */
+ public static final String STATUSBAR_BATTERY_ICON = "statusbar_battery_icon";
+
+ /**
+ * Statusbar transparency value
+ * from 0% to 100%
+ * @hide
+ */
+ public static final String STATUS_BAR_TRANSPARENCY = "status_bar_transparency";
+
+ /**
+ * @hide
+ * Shows the battery icon in the notification pull down
+ */
+ public static final String NOTIFICATION_BATTERY_DISPLAY = "notification_battery_display";
+
+ /**
+ * custom lockscreen text color
+ * @hide
+ */
+ public static final String LOCKSCREEN_CUSTOM_TEXT_COLOR = "lockscreen_custom_text_color";
+
+ /**
+ * Setting to allow % on lockscreen always showing.
+ * @hide
+ */
+ public static final String LOCKSCREEN_BATTERY = "lockscreen_battery";
+
+ /**
+ * Whether to show weather on lockscreen
+ * @hide
+ */
+ public static final String LOCKSCREEN_WEATHER = "lockscreen_weather";
+
+ /**
+ * Style of weather shown on lockscreen (text or panel)
+ * @hide
+ */
+ public static final String LOCKSCREEN_WEATHER_TYPE = "lockscreen_weather_type";
+
+ /**
+ *
+ *
+ * @hide
+ */
+ public static final String WEATHER_PANEL_LONGCLICK = "weather_panel_longclick";
+
+ /**
+ *
+ *
+ * @hide
+ */
+ public static final String WEATHER_PANEL_SHORTCLICK = "weather_panel_shortclick";
+
+
+ /**
+ * Whether to show upcoming events on lockscreen
+ * @hide
+ */
+ public static final String LOCKSCREEN_CALENDAR = "lockscreen_calendar";
+
+ /**
+ * Whether to flip the calendar indicator
+ * @hide
+ */
+ public static final String LOCKSCREEN_CALENDAR_FLIP = "lockscreen_calendar_flip";
+
+ /**
+ * Selected calendars to get the events from
+ * @hide
+ */
+ public static final String LOCKSCREEN_CALENDAR_SOURCES = "lockscreen_calendar_sources";
+
+ /**
+ * Time range of the pulled events
+ * @hide
+ */
+ public static final String LOCKSCREEN_CALENDAR_RANGE = "lockscreen_calendar_range";
+
+ /**
+ * Whether to hide an event if it already started
+ * @hide
+ */
+ public static final String LOCKSCREEN_CALENDAR_HIDE_ONGOING = "lockscreen_calendar_hide_ongoing";
+
+ /**
+ * Whether to show an event in the color of the parent calendar
+ * @hide
+ */
+ public static final String LOCKSCREEN_CALENDAR_USE_COLORS = "lockscreen_calendar_use_colors";
+
+ /**
+ * If flipping, interval of flips
+ * @hide
+ */
+ public static final String LOCKSCREEN_CALENDAR_INTERVAL = "lockscreen_calendar_interval";
+
+ /**
+ * Whether to show the stock music layout on the lockscreen
+ * @hide
+ */
+ public static final String LOCKSCREEN_STOCK_MUSIC_LAYOUT = "lockscreen_stock_music_layout";
+
+ /**
+ * Action for long-pressing back button on lock screen
+ * @hide
+ */
+ public static final String LOCKSCREEN_LONG_BACK_ACTION = "lockscreen_long_back_action";
+
+ /**
+ * Action for long-pressing home button on lock screen
+ * @hide
+ */
+ public static final String LOCKSCREEN_LONG_HOME_ACTION = "lockscreen_long_home_action";
+
+ /**
+ * Action for long-pressing menu button on lock screen
+ * @hide
+ */
+ public static final String LOCKSCREEN_LONG_MENU_ACTION = "lockscreen_long_menu_action";
+
+ /**
+ * Whether to show the alt lockscreen layout
+ * @hide
+ */
+ public static final String USE_CIRCLES_LOCKSCREEN = "use_circles_lockscreen";
+
+ /**
+ * Sets bg color of alt lockscreen bg
+ * @hide
+ */
+ public static final String CIRCLES_LOCK_BG_COLOR = "circles_lock_bg_color";
+
+ /**
+ * Sets ring color of alt lockscreen
+ * @hide
+ */
+ public static final String CIRCLES_LOCK_RING_COLOR = "circles_lock_ring_color";
+
+ /**
+ * Sets halo color of alt lockscreen
+ * @hide
+ */
+ public static final String CIRCLES_LOCK_HALO_COLOR = "circles_lock_halo_color";
+
+ /**
+ * Sets wave color of alt lockscreen
+ * @hide
+ */
+ public static final String CIRCLES_LOCK_WAVE_COLOR = "circles_lock_wave_color";
+
+ /**
+ * Sets ring alpha of alt lockscreen
+ * @hide
+ */
+ public static final String CIRCLES_LOCK_RING_ALPHA = "circles_lock_ring_alpha";
+
+ /**
+ * Sets halo alpha of alt lockscreen
+ * @hide
+ */
+ public static final String CIRCLES_LOCK_HALO_ALPHA = "circles_lock_halo_alpha";
+
+ /**
+ * Sets wave alpha of alt lockscreen
+ * @hide
+ */
+ public static final String CIRCLES_LOCK_WAVE_ALPHA = "circles_lock_wave_alpha";
+
+ /**
+ * How to show weather on the statusbar
+ *
+ * @hide
+ */
+ public static final String STATUSBAR_WEATHER_STYLE = "statusbar_weather_style";
+
+ /**
+ * How to hide weather panel
+ * 1 = Default always on
+ * 2 = Same as toggles
+ * 3 = Opposite toggles
+ * @hide
+ */
+ public static final String STATUSBAR_WEATHER_HIDE = "statusbar_weather_hide";
+
+ /**
+ * Whether to show the battery bar
+ *
+ * @hide
+ */
+ public static final String STATUSBAR_BATTERY_BAR = "statusbar_battery_bar";
+
+ /**
+ * Volume keys control cursor in text fields (default is 0)
+ * 0 - Disabled
+ * 1 - Volume up/down moves cursor left/right
+ * 2 - Volume up/down moves cursor right/left
+ * @hide
+ */
+ public static final String VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control";
+
+ /**
+ * @hide
+ */
+ public static final String STATUSBAR_BATTERY_BAR_COLOR = "statusbar_battery_bar_color";
+
+ /**
+ * thickness of the batteyr bar (in dp)
+ *
+ * @hide
+ */
+ public static final String STATUSBAR_BATTERY_BAR_THICKNESS = "statusbar_battery_bar_thickness";
+
+ /**
+ * 0 = regular
+ * 1 = mirrored from center
+ *
+ * @hide
+ */
+ public static final String STATUSBAR_BATTERY_BAR_STYLE = "statusbar_battery_bar_style";
+
+ /**
+ * whether to show charging animation
+ *
+ * @hide
+ */
+ public static final String STATUSBAR_BATTERY_BAR_ANIMATE = "statusbar_battery_bar_animate";
+
+ /**
+ * Sets the alpha (transparency) of notification wallpaper
+ * @hide
+ */
+ public static final String NOTIF_WALLPAPER_ALPHA = "notif_wallpaper_alpha";
+
+ /**
+ * Whether to enable quiet hours.
+ * @hide
+ */
+ public static final String QUIET_HOURS_ENABLED = "quiet_hours_enabled";
+
+ /**
+ * Sets when quiet hours starts. This is stored in minutes from the start of the day.
+ * @hide
+ */
+ public static final String QUIET_HOURS_START = "quiet_hours_start";
+
+ /**
+ * Sets when quiet hours end. This is stored in minutes from the start of the day.
+ * @hide
*/
- @Deprecated
- public static final String LOGGING_ID = Secure.LOGGING_ID;
+ public static final String QUIET_HOURS_END = "quiet_hours_end";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#NETWORK_PREFERENCE} instead
+ * Whether to remove the sound from outgoing notifications during quiet hours.
+ * @hide
*/
- @Deprecated
- public static final String NETWORK_PREFERENCE = Secure.NETWORK_PREFERENCE;
+ public static final String QUIET_HOURS_NOTIFICATIONS = "quiet_hours_notifications";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#PARENTAL_CONTROL_ENABLED}
- * instead
+ * Whether to mute phone ringtones during quiet hours.
+ * @hide
*/
- @Deprecated
- public static final String PARENTAL_CONTROL_ENABLED = Secure.PARENTAL_CONTROL_ENABLED;
+ public static final String QUIET_HOURS_RINGER = "quiet_hours_ringer";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#PARENTAL_CONTROL_LAST_UPDATE}
- * instead
+ * Whether to disable vibrations during quiet hours.
+ * @hide
*/
- @Deprecated
- public static final String PARENTAL_CONTROL_LAST_UPDATE = Secure.PARENTAL_CONTROL_LAST_UPDATE;
+ public static final String QUIET_HOURS_STILL = "quiet_hours_still";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#PARENTAL_CONTROL_REDIRECT_URL}
- * instead
+ * Whether to attempt to dim the LED color during quiet hours.
+ * @hide
*/
- @Deprecated
- public static final String PARENTAL_CONTROL_REDIRECT_URL =
- Secure.PARENTAL_CONTROL_REDIRECT_URL;
+ public static final String QUIET_HOURS_DIM = "quiet_hours_dim";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#SETTINGS_CLASSNAME} instead
+ * Whether disable Bln during quiet hours.
+ * @hide
*/
- @Deprecated
- public static final String SETTINGS_CLASSNAME = Secure.SETTINGS_CLASSNAME;
+ public static final String QUIET_HOURS_BLN = "quiet_hours_bln";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#USB_MASS_STORAGE_ENABLED} instead
+ * Key to store Torch state.
+ * @hide
*/
- @Deprecated
- public static final String USB_MASS_STORAGE_ENABLED = Secure.USB_MASS_STORAGE_ENABLED;
+ public static final String TORCH_STATE = "torch_state";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#USE_GOOGLE_MAIL} instead
+ * where to show the legacy menu key
+ * 0 = right (default)
+ * 1 = left
+ * 2 = both
+ *
+ * @hide
*/
- @Deprecated
- public static final String USE_GOOGLE_MAIL = Secure.USE_GOOGLE_MAIL;
+ public static final String MENU_LOCATION = "menu_location";
- /**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT} instead
+ /**
+ * Menu visibility style
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_MAX_DHCP_RETRY_COUNT = Secure.WIFI_MAX_DHCP_RETRY_COUNT;
+ public static final String MENU_VISIBILITY = "menu_visibility";
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS} instead
+ * IME Switcher
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
- Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS;
+ public static final String SHOW_STATUSBAR_IME_SWITCHER = "show_statusbar_ime_switcher";
+
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON} instead
+ * Number of custom navbar buttons
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
- Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
+ public static final String NAVIGATION_BAR_BUTTONS_QTY = "navigation_bar_buttons_qty";
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} instead
+ * Custom navigation bar intents (short press)
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
- Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
+ public static final String[] NAVIGATION_CUSTOM_ACTIVITIES = new String[] {
+ "navigation_custom_app_intent_0",
+ "navigation_custom_app_intent_1",
+ "navigation_custom_app_intent_2",
+ "navigation_custom_app_intent_3",
+ "navigation_custom_app_intent_4",
+ "navigation_custom_app_intent_5",
+ "navigation_custom_app_intent_6",
+ };
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_NUM_OPEN_NETWORKS_KEPT}
- * instead
+ * Custom navigation bar intents (long press)
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Secure.WIFI_NUM_OPEN_NETWORKS_KEPT;
+ public static final String[] NAVIGATION_LONGPRESS_ACTIVITIES = new String[] {
+ "navigation_longpress_app_intent_0",
+ "navigation_longpress_app_intent_1",
+ "navigation_longpress_app_intent_2",
+ "navigation_longpress_app_intent_3",
+ "navigation_longpress_app_intent_4",
+ "navigation_longpress_app_intent_5",
+ "navigation_longpress_app_intent_6",
+ };
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_ON} instead
+ * Drawable URIs, each index needs to be matched up to NAVIGATION_CUSTOM_ACTIVITIES
+ *
+ * OR ELSE
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_ON = Secure.WIFI_ON;
+ public static final String[] NAVIGATION_CUSTOM_APP_ICONS = new String[] {
+ "navigation_custom_app_icon_0",
+ "navigation_custom_app_icon_1",
+ "navigation_custom_app_icon_2",
+ "navigation_custom_app_icon_3",
+ "navigation_custom_app_icon_4",
+ "navigation_custom_app_icon_5",
+ "navigation_custom_app_icon_6",
+ };
+
+ /**
+ * Widgets to show, should be separated by |
+ */
+ public static final String NAVIGATION_BAR_WIDGETS = "navigation_bar_widgets";
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE}
- * instead
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
- Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE;
+ public static final String NAVIGATION_BAR_TINT = "navigation_bar_tint";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_AP_COUNT} instead
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_AP_COUNT = Secure.WIFI_WATCHDOG_AP_COUNT;
+ public static final String NAVIGATION_BAR_GLOW_TINT = "navigation_bar_glow_tint";
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS} instead
+ * [0] = how long to animate glow off
+ * [1] = how long to animate glow on
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
- Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS;
+ public static final String[] NAVIGATION_BAR_GLOW_DURATION = new String[] {
+ "navigation_bar_glow_duration_off",
+ "navigation_bar_glow_duration_on"
+ };
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead
+ * How long to keep the notification LED on (in milliseconds)
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED =
- Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED;
+ public static final String NOTIFICATION_LIGHT_ON = "notification_light_on";
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS}
- * instead
+ * How long to keep the notification LED off (in milliseconds)
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
- Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS;
+ public static final String NOTIFICATION_LIGHT_OFF = "notification_light_off";
/**
- * @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} instead
+ * What color to use for the notificaion LED
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT =
- Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT;
+ public static final String NOTIFICATION_LIGHT_COLOR = "notification_light_color";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS}
- * instead
+ * Custom string for package;color|pacakge;color
+ * so we can change custom colors per app
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = Secure.WIFI_WATCHDOG_MAX_AP_CHECKS;
+ public static final String LED_CUSTOM_VALUES = "led_custom_values";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_ON} instead
+ * What brightness to use for the notificaion LED
+ *
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_ON = Secure.WIFI_WATCHDOG_ON;
+ public static final String LED_BRIGHTNESS = "led_brightness";
+
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_COUNT} instead
+ *
+ * @hide
+ */
+ public static final String NAVIGATION_BAR_BUTTON_ALPHA = "navigation_bar_button_alpha";
+
+ /**
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_PING_COUNT = Secure.WIFI_WATCHDOG_PING_COUNT;
+ public static final String USE_WEATHER = "use_weather";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_DELAY_MS}
- * instead
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_PING_DELAY_MS = Secure.WIFI_WATCHDOG_PING_DELAY_MS;
+ public static final String WEATHER_SHOW_LOCATION = "weather_show_location";
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_TIMEOUT_MS}
- * instead
+ * How long to wait between playing notification sounds from a package
+ * Should be in milliseconds. 0 to disable
+ * @hide
*/
- @Deprecated
- public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS =
- Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS;
+ public static final String MUTE_ANNOYING_NOTIFICATIONS_THRESHOLD = "mute_annoying_notifications_threshold";
- /**
- * Whether the UI is initiated in tablet UI (false = phone UI)
+ /**
+ * Holds the text for the Carrier Label. An empty string will bring
+ * the default text back.
* @hide
*/
- public static final String TABLET_UI = "tablet_ui";
+ public static final String CUSTOM_CARRIER_LABEL = "custom_carrier_label";
/**
+ * whether to hide the kill-all-button on recent switcher
+ *
* @hide
*/
- public static final String STATUSBAR_TOGGLES_USE_BUTTONS = "statusbar_toggles_use_buttons";
+ public static final String RECENT_KILL_ALL_BUTTON = "recent_kill_all_button";
/**
+ * whether to hide the Ram Usage Bar on recent switcher
+ *
* @hide
*/
- public static final String STATUS_BAR_BRIGHTNESS_SLIDER = "statusbar_brightness_slider";
+ public static final String RAM_USAGE_BAR = "ram_usage_bar";
/**
+ * whether to enable end app on back longpress functionality
+ *
* @hide
*/
- public static final String STATUSBAR_TOGGLES = "statusbar_toggles";
+ public static final String KILL_APP_LONGPRESS_BACK = "kill_app_longpress_back";
/**
+ *
+ *
* @hide
*/
- public static final String STATUSBAR_TOGGLES_STYLE = "statusbar_toggles_style";
+ public static final String NOTIFICATION_DATE_LONGCLICK = "notification_date_longclick";
/**
+ *
+ *
* @hide
*/
- public static final String STATUSBAR_TOGGLES_BRIGHTNESS_LOC = "statusbar_toggles_brightness_loc";
+ public static final String NOTIFICATION_DATE_SHORTCLICK = "notification_date_shortclick";
/**
+ *
+ *
* @hide
*/
- public static final String STATUSBAR_TOGGLES_NUMBER_PER_ROW = "statusbar_toggles_number_per_row";
+ public static final String NOTIFICATION_CLOCK_LONGCLICK = "notification_clock_longclick";
/**
+ *
+ *
* @hide
*/
- public static final String STATUS_BAR_LAYOUT = "statusbar_layout";
+ public static final String NOTIFICATION_CLOCK_SHORTCLICK = "notification_clock_shortclick";
/**
+ * Whether to show statusbar signal text
+ *
* @hide
- * AM/PM Style for clock options
- * 0 - Normal AM/PM
- * 1 - Small AM/PM
- * 2 - No AM/PM
- * 3 - ProTekk 9999999999999999999
*/
- public static final String STATUSBAR_CLOCK_AM_PM_STYLE = "statusbar_clock_am_pm_style";
+ public static final String STATUSBAR_SIGNAL_TEXT = "statusbar_signal_text";
/**
+ * statusbar signal text color
+ *
* @hide
- * Style of clock
- * 0 - Hide Clock
- * 1 - Right Clock
- * 2 - Center Clock
*/
- public static final String STATUSBAR_CLOCK_STYLE = "statusbar_clock_style";
+ public static final String STATUSBAR_SIGNAL_TEXT_COLOR = "statusbar_signal_text_color";
/**
+ * whether to hide the signal barss
+ *
* @hide
- * Shows weekday before clock time
- * 0 - No Day
- * 1 - Small Day
- * 2 - Normal Day
*/
- public static final String STATUSBAR_CLOCK_WEEKDAY = "statusbar_clock_weekday";
+ public static final String STATUSBAR_HIDE_SIGNAL_BARS = "statusbar_hide_signal_bars";
+ /**
+ * Whether to show statusbar WiFi signal text
+ *
+ * @hide
+ */
+ public static final String STATUSBAR_WIFI_SIGNAL_TEXT = "statusbar_wifi_signal_text";
/**
+ * statusbar WIFI signal text color
+ *
* @hide
- * Style of Battery
- * 0 - Icon Only
- * 1 - Text Only
- * 2 - Icon Text
- * 3 - Icon Centered Text
- * 4 - Icon Circle
- * 5 - Hide
*/
- public static final String STATUSBAR_BATTERY_ICON = "statusbar_battery_icon";
+ public static final String STATUSBAR_WIFI_SIGNAL_TEXT_COLOR = "statusbar_wifi_signal_text_color";
/**
+ * use Alt Statusbar Signal Layout
+ * boolean
+ *
+ * @hide
+ */
+ public static final String STATUSBAR_SIGNAL_CLUSTER_ALT = "statusbar_signal_cluster_alt";
+
+ /**
+ * use Alt Activity Resolver Grid (GB style)
+ * boolean
+ *
* @hide
- * Shows the battery icon in the notification pull down
*/
- public static final String NOTIFICATION_BATTERY_DISPLAY = "notification_battery_display";
+ public static final String ACTIVITY_RESOLVER_USE_ALT = "activity_resolver_use_alt";
}
/**
@@ -2721,6 +3918,24 @@ public static boolean putFloat(ContentResolver cr, String name, float value) {
*/
public static final String ADB_ENABLED = "adb_enabled";
+ /**
+ * The TCP/IP port to run ADB on, or -1 for USB
+ * @hide
+ */
+ public static final String ADB_PORT = "adb_port";
+
+ /**
+ * Whether to display the ADB notification.
+ * @hide
+ */
+ public static final String ADB_NOTIFY = "adb_notify";
+
+ /**
+ * The hostname for this device
+ * @hide
+ */
+ public static final String DEVICE_HOSTNAME = "device_hostname";
+
/**
* Setting to allow mock locations and location provider status to be injected into the
* LocationManager service for testing purposes during application development. These
@@ -2869,6 +4084,13 @@ public static final String getBluetoothInputDevicePriorityKey(String address) {
*/
public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ /**
+ * Whether to blink the LED when screen is on
+ *
+ * @hide
+ */
+ public static final String LED_SCREEN_ON = "led_screen_on";
+
/**
* Comma-separated list of location providers that activities may access.
*/
@@ -2931,6 +4153,11 @@ public static final String getBluetoothInputDevicePriorityKey(String address) {
*/
public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
+ /**
+ * External GPS source/device
+ * @hide
+ */
+ public static final String EXTERNAL_GPS_BT_DEVICE = "0";
/**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
@@ -3528,14 +4755,6 @@ public static final String getBluetoothInputDevicePriorityKey(String address) {
public static final String CDMA_CELL_BROADCAST_SMS =
"cdma_cell_broadcast_sms";
- /**
- * The cdma subscription 0 = Subscription from RUIM, when available
- * 1 = Subscription from NV
- * @hide
- */
- public static final String PREFERRED_CDMA_SUBSCRIPTION =
- "preferred_cdma_subscription";
-
/**
* Whether the enhanced voice privacy mode is enabled.
* 0 = normal voice privacy
@@ -4208,6 +5427,36 @@ public static final String getBluetoothInputDevicePriorityKey(String address) {
public static final int INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT =
INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF;
+ /**
+ * What happens when the user presses the Home button when the
+ * phone is ringing.
+ * Values:
+ * 1 - Nothing happens. (Default behavior)
+ * 2 - The Home button answer the current call.
+ *
+ * @hide
+ */
+ public static final String RING_HOME_BUTTON_BEHAVIOR = "ring_home_button_behavior";
+
+ /**
+ * RING_HOME_BUTTON_BEHAVIOR value for "do nothing".
+ * @hide
+ */
+ public static final int RING_HOME_BUTTON_BEHAVIOR_DO_NOTHING = 0x1;
+
+ /**
+ * RING_HOME_BUTTON_BEHAVIOR value for "answer".
+ * @hide
+ */
+ public static final int RING_HOME_BUTTON_BEHAVIOR_ANSWER = 0x2;
+
+ /**
+ * RING_HOME_BUTTON_BEHAVIOR default value.
+ * @hide
+ */
+ public static final int RING_HOME_BUTTON_BEHAVIOR_DEFAULT =
+ RING_HOME_BUTTON_BEHAVIOR_DO_NOTHING;
+
/**
* The current night mode that has been selected by the user. Owned
* and controlled by UiModeManagerService. Constants are as per
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 08a99d2fb9e..49be1daabe2 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -32,20 +32,27 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
+import android.os.Bundle;
import android.os.ParcelUuid;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.provider.Settings;
import android.util.Log;
+import com.android.internal.R;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
@@ -69,17 +76,49 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private final BluetoothAdapter mAdapter;
private int mTargetA2dpState;
private BluetoothDevice mPlayingA2dpDevice;
+
private IntentBroadcastHandler mIntentBroadcastHandler;
private final WakeLock mWakeLock;
private static final int MSG_CONNECTION_STATE_CHANGED = 0;
+ /* AVRCP1.3 Metadata variables */
+ private String mTrackName = DEFAULT_METADATA_STRING;
+ private String mArtistName = DEFAULT_METADATA_STRING;
+ private String mAlbumName = DEFAULT_METADATA_STRING;
+ private String mMediaNumber = DEFAULT_METADATA_NUMBER;
+ private String mMediaCount = DEFAULT_METADATA_NUMBER;
+ private String mDuration = DEFAULT_METADATA_NUMBER;
+ private int mPlayStatus = (int)Integer.valueOf(DEFAULT_METADATA_NUMBER);
+ private long mPosition = (long)Long.valueOf(DEFAULT_METADATA_NUMBER);
+
+ /* AVRCP1.3 Events */
+ private final static int EVENT_PLAYSTATUS_CHANGED = 0x1;
+ private final static int EVENT_TRACK_CHANGED = 0x2;
+
+ private final static String DEFAULT_METADATA_STRING = "Unknown";
+ private final static String DEFAULT_METADATA_NUMBER = "0";
+
+ /* AVRCP 1.3 PlayStatus */
+ private final static int STATUS_STOPPED = 0X00;
+ private final static int STATUS_PLAYING = 0X01;
+ private final static int STATUS_PAUSED = 0X02;
+ private final static int STATUS_FWD_SEEK = 0X03;
+ private final static int STATUS_REV_SEEK = 0X04;
+ private final static int STATUS_ERROR = 0XFF;
+
+ /* AVRCP 1.3 Intents */
+ private List metachanged_intents;
+ private List playstatechanged_intents;
+
+ /* AVRCP 1.3 special extra keys */
+ private List has_special_extra_keys;
+ private HashMap special_extra_keys;
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- BluetoothDevice device =
- intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
@@ -93,6 +132,8 @@ public void onReceive(Context context, Intent intent) {
}
} else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
synchronized (this) {
+ BluetoothDevice device =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (mAudioDevices.containsKey(device)) {
int state = mAudioDevices.get(device);
handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
@@ -117,10 +158,273 @@ public void onReceive(Context context, Intent intent) {
}
}
}
+ } else if (metachanged_intents.contains(action)) {
+ try {
+ if(DBG) {
+ Log.d(TAG, "action: " + action);
+
+ Bundle extras = intent.getExtras();
+
+ if (extras != null) {
+ Set ks = extras.keySet();
+ Iterator iterator = ks.iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ Object value = extras.get(key);
+ if (value != null)
+ Log.d(TAG, key + ": " + value.toString());
+ }
+ }
+ }
+
+ // check if there are special extra keys that we will use
+ if (has_special_extra_keys.contains(action)) {
+ if (special_extra_keys.containsKey(action + "_track")) {
+ mTrackName = intent.getStringExtra(special_extra_keys.get(action + "_track"));
+ }
+ else {
+ mTrackName = intent.getStringExtra("track");
+ }
+
+ if (special_extra_keys.containsKey(action + "_artist")) {
+ mArtistName = intent.getStringExtra(special_extra_keys.get(action + "_artist"));
+ }
+ else {
+ mArtistName = intent.getStringExtra("artist");
+ }
+
+ if (special_extra_keys.containsKey(action + "_album")) {
+ mAlbumName = intent.getStringExtra(special_extra_keys.get(action + "_album"));
+ }
+ else {
+ mAlbumName = intent.getStringExtra("album");
+ }
+
+ long extra;
+ if (special_extra_keys.containsKey(action + "_id")){
+ extra = intent.getLongExtra(special_extra_keys.get(action + "_id"), 0);
+ }
+ else {
+ extra = intent.getLongExtra("id", 0);
+ }
+ if (extra < 0)
+ extra = 0;
+ mMediaNumber = String.valueOf(extra);
+ }
+ else {
+ mTrackName = intent.getStringExtra("track");
+ mArtistName = intent.getStringExtra("artist");
+ mAlbumName = intent.getStringExtra("album");
+ long extra = intent.getLongExtra("id", 0);
+ if (extra < 0)
+ extra = 0;
+ mMediaNumber = String.valueOf(extra);
+ }
+
+ if (mTrackName == null)
+ mTrackName = DEFAULT_METADATA_STRING;
+ if (mArtistName == null)
+ mArtistName = DEFAULT_METADATA_STRING;
+ if (mAlbumName == null)
+ mAlbumName = DEFAULT_METADATA_STRING;
+
+ long extra = intent.getLongExtra("ListSize", 0);
+ if (extra < 0)
+ extra = 0;
+ mMediaCount = String.valueOf(extra);
+
+ extra = intent.getLongExtra("duration", 0);
+ if (extra < 0)
+ extra = 0;
+ mDuration = String.valueOf(extra);
+ extra = intent.getLongExtra("position", 0);
+ if (extra < 0)
+ extra = 0;
+ mPosition = extra;
+ if(DBG) {
+ Log.d(TAG, "Meta changed " + mPlayStatus);
+ Log.d(TAG, "player: " + action);
+ Log.d(TAG, "trackname: "+ mTrackName + " artist: " + mArtistName);
+ Log.d(TAG, "album: "+ mAlbumName);
+ Log.d(TAG, "medianumber: " + mMediaNumber + " mediacount " + mMediaCount);
+ Log.d(TAG, "postion "+ mPosition + " duration "+ mDuration);
+ }
+ for (String path: getConnectedSinksPaths()) {
+ sendMetaData(path);
+ sendEvent(path, EVENT_TRACK_CHANGED, Long.valueOf(mMediaNumber));
+ }
+ }
+ catch (Exception e) {
+ Log.e(TAG, "Error getting metadata from intent", e);
+ }
+ } else if (playstatechanged_intents.contains(action)) {
+ try {
+ if(DBG) {
+ Log.d(TAG, "action: " + action);
+
+ Bundle extras = intent.getExtras();
+
+ if (extras != null) {
+ Set ks = extras.keySet();
+ Iterator iterator = ks.iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ Object value = extras.get(key);
+ if (value != null)
+ Log.d(TAG, key + ": " + value.toString());
+ }
+ }
+ }
+
+ String currentTrackName;
+ // check if there are special extra keys that we will use
+ if (has_special_extra_keys.contains(action)) {
+ if (special_extra_keys.containsKey(action + "_track")) {
+ currentTrackName = intent.getStringExtra(special_extra_keys.get(action + "_track"));
+ }
+ else {
+ currentTrackName = intent.getStringExtra("track");
+ }
+ if (currentTrackName == null)
+ currentTrackName = DEFAULT_METADATA_STRING;
+ }
+ else {
+ currentTrackName = intent.getStringExtra("track");
+ if (currentTrackName == null)
+ currentTrackName = DEFAULT_METADATA_STRING;
+ }
+ if ((!currentTrackName.equals(DEFAULT_METADATA_STRING)) && (!currentTrackName.equals(mTrackName))) {
+ mTrackName = currentTrackName;
+ // check if there are special extra keys that we will use
+ if (has_special_extra_keys.contains(action)) {
+ if (special_extra_keys.containsKey(action + "_artist")) {
+ mArtistName = intent.getStringExtra(special_extra_keys.get(action + "_artist"));
+ }
+ else {
+ mArtistName = intent.getStringExtra("artist");
+ }
+
+ if (special_extra_keys.containsKey(action + "_album")) {
+ mAlbumName = intent.getStringExtra(special_extra_keys.get(action + "_album"));
+ }
+ else {
+ mAlbumName = intent.getStringExtra("album");
+ }
+
+ long extra;
+ if (special_extra_keys.containsKey(action + "_id")) {
+ extra = intent.getLongExtra(special_extra_keys.get(action + "_id"), 0);
+ }
+ else {
+ extra = intent.getLongExtra("id", 0);
+ }
+ if (extra < 0)
+ extra = 0;
+ mMediaNumber = String.valueOf(extra);
+ }
+ else {
+ mArtistName = intent.getStringExtra("artist");
+ mAlbumName = intent.getStringExtra("album");
+ long extra = intent.getLongExtra("id", 0);
+ if (extra < 0)
+ extra = 0;
+ mMediaNumber = String.valueOf(extra);
+ }
+
+ if (mArtistName == null)
+ mArtistName = DEFAULT_METADATA_STRING;
+ if (mAlbumName == null)
+ mAlbumName = DEFAULT_METADATA_STRING;
+
+ long extra = intent.getLongExtra("ListSize", 0);
+ if (extra < 0)
+ extra = 0;
+ mMediaCount = String.valueOf(extra);
+ extra = intent.getLongExtra("duration", 0);
+ if (extra < 0)
+ extra = 0;
+ mDuration = String.valueOf(extra);
+ extra = intent.getLongExtra("position", 0);
+ if (extra < 0)
+ extra = 0;
+ mPosition = extra;
+ for (String path: getConnectedSinksPaths())
+ sendMetaData(path);
+ }
+ boolean playStatusPlaying = intent.getBooleanExtra("playing", false);
+ boolean playStatusPlaystate = intent.getBooleanExtra("playstate", false);
+ boolean playStatusState;
+
+ int state = intent.getIntExtra("state", 2);
+
+ if ((state == 0) || (state == 1))
+ playStatusState = true;
+ else
+ playStatusState = false;
+
+ boolean playStatus = playStatusPlaying || playStatusPlaystate || playStatusState;
+
+ mPosition = intent.getLongExtra("position", 0);
+ if (mPosition < 0)
+ mPosition = 0;
+ mPlayStatus = convertedPlayStatus(playStatus, mPosition);
+ if(DBG) {
+ Log.d(TAG, "PlayState changed " + mPlayStatus);
+ Log.d(TAG, "player: " + action);
+ Log.d(TAG, "trackname: "+ mTrackName + " artist: " + mArtistName);
+ Log.d(TAG, "album: "+ mAlbumName);
+ Log.d(TAG, "medianumber: " + mMediaNumber + " mediacount " + mMediaCount);
+ Log.d(TAG, "postion "+ mPosition + " duration "+ mDuration);
+ }
+
+ for (String path: getConnectedSinksPaths()) {
+ sendEvent(path, EVENT_PLAYSTATUS_CHANGED, (long)mPlayStatus);
+ }
+ }
+ catch (Exception e) {
+ Log.e(TAG, "Error getting playstate from intent", e);
+ }
}
}
};
+ private synchronized int convertedPlayStatus(boolean playing, long position) {
+ if (playing == false && position == 0)
+ return STATUS_STOPPED;
+ if (playing == false)
+ return STATUS_PAUSED;
+ if (playing == true)
+ return STATUS_PLAYING;
+ return STATUS_ERROR;
+ }
+
+ private synchronized void sendMetaData(String path) {
+ if(DBG) {
+ Log.d(TAG, "sendMetaData "+ path);
+ }
+ sendMetaDataNative(path);
+ }
+
+ private synchronized void sendEvent(String path, int eventId, long data) {
+ if(DBG)
+ Log.d(TAG, "sendEvent "+path+ " data "+ data);
+ sendEventNative(path, eventId, data);
+ }
+
+ private synchronized void sendPlayStatus(String path) {
+ if(DBG)
+ Log.d(TAG, "sendPlayStatus"+ path);
+ sendPlayStatusNative(path, (int)Integer.valueOf(mDuration), (int)mPosition, mPlayStatus);
+ }
+
+ private void onGetPlayStatusRequest() {
+ if(DBG)
+ Log.d(TAG, "onGetPlayStatusRequest");
+ for (String path: getConnectedSinksPaths()) {
+ sendPlayStatus(path);
+ }
+ }
+
private boolean isPhoneDocked(BluetoothDevice device) {
// This works only because these broadcast intents are "sticky"
Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
@@ -161,6 +465,59 @@ public BluetoothA2dpService(Context context, BluetoothService bluetoothService)
mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntentFilter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
+
+ Resources res = mContext.getResources();
+ try {
+ /* AVRCP 1.3 Intents */
+ metachanged_intents = Arrays.asList(res.getStringArray(R.array.avrcp_meta_changed_intents));
+ playstatechanged_intents = Arrays.asList(res.getStringArray(R.array.avrcp_playstate_changed_intents));
+
+ for (String intent: metachanged_intents) {
+ mIntentFilter.addAction(intent);
+ }
+
+ for (String intent: playstatechanged_intents) {
+ mIntentFilter.addAction(intent);
+ }
+ }
+ catch (Exception e) {
+ Log.e(TAG, "Error getting AVRCP 1.3 intents from the resource file.");
+ }
+
+ try {
+ /* AVRCP 1.3 special extra keys */
+ has_special_extra_keys = Arrays.asList(res.getStringArray(R.array.avrcp_special_extra_keys));
+
+ special_extra_keys = new HashMap();
+
+ String key_name;
+ int resID;
+
+ List overridable_extra_keys = Arrays.asList(res.getStringArray(R.array.avrcp_overridable_extra_keys));
+
+ for (String intent: has_special_extra_keys) {
+ if(DBG) {
+ Log.d(TAG, "has_special_extra_keys: " + intent);
+ }
+ for (String key: overridable_extra_keys) {
+ key_name = intent + "_" + key;
+ if(DBG) {
+ Log.d(TAG, "key_name: " + key_name);
+ }
+ resID = res.getIdentifier(key_name, "string", mContext.getPackageName());
+ if (resID != 0) {
+ special_extra_keys.put(key_name, res.getString(resID));
+ if(DBG) {
+ Log.d(TAG, key_name + ": " + special_extra_keys.get(key_name));
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ Log.e(TAG, "Error getting AVRCP 1.3 special extra keys from the resource file.");
+ }
+
mContext.registerReceiver(mReceiver, mIntentFilter);
mAudioDevices = new HashMap();
@@ -213,6 +570,10 @@ private synchronized void onBluetoothEnable() {
for (String path: paths) {
String address = mBluetoothService.getAddressFromObjectPath(path);
BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (DBG) {
+ log("RemoteName: " + mBluetoothService.getRemoteName(address));
+ log("RemoteAlias: " + mBluetoothService.getRemoteAlias(address));
+ }
ParcelUuid[] remoteUuids = mBluetoothService.getRemoteUuids(address);
if (remoteUuids != null)
if (BluetoothUuid.containsAnyUuid(remoteUuids,
@@ -222,7 +583,7 @@ private synchronized void onBluetoothEnable() {
}
}
}
- mAudioManager.setParameters(BLUETOOTH_ENABLED+"=true");
+ mAudioManager.setParameters(BLUETOOTH_ENABLED + "=true");
mAudioManager.setParameters("A2dpSuspended=false");
}
@@ -372,7 +733,7 @@ public synchronized boolean disconnectSinkInternal(BluetoothDevice device) {
public synchronized boolean suspendSink(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- if (DBG) log("suspendSink(" + device + "), mTargetA2dpState: "+mTargetA2dpState);
+ if (DBG) log("suspendSink(" + device + "), mTargetA2dpState: "+ mTargetA2dpState);
if (device == null || mAudioDevices == null) {
return false;
}
@@ -389,7 +750,7 @@ public synchronized boolean suspendSink(BluetoothDevice device) {
public synchronized boolean resumeSink(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- if (DBG) log("resumeSink(" + device + "), mTargetA2dpState: "+mTargetA2dpState);
+ if (DBG) log("resumeSink(" + device + "), mTargetA2dpState: "+ mTargetA2dpState);
if (device == null || mAudioDevices == null) {
return false;
}
@@ -410,6 +771,16 @@ public synchronized int getConnectionState(BluetoothDevice device) {
return state;
}
+ public synchronized List getConnectedSinksPaths() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ List btDevices = getConnectedDevices();
+ ArrayList paths = new ArrayList();
+ for(BluetoothDevice device:btDevices) {
+ paths.add(mBluetoothService.getObjectPathFromAddress(device.getAddress()));
+ }
+ return paths;
+ }
+
public synchronized List getConnectedDevices() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
List sinks = getDevicesMatchingConnectionStates(
@@ -486,7 +857,7 @@ private synchronized void onSinkPropertyChanged(String path, String[] propValues
if (name.equals(PROPERTY_STATE)) {
int state = convertBluezSinkStringToState(propValues[1]);
- log("A2DP: onSinkPropertyChanged newState is: " + state + "mPlayingA2dpDevice: " + mPlayingA2dpDevice);
+ log("A2DP: onSinkPropertyChanged newState is: " + state + " mPlayingA2dpDevice: " + mPlayingA2dpDevice);
if (mAudioDevices.get(device) == null) {
// This is for an incoming connection for a device not known to us.
@@ -537,6 +908,13 @@ private void handleSinkStateChange(BluetoothDevice device, int prevState, int st
device),
delay);
}
+ if (prevState == BluetoothA2dp.STATE_CONNECTING &&
+ state == BluetoothA2dp.STATE_CONNECTED) {
+ for (String path: getConnectedSinksPaths()) {
+ sendMetaData(path);
+ sendEvent(path, EVENT_PLAYSTATUS_CHANGED, (long)mPlayStatus);
+ }
+ }
}
private void handleSinkPlayingStateChange(BluetoothDevice device, int state, int prevState) {
@@ -625,6 +1003,7 @@ public void handleMessage(Message msg) {
}
}
+
@Override
protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
@@ -650,4 +1029,8 @@ private static void log(String msg) {
private synchronized native Object []getSinkPropertiesNative(String path);
private synchronized native boolean avrcpVolumeUpNative(String path);
private synchronized native boolean avrcpVolumeDownNative(String path);
+ private synchronized native boolean sendMetaDataNative(String path);
+ private synchronized native boolean sendEventNative(String path, int eventId, long data);
+ private synchronized native boolean sendPlayStatusNative(String path, int duration,
+ int position, int playStatus);
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
old mode 100755
new mode 100644
index 97c0209850c..db599f56382
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -137,7 +137,8 @@ public class BluetoothService extends IBluetooth.Stub {
private static final ParcelUuid[] RFCOMM_UUIDS = {
BluetoothUuid.Handsfree,
BluetoothUuid.HSP,
- BluetoothUuid.ObexObjectPush };
+ BluetoothUuid.ObexObjectPush,
+ BluetoothUuid.MessageNotificationServer };
private final BluetoothAdapterProperties mAdapterProperties;
private final BluetoothDeviceProperties mDeviceProperties;
@@ -165,6 +166,7 @@ private static class ServiceRecordClient {
private static String mDockAddress;
private String mDockPin;
+ private Object mAllowConnectLock = new Object();
private boolean mAllowConnect = true;
private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
@@ -589,6 +591,7 @@ private synchronized void updateSdpRecords() {
if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
uuids.add(BluetoothUuid.HSP_AG);
uuids.add(BluetoothUuid.ObexObjectPush);
+ uuids.add(BluetoothUuid.MessageAccessServer);
}
if (R.getBoolean(com.android.internal.R.bool.config_voice_capable)) {
@@ -2439,7 +2442,11 @@ public boolean disconnectSink(String address) {
BluetoothDeviceProfileState addProfileState(String address, boolean setTrust) {
BluetoothDeviceProfileState state =
new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust);
- mDeviceProfileState.put(address, state);
+ BluetoothDeviceProfileState oldStateMachine = mDeviceProfileState.put(address, state);
+ if (oldStateMachine != null) {
+ oldStateMachine.quit();
+ oldStateMachine = null;
+ }
state.start();
return state;
}
@@ -2477,7 +2484,7 @@ private void initProfileState() {
}
private void autoConnect() {
- synchronized (this) {
+ synchronized (mAllowConnectLock) {
if (!mAllowConnect) {
Log.d(TAG, "Not auto-connecting devices because of temporary BT on state.");
return;
@@ -2500,7 +2507,7 @@ private void autoConnect() {
}
public boolean notifyIncomingConnection(String address, boolean rejected) {
- synchronized (this) {
+ synchronized (mAllowConnectLock) {
if (!mAllowConnect) {
Log.d(TAG, "Not allowing incoming connection because of temporary BT on state.");
return false;
@@ -2527,7 +2534,7 @@ public boolean notifyIncomingConnection(String address, boolean rejected) {
}
/*package*/ boolean notifyIncomingA2dpConnection(String address, boolean rejected) {
- synchronized (this) {
+ synchronized (mAllowConnectLock) {
if (!mAllowConnect) {
Log.d(TAG, "Not allowing a2dp connection because of temporary BT on state.");
return false;
diff --git a/core/java/android/server/package.html b/core/java/android/server/package.html
old mode 100755
new mode 100644
diff --git a/core/java/android/speech/tts/ITextToSpeechCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
old mode 100755
new mode 100644
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index d909362357e..f421f4548c3 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -503,8 +503,11 @@ void updateBlocks(int startLine, int endLine, int newLineCount) {
mNumberOfBlocks = newNumberOfBlocks;
final int deltaLines = newLineCount - (endLine - startLine + 1);
- for (int i = firstBlock + numAddedBlocks; i < mNumberOfBlocks; i++) {
- mBlockEndLines[i] += deltaLines;
+ if (deltaLines != 0) {
+ for (int i = firstBlock + numAddedBlocks; i < mNumberOfBlocks; i++) {
+ mBlockEndLines[i] += deltaLines;
+ mBlockIndices[i] = INVALID_BLOCK_INDEX;
+ }
}
int blockIndex = firstBlock;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 445aac63ae6..bd9310c1b1b 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -83,7 +83,7 @@ static MeasuredText recycle(MeasuredText mt) {
}
void setPos(int pos) {
- mPos = pos;
+ mPos = pos - mTextStart;
}
/**
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index da103111a8f..2e962a00434 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -161,12 +161,17 @@ public class DateUtils
public static final int FORMAT_NO_YEAR = 0x00008;
public static final int FORMAT_SHOW_DATE = 0x00010;
public static final int FORMAT_NO_MONTH_DAY = 0x00020;
+ @Deprecated
public static final int FORMAT_12HOUR = 0x00040;
+ @Deprecated
public static final int FORMAT_24HOUR = 0x00080;
+ @Deprecated
public static final int FORMAT_CAP_AMPM = 0x00100;
public static final int FORMAT_NO_NOON = 0x00200;
+ @Deprecated
public static final int FORMAT_CAP_NOON = 0x00400;
public static final int FORMAT_NO_MIDNIGHT = 0x00800;
+ @Deprecated
public static final int FORMAT_CAP_MIDNIGHT = 0x01000;
/**
* @deprecated Use
@@ -181,19 +186,25 @@ public class DateUtils
public static final int FORMAT_NUMERIC_DATE = 0x20000;
public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
public static final int FORMAT_ABBREV_ALL = 0x80000;
+ @Deprecated
public static final int FORMAT_CAP_NOON_MIDNIGHT = (FORMAT_CAP_NOON | FORMAT_CAP_MIDNIGHT);
+ @Deprecated
public static final int FORMAT_NO_NOON_MIDNIGHT = (FORMAT_NO_NOON | FORMAT_NO_MIDNIGHT);
// Date and time format strings that are constant and don't need to be
// translated.
/**
* This is not actually the preferred 24-hour date format in all locales.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final String HOUR_MINUTE_24 = "%H:%M";
public static final String MONTH_FORMAT = "%B";
/**
* This is not actually a useful month name in all locales.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final String ABBREV_MONTH_FORMAT = "%b";
public static final String NUMERIC_MONTH_FORMAT = "%m";
public static final String MONTH_DAY_FORMAT = "%-d";
@@ -207,6 +218,7 @@ public class DateUtils
// The index is constructed from a bit-wise OR of the boolean values:
// {showTime, showYear, showWeekDay}. For example, if showYear and
// showWeekDay are both true, then the index would be 3.
+ /** @deprecated do not use. */
public static final int sameYearTable[] = {
com.android.internal.R.string.same_year_md1_md2,
com.android.internal.R.string.same_year_wday1_md1_wday2_md2,
@@ -233,6 +245,7 @@ public class DateUtils
// The index is constructed from a bit-wise OR of the boolean values:
// {showTime, showYear, showWeekDay}. For example, if showYear and
// showWeekDay are both true, then the index would be 3.
+ /** @deprecated do not use. */
public static final int sameMonthTable[] = {
com.android.internal.R.string.same_month_md1_md2,
com.android.internal.R.string.same_month_wday1_md1_wday2_md2,
@@ -259,7 +272,9 @@ public class DateUtils
*
* @more
* e.g. "Sunday" or "January"
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_LONG = 10;
/**
@@ -268,7 +283,9 @@ public class DateUtils
*
* @more
* e.g. "Sun" or "Jan"
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_MEDIUM = 20;
/**
@@ -278,14 +295,18 @@ public class DateUtils
*
e.g. "Su" or "Jan"
*
In most languages, the results returned for LENGTH_SHORT will be the same as
* the results returned for {@link #LENGTH_MEDIUM}.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_SHORT = 30;
/**
* Request an even shorter abbreviated version of the name.
* Do not use this. Currently this will always return the same result
* as {@link #LENGTH_SHORT}.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_SHORTER = 40;
/**
@@ -295,7 +316,9 @@ public class DateUtils
*
e.g. "S", "T", "T" or "J"
*
In some languages, the results returned for LENGTH_SHORTEST will be the same as
* the results returned for {@link #LENGTH_SHORT}.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_SHORTEST = 50;
/**
@@ -309,7 +332,9 @@ public class DateUtils
* Undefined lengths will return {@link #LENGTH_MEDIUM}
* but may return something different in the future.
* @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
int[] list;
switch (abbrev) {
@@ -330,7 +355,9 @@ public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
* @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}.
* @throws IndexOutOfBoundsException if the ampm is out of bounds.
* @return Localized version of "AM" or "PM".
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getAMPMString(int ampm) {
Resources r = Resources.getSystem();
return r.getString(sAmPm[ampm - Calendar.AM]);
@@ -345,7 +372,9 @@ public static String getAMPMString(int ampm) {
* Undefined lengths will return {@link #LENGTH_MEDIUM}
* but may return something different in the future.
* @return Localized month of the year.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getMonthString(int month, int abbrev) {
// Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
// This is a shortcut to not spam the translators with too many variations
@@ -378,7 +407,9 @@ public static String getMonthString(int month, int abbrev) {
* but may return something different in the future.
* @return Localized month of the year.
* @hide Pending API council approval
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getStandaloneMonthString(int month, int abbrev) {
// Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
// This is a shortcut to not spam the translators with too many variations
@@ -1618,7 +1649,7 @@ public static CharSequence getRelativeTimeSpanString(Context c, long millis,
String result;
long now = System.currentTimeMillis();
- long span = now - millis;
+ long span = Math.abs(now - millis);
synchronized (DateUtils.class) {
if (sNowTime == null) {
diff --git a/core/java/android/util/IntProperty.java b/core/java/android/util/IntProperty.java
index 459d6b28243..17977ca6351 100644
--- a/core/java/android/util/IntProperty.java
+++ b/core/java/android/util/IntProperty.java
@@ -42,7 +42,7 @@ public IntProperty(String name) {
@Override
final public void set(T object, Integer value) {
- set(object, value.intValue());
+ setValue(object, value.intValue());
}
}
\ No newline at end of file
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 055aee3e844..fa7d005116c 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -224,9 +224,25 @@ public int onPreDraw(Rect dirty) {
}
}
+ @Override
+ void startTileRendering(Rect dirty) {
+ if (dirty != null) {
+ nStartTileRendering(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom);
+ } else {
+ nStartTileRendering(mRenderer, 0, 0, 0, 0);
+ }
+ }
+
+ @Override
+ void endTileRendering() {
+ nEndTileRendering(mRenderer);
+ }
+
private static native int nPrepare(int renderer, boolean opaque);
private static native int nPrepareDirty(int renderer, int left, int top, int right, int bottom,
boolean opaque);
+ private static native void nStartTileRendering(int renderer, int left, int top, int right, int bottom);
+ private static native void nEndTileRendering(int renderer);
@Override
public void onPostDraw() {
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 0114a419f23..4bbdd4e3b53 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -226,17 +226,12 @@ public boolean onSingleTapConfirmed(MotionEvent e) {
*/
private boolean mIsDoubleTapping;
- private float mLastMotionY;
- private float mLastMotionX;
+ private float mLastFocusX;
+ private float mLastFocusY;
+ private float mDownFocusX;
+ private float mDownFocusY;
private boolean mIsLongpressEnabled;
-
- /**
- * True if we are at a target API level of >= Froyo or the developer can
- * explicitly set it. If true, input events with > 1 pointer will be ignored
- * so we can work side by side with multitouch gesture detectors.
- */
- private boolean mIgnoreMultitouch;
/**
* Determines speed during touch scrolling
@@ -349,8 +344,16 @@ public GestureDetector(Context context, OnGestureListener listener) {
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
- this(context, listener, handler, context != null &&
- context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);
+ if (handler != null) {
+ mHandler = new GestureHandler(handler);
+ } else {
+ mHandler = new GestureHandler();
+ }
+ mListener = listener;
+ if (listener instanceof OnDoubleTapListener) {
+ setOnDoubleTapListener((OnDoubleTapListener) listener);
+ }
+ init(context);
}
/**
@@ -362,31 +365,19 @@ public GestureDetector(Context context, OnGestureListener listener, Handler hand
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use
- * @param ignoreMultitouch whether events involving more than one pointer should
- * be ignored.
*
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler,
- boolean ignoreMultitouch) {
- if (handler != null) {
- mHandler = new GestureHandler(handler);
- } else {
- mHandler = new GestureHandler();
- }
- mListener = listener;
- if (listener instanceof OnDoubleTapListener) {
- setOnDoubleTapListener((OnDoubleTapListener) listener);
- }
- init(context, ignoreMultitouch);
+ boolean unused) {
+ this(context, listener, handler);
}
- private void init(Context context, boolean ignoreMultitouch) {
+ private void init(Context context) {
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
mIsLongpressEnabled = true;
- mIgnoreMultitouch = ignoreMultitouch;
// Fallback to support pre-donuts releases
int touchSlop, doubleTapSlop, doubleTapTouchSlop;
@@ -456,34 +447,41 @@ public boolean onTouchEvent(MotionEvent ev) {
}
final int action = ev.getAction();
- final float y = ev.getY();
- final float x = ev.getX();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
+ final boolean pointerUp =
+ (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ final float focusX = sumX / div;
+ final float focusY = sumY / div;
+
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
- if (mIgnoreMultitouch) {
- // Multitouch event - abort.
- cancel();
- }
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ // Cancel long press and taps
+ cancelTaps();
break;
case MotionEvent.ACTION_POINTER_UP:
- // Ending a multitouch gesture and going back to 1 finger
- if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
- int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
- >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
- mLastMotionX = ev.getX(index);
- mLastMotionY = ev.getY(index);
- mVelocityTracker.recycle();
- mVelocityTracker = VelocityTracker.obtain();
- }
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
break;
case MotionEvent.ACTION_DOWN:
@@ -504,8 +502,8 @@ public boolean onTouchEvent(MotionEvent ev) {
}
}
- mLastMotionX = x;
- mLastMotionY = y;
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
@@ -525,22 +523,22 @@ public boolean onTouchEvent(MotionEvent ev) {
break;
case MotionEvent.ACTION_MOVE:
- if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
+ if (mInLongPress) {
break;
}
- final float scrollX = mLastMotionX - x;
- final float scrollY = mLastMotionY - y;
+ final float scrollX = mLastFocusX - focusX;
+ final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
- final int deltaX = (int) (x - mCurrentDownEvent.getX());
- final int deltaY = (int) (y - mCurrentDownEvent.getY());
+ final int deltaX = (int) (focusX - mDownFocusX);
+ final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
mAlwaysInTapRegion = false;
mHandler.removeMessages(TAP);
mHandler.removeMessages(SHOW_PRESS);
@@ -551,8 +549,8 @@ public boolean onTouchEvent(MotionEvent ev) {
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
}
break;
@@ -571,9 +569,10 @@ public boolean onTouchEvent(MotionEvent ev) {
// A fling must travel the minimum tap distance
final VelocityTracker velocityTracker = mVelocityTracker;
+ final int pointerId = ev.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
- final float velocityY = velocityTracker.getYVelocity();
- final float velocityX = velocityTracker.getXVelocity();
+ final float velocityY = velocityTracker.getYVelocity(pointerId);
+ final float velocityX = velocityTracker.getXVelocity(pointerId);
if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
@@ -622,6 +621,18 @@ private void cancel() {
}
}
+ private void cancelTaps() {
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.removeMessages(TAP);
+ mIsDoubleTapping = false;
+ mAlwaysInTapRegion = false;
+ mAlwaysInBiggerTapRegion = false;
+ if (mInLongPress) {
+ mInLongPress = false;
+ }
+ }
+
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
MotionEvent secondDown) {
if (!mAlwaysInBiggerTapRegion) {
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 777552a23b5..a03678ac91d 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -45,6 +45,8 @@ public void setBitmap(Bitmap bitmap) {
* the canvas).
*/
public abstract int onPreDraw(Rect dirty);
+ abstract void startTileRendering(Rect dirty);
+ abstract void endTileRendering();
/**
* Invoked after all drawing operation have been performed.
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index dab48b1400e..dc995611dbe 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -76,10 +76,19 @@ public abstract class HardwareRenderer {
*/
static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
+ /**
+ * System property used to enable or disable tile rendering
+ *
+ * Possible values:
+ * "true", to enable tile rendering
+ * "false", to disable tile rendering
+ */
+ static final String TILE_RENDERING_PROPERTY = "debug.enabletr";
+
/**
* System property used to enable or disable vsync.
* The default value of this property is assumed to be false.
- *
+ *
* Possible values:
* "true", to disable vsync
* "false", to enable vsync
@@ -611,10 +620,16 @@ static abstract class GlRenderer extends HardwareRenderer {
static boolean sDirtyRegions;
static final boolean sDirtyRegionsRequested;
+ static boolean sTileRendering;
static {
String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
+ String trProperty = SystemProperties.get(TILE_RENDERING_PROPERTY, "true");
//noinspection PointlessBooleanExpression,ConstantConditions
- sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
+ //enable dirty regions if tile-rendering enabled or dirty regions property enabled
+ sTileRendering = "true".equalsIgnoreCase(trProperty);
+ sDirtyRegions = RENDER_DIRTY_REGIONS &&
+ ("true".equalsIgnoreCase(dirtyProperty) ||
+ sTileRendering);
sDirtyRegionsRequested = sDirtyRegions;
}
@@ -1054,6 +1069,12 @@ boolean canDraw() {
return mGl != null && mCanvas != null;
}
+ void startTileRendering(Rect dirty) {
+ }
+
+ void endTileRendering() {
+ }
+
int onPreDraw(Rect dirty) {
return DisplayList.STATUS_DONE;
}
@@ -1118,6 +1139,9 @@ boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callba
}
}
+ if (sTileRendering)
+ startTileRendering(dirty);
+
int status = onPreDraw(dirty);
int saveCount = canvas.save();
callbacks.onHardwarePreDraw(canvas);
@@ -1198,6 +1222,8 @@ boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callba
}
onPostDraw();
+ if (sTileRendering)
+ endTileRendering();
attachInfo.mIgnoreDirtyState = false;
@@ -1411,6 +1437,16 @@ void onPostDraw() {
mGlCanvas.onPostDraw();
}
+ @Override
+ void startTileRendering(Rect dirty) {
+ mGlCanvas.startTileRendering(dirty);
+ }
+
+ @Override
+ void endTileRendering() {
+ mGlCanvas.endTileRendering();
+ }
+
@Override
void destroy(boolean full) {
try {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
old mode 100755
new mode 100644
index 3bb9c01d3c3..2e9fdb4f8f0
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -168,19 +168,6 @@ public final class InputDevice implements Parcelable {
/**
* The input source is a stylus pointing device.
- *
- * Note that this bit merely indicates that an input device is capable of obtaining
- * input from a stylus. To determine whether a given touch event was produced
- * by a stylus, examine the tool type returned by {@link MotionEvent#getToolType(int)}
- * for each individual pointer.
- *
- * A single touch event may multiple pointers with different tool types,
- * such as an event that has one pointer with tool type
- * {@link MotionEvent#TOOL_TYPE_FINGER} and another pointer with tool type
- * {@link MotionEvent#TOOL_TYPE_STYLUS}. So it is important to examine
- * the tool type of each pointer, regardless of the source reported
- * by {@link MotionEvent#getSource()}.
- *
*
* @see #SOURCE_CLASS_POINTER
*/
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
old mode 100755
new mode 100644
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
old mode 100755
new mode 100644
index 2865e8b27bf..8c43c2261b1
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -624,12 +624,19 @@ public class KeyEvent extends InputEvent implements Parcelable {
* Launches the global assist activity. Not delivered to applications. */
public static final int KEYCODE_ASSIST = 219;
+ /** @hide */
public static final int KEYCODE_TOGGLE_WIFI = 220;
+ /** @hide */
public static final int KEYCODE_TOGGLE_BT = 221;
+ /** @hide */
public static final int KEYCODE_TOGGLE_TOUCHPAD = 222;
+ /** @hide */
public static final int KEYCODE_BRIGHTNESS_DOWN = 223;
+ /** @hide */
public static final int KEYCODE_BRIGHTNESS_UP = 224;
+ /** @hide */
public static final int KEYCODE_BRIGHTNESS_AUTO = 225;
+ /** @hide */
public static final int KEYCODE_SCREENSHOT = 226;
private static final int LAST_KEYCODE = KEYCODE_SCREENSHOT;
diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java
old mode 100755
new mode 100644
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 73f94bc2f53..bcb8800621e 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -17,14 +17,13 @@
package android.view;
import android.content.Context;
-import android.util.DisplayMetrics;
import android.util.FloatMath;
-import android.util.Log;
/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
- * callback will notify users when a particular gesture event has occurred.
+ * Detects scaling transformation gestures using the supplied {@link MotionEvent}s.
+ * The {@link OnScaleGestureListener} callback will notify users when a particular
+ * gesture event has occurred.
+ *
* This class should only be used with {@link MotionEvent}s reported via touch.
*
* To use this class:
@@ -87,8 +86,8 @@ public interface OnScaleGestureListener {
* pointers going up.
*
* Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
- * and {@link ScaleGestureDetector#getFocusY()} will return the location
- * of the pointer remaining on the screen.
+ * and {@link ScaleGestureDetector#getFocusY()} will return focal point
+ * of the pointers remaining on the screen.
*
* @param detector The detector reporting the event - use this to
* retrieve extended info about event state.
@@ -121,43 +120,23 @@ public void onScaleEnd(ScaleGestureDetector detector) {
}
}
- /**
- * This value is the threshold ratio between our previous combined pressure
- * and the current combined pressure. We will only fire an onScale event if
- * the computed ratio between the current and previous event pressures is
- * greater than this value. When pressure decreases rapidly between events
- * the position values can often be imprecise, as it usually indicates
- * that the user is in the process of lifting a pointer off of the device.
- * Its value was tuned experimentally.
- */
- private static final float PRESSURE_THRESHOLD = 0.67f;
-
private final Context mContext;
private final OnScaleGestureListener mListener;
- private boolean mGestureInProgress;
-
- private MotionEvent mPrevEvent;
- private MotionEvent mCurrEvent;
private float mFocusX;
private float mFocusY;
- private float mPrevFingerDiffX;
- private float mPrevFingerDiffY;
- private float mCurrFingerDiffX;
- private float mCurrFingerDiffY;
- private float mCurrLen;
- private float mPrevLen;
- private float mScaleFactor;
- private float mCurrPressure;
- private float mPrevPressure;
- private long mTimeDelta;
-
- private boolean mInvalidGesture;
-
- // Pointer IDs currently responsible for the two fingers controlling the gesture
- private int mActiveId0;
- private int mActiveId1;
- private boolean mActive0MostRecent;
+
+ private float mCurrSpan;
+ private float mPrevSpan;
+ private float mInitialSpan;
+ private float mCurrSpanX;
+ private float mCurrSpanY;
+ private float mPrevSpanX;
+ private float mPrevSpanY;
+ private long mCurrTime;
+ private long mPrevTime;
+ private boolean mInProgress;
+ private int mSpanSlop;
/**
* Consistency verifier for debugging purposes.
@@ -169,8 +148,21 @@ public void onScaleEnd(ScaleGestureDetector detector) {
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
mContext = context;
mListener = listener;
+ mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
}
+ /**
+ * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener}
+ * when appropriate.
+ *
+ * Applications should pass a complete and consistent event stream to this method.
+ * A complete and consistent event stream involves all MotionEvents from the initial
+ * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.
+ *
+ * @param event The event to process
+ * @return true if the event was processed and the detector wants to receive the
+ * rest of the MotionEvents in this event stream.
+ */
public boolean onTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
@@ -178,265 +170,115 @@ public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- reset(); // Start fresh
- }
-
- boolean handled = true;
- if (mInvalidGesture) {
- handled = false;
- } else if (!mGestureInProgress) {
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- mActiveId0 = event.getPointerId(0);
- mActive0MostRecent = true;
- }
- break;
-
- case MotionEvent.ACTION_UP:
- reset();
- break;
-
- case MotionEvent.ACTION_POINTER_DOWN: {
- // We have a new multi-finger gesture
- if (mPrevEvent != null) mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- mTimeDelta = 0;
-
- int index1 = event.getActionIndex();
- int index0 = event.findPointerIndex(mActiveId0);
- mActiveId1 = event.getPointerId(index1);
- if (index0 < 0 || index0 == index1) {
- // Probably someone sending us a broken event stream.
- index0 = findNewActiveIndex(event, mActiveId1, -1);
- mActiveId0 = event.getPointerId(index0);
- }
- mActive0MostRecent = false;
-
- setContext(event);
-
- mGestureInProgress = mListener.onScaleBegin(this);
- break;
- }
+ final boolean streamComplete = action == MotionEvent.ACTION_UP ||
+ action == MotionEvent.ACTION_CANCEL;
+ if (action == MotionEvent.ACTION_DOWN || streamComplete) {
+ // Reset any scale in progress with the listener.
+ // If it's an ACTION_DOWN we're beginning a new event stream.
+ // This means the app probably didn't give us all the events. Shame on it.
+ if (mInProgress) {
+ mListener.onScaleEnd(this);
+ mInProgress = false;
+ mInitialSpan = 0;
}
- } else {
- // Transform gesture in progress - attempt to handle it
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN: {
- // End the old gesture and begin a new one with the most recent two fingers.
- mListener.onScaleEnd(this);
- final int oldActive0 = mActiveId0;
- final int oldActive1 = mActiveId1;
- reset();
-
- mPrevEvent = MotionEvent.obtain(event);
- mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1;
- mActiveId1 = event.getPointerId(event.getActionIndex());
- mActive0MostRecent = false;
-
- int index0 = event.findPointerIndex(mActiveId0);
- if (index0 < 0 || mActiveId0 == mActiveId1) {
- // Probably someone sending us a broken event stream.
- Log.e(TAG, "Got " + MotionEvent.actionToString(action) +
- " with bad state while a gesture was in progress. " +
- "Did you forget to pass an event to " +
- "ScaleGestureDetector#onTouchEvent?");
- index0 = findNewActiveIndex(event, mActiveId1, -1);
- mActiveId0 = event.getPointerId(index0);
- }
-
- setContext(event);
-
- mGestureInProgress = mListener.onScaleBegin(this);
- }
- break;
-
- case MotionEvent.ACTION_POINTER_UP: {
- final int pointerCount = event.getPointerCount();
- final int actionIndex = event.getActionIndex();
- final int actionId = event.getPointerId(actionIndex);
-
- boolean gestureEnded = false;
- if (pointerCount > 2) {
- if (actionId == mActiveId0) {
- final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex);
- if (newIndex >= 0) {
- mListener.onScaleEnd(this);
- mActiveId0 = event.getPointerId(newIndex);
- mActive0MostRecent = true;
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- mGestureInProgress = mListener.onScaleBegin(this);
- } else {
- gestureEnded = true;
- }
- } else if (actionId == mActiveId1) {
- final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex);
- if (newIndex >= 0) {
- mListener.onScaleEnd(this);
- mActiveId1 = event.getPointerId(newIndex);
- mActive0MostRecent = false;
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- mGestureInProgress = mListener.onScaleBegin(this);
- } else {
- gestureEnded = true;
- }
- }
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- } else {
- gestureEnded = true;
- }
-
- if (gestureEnded) {
- // Gesture ended
- setContext(event);
-
- // Set focus point to the remaining finger
- final int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0;
- final int index = event.findPointerIndex(activeId);
- mFocusX = event.getX(index);
- mFocusY = event.getY(index);
-
- mListener.onScaleEnd(this);
- reset();
- mActiveId0 = activeId;
- mActive0MostRecent = true;
- }
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- mListener.onScaleEnd(this);
- reset();
- break;
-
- case MotionEvent.ACTION_UP:
- reset();
- break;
-
- case MotionEvent.ACTION_MOVE: {
- setContext(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit - this can help filter shaky data as a
- // finger is lifted.
- if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
- final boolean updatePrevious = mListener.onScale(this);
-
- if (updatePrevious) {
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- }
- }
- }
- break;
+
+ if (streamComplete) {
+ return true;
}
}
- if (!handled && mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? event.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = event.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += event.getX(i);
+ sumY += event.getY(i);
}
- return handled;
- }
-
- private int findNewActiveIndex(MotionEvent ev, int otherActiveId, int removedPointerIndex) {
- final int pointerCount = ev.getPointerCount();
-
- // It's ok if this isn't found and returns -1, it simply won't match.
- final int otherActiveIndex = ev.findPointerIndex(otherActiveId);
-
- // Pick a new id and update tracking state.
- for (int i = 0; i < pointerCount; i++) {
- if (i != removedPointerIndex && i != otherActiveIndex) {
- return i;
- }
+ final int div = pointerUp ? count - 1 : count;
+ final float focusX = sumX / div;
+ final float focusY = sumY / div;
+
+ // Determine average deviation from focal point
+ float devSumX = 0, devSumY = 0;
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ devSumX += Math.abs(event.getX(i) - focusX);
+ devSumY += Math.abs(event.getY(i) - focusY);
}
- return -1;
- }
-
- private void setContext(MotionEvent curr) {
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
+ final float devX = devSumX / div;
+ final float devY = devSumY / div;
+
+ // Span is the average distance between touch points through the focal point;
+ // i.e. the diameter of the circle with a radius of the average deviation from
+ // the focal point.
+ final float spanX = devX * 2;
+ final float spanY = devY * 2;
+ final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
+
+ // Dispatch begin/end events as needed.
+ // If the configuration changes, notify the app to reset its current state by beginning
+ // a fresh scale event stream.
+ final boolean wasInProgress = mInProgress;
+ mFocusX = focusX;
+ mFocusY = focusY;
+ if (mInProgress && (span == 0 || configChanged)) {
+ mListener.onScaleEnd(this);
+ mInProgress = false;
+ mInitialSpan = span;
+ }
+ if (configChanged) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mInitialSpan = mPrevSpan = mCurrSpan = span;
+ }
+ if (!mInProgress && span != 0 &&
+ (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mPrevSpan = mCurrSpan = span;
+ mInProgress = mListener.onScaleBegin(this);
}
- mCurrEvent = MotionEvent.obtain(curr);
-
- mCurrLen = -1;
- mPrevLen = -1;
- mScaleFactor = -1;
- final MotionEvent prev = mPrevEvent;
+ // Handle motion; focal point and span/scale factor are changing.
+ if (action == MotionEvent.ACTION_MOVE) {
+ mCurrSpanX = spanX;
+ mCurrSpanY = spanY;
+ mCurrSpan = span;
- final int prevIndex0 = prev.findPointerIndex(mActiveId0);
- final int prevIndex1 = prev.findPointerIndex(mActiveId1);
- final int currIndex0 = curr.findPointerIndex(mActiveId0);
- final int currIndex1 = curr.findPointerIndex(mActiveId1);
+ boolean updatePrev = true;
+ if (mInProgress) {
+ updatePrev = mListener.onScale(this);
+ }
- if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) {
- mInvalidGesture = true;
- Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
- if (mGestureInProgress) {
- mListener.onScaleEnd(this);
+ if (updatePrev) {
+ mPrevSpanX = mCurrSpanX;
+ mPrevSpanY = mCurrSpanY;
+ mPrevSpan = mCurrSpan;
}
- return;
}
- final float px0 = prev.getX(prevIndex0);
- final float py0 = prev.getY(prevIndex0);
- final float px1 = prev.getX(prevIndex1);
- final float py1 = prev.getY(prevIndex1);
- final float cx0 = curr.getX(currIndex0);
- final float cy0 = curr.getY(currIndex0);
- final float cx1 = curr.getX(currIndex1);
- final float cy1 = curr.getY(currIndex1);
-
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- mPrevFingerDiffX = pvx;
- mPrevFingerDiffY = pvy;
- mCurrFingerDiffX = cvx;
- mCurrFingerDiffY = cvy;
-
- mFocusX = cx0 + cvx * 0.5f;
- mFocusY = cy0 + cvy * 0.5f;
- mTimeDelta = curr.getEventTime() - prev.getEventTime();
- mCurrPressure = curr.getPressure(currIndex0) + curr.getPressure(currIndex1);
- mPrevPressure = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1);
- }
-
- private void reset() {
- if (mPrevEvent != null) {
- mPrevEvent.recycle();
- mPrevEvent = null;
- }
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
- mCurrEvent = null;
- }
- mGestureInProgress = false;
- mActiveId0 = -1;
- mActiveId1 = -1;
- mInvalidGesture = false;
+ return true;
}
/**
- * Returns {@code true} if a two-finger scale gesture is in progress.
- * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+ * Returns {@code true} if a scale gesture is in progress.
*/
public boolean isInProgress() {
- return mGestureInProgress;
+ return mInProgress;
}
/**
* Get the X coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is directly between
- * the two pointers forming the gesture.
- * If a gesture is ending, the focal point is the location of the
- * remaining pointer on the screen.
+ * If a gesture is in progress, the focal point is between
+ * each of the pointers forming the gesture.
+ *
* If {@link #isInProgress()} would return false, the result of this
* function is undefined.
*
@@ -448,10 +290,9 @@ public float getFocusX() {
/**
* Get the Y coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is directly between
- * the two pointers forming the gesture.
- * If a gesture is ending, the focal point is the location of the
- * remaining pointer on the screen.
+ * If a gesture is in progress, the focal point is between
+ * each of the pointers forming the gesture.
+ *
* If {@link #isInProgress()} would return false, the result of this
* function is undefined.
*
@@ -462,73 +303,63 @@ public float getFocusY() {
}
/**
- * Return the current distance between the two pointers forming the
- * gesture in progress.
+ * Return the average distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpan() {
- if (mCurrLen == -1) {
- final float cvx = mCurrFingerDiffX;
- final float cvy = mCurrFingerDiffY;
- mCurrLen = FloatMath.sqrt(cvx*cvx + cvy*cvy);
- }
- return mCurrLen;
+ return mCurrSpan;
}
/**
- * Return the current x distance between the two pointers forming the
- * gesture in progress.
+ * Return the average X distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpanX() {
- return mCurrFingerDiffX;
+ return mCurrSpanX;
}
/**
- * Return the current y distance between the two pointers forming the
- * gesture in progress.
+ * Return the average Y distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpanY() {
- return mCurrFingerDiffY;
+ return mCurrSpanY;
}
/**
- * Return the previous distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpan() {
- if (mPrevLen == -1) {
- final float pvx = mPrevFingerDiffX;
- final float pvy = mPrevFingerDiffY;
- mPrevLen = FloatMath.sqrt(pvx*pvx + pvy*pvy);
- }
- return mPrevLen;
+ return mPrevSpan;
}
/**
- * Return the previous x distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average X distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpanX() {
- return mPrevFingerDiffX;
+ return mPrevSpanX;
}
/**
- * Return the previous y distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average Y distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpanY() {
- return mPrevFingerDiffY;
+ return mPrevSpanY;
}
/**
@@ -539,10 +370,7 @@ public float getPreviousSpanY() {
* @return The current scaling factor.
*/
public float getScaleFactor() {
- if (mScaleFactor == -1) {
- mScaleFactor = getCurrentSpan() / getPreviousSpan();
- }
- return mScaleFactor;
+ return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
}
/**
@@ -552,7 +380,7 @@ public float getScaleFactor() {
* @return Time difference since the last scaling event in milliseconds.
*/
public long getTimeDelta() {
- return mTimeDelta;
+ return mCurrTime - mPrevTime;
}
/**
@@ -561,6 +389,6 @@ public long getTimeDelta() {
* @return Current event time in milliseconds.
*/
public long getEventTime() {
- return mCurrEvent.getEventTime();
+ return mCurrTime;
}
}
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index 2a16725ad32..015a78e0daa 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -155,7 +155,7 @@ public interface Callback2 extends Callback {
/**
* Make the surface a fixed size. It will never change from this size.
- * When working with a {link SurfaceView}, this must be called from the
+ * When working with a {@link SurfaceView}, this must be called from the
* same thread running the SurfaceView's window.
*
* @param width The surface's width.
@@ -167,14 +167,14 @@ public interface Callback2 extends Callback {
* Allow the surface to resized based on layout of its container (this is
* the default). When this is enabled, you should monitor
* {@link Callback#surfaceChanged} for changes to the size of the surface.
- * When working with a {link SurfaceView}, this must be called from the
+ * When working with a {@link SurfaceView}, this must be called from the
* same thread running the SurfaceView's window.
*/
public void setSizeFromLayout();
/**
* Set the desired PixelFormat of the surface. The default is OPAQUE.
- * When working with a {link SurfaceView}, this must be called from the
+ * When working with a {@link SurfaceView}, this must be called from the
* same thread running the SurfaceView's window.
*
* @param format A constant from PixelFormat.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fd302dc5b35..ed4c75cdb45 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -531,7 +531,7 @@ private void updateWindow(boolean force, boolean redrawNeeded) {
mSurface.transferFrom(mNewSurface);
- if (visible) {
+ if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b1caa2f4519..f0ca302f421 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2362,7 +2362,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* with a stable layout. (Note that you should avoid using
* {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} by itself.)
*
- * If you have set the window flag {@ WindowManager.LayoutParams#FLAG_FULLSCREEN}
+ * If you have set the window flag {@link WindowManager.LayoutParams#FLAG_FULLSCREEN}
* to hide the status bar (instead of using {@link #SYSTEM_UI_FLAG_FULLSCREEN}),
* then a hidden status bar will be considered a "stable" state for purposes
* here. This allows your UI to continually hide the status bar, while still
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 823befb22f7..d12c47b39ee 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -240,6 +240,8 @@ public class ViewConfiguration {
private boolean sHasPermanentMenuKey;
private boolean sHasPermanentMenuKeySet;
+ private Context mContext;
+
static final SparseArray sConfigurations =
new SparseArray(2);
@@ -287,6 +289,8 @@ private ViewConfiguration(Context context) {
sizeAndDensity = density;
}
+ mContext = context;
+
mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f);
mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f);
mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);
@@ -692,7 +696,18 @@ public static float getScrollFriction() {
* @return true if a permanent menu key is present, false otherwise.
*/
public boolean hasPermanentMenuKey() {
- return sHasPermanentMenuKey;
+ // The action overflow button within app UI can
+ // be controlled with a system setting
+ int showOverflowButton = Settings.System.getInt(
+ mContext.getContentResolver(),
+ Settings.System.UI_FORCE_OVERFLOW_BUTTON, 0);
+ if (showOverflowButton == 1) {
+ // Force overflow button on by reporting that
+ // the device has no permanent menu key
+ return false;
+ } else {
+ return sHasPermanentMenuKey;
+ }
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 044627c32fc..0c83a738e7e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4100,21 +4100,23 @@ public ViewParent invalidateChildInParent(final int[] location, final Rect dirty
final int left = mLeft;
final int top = mTop;
- if ((mGroupFlags & FLAG_CLIP_CHILDREN) != FLAG_CLIP_CHILDREN ||
- dirty.intersect(0, 0, mRight - left, mBottom - top) ||
- (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
- mPrivateFlags &= ~DRAWING_CACHE_VALID;
-
- location[CHILD_LEFT_INDEX] = left;
- location[CHILD_TOP_INDEX] = top;
-
- if (mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= INVALIDATED;
- mLocalDirtyRect.union(dirty);
+ if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
+ if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
+ dirty.setEmpty();
}
+ }
+ mPrivateFlags &= ~DRAWING_CACHE_VALID;
+
+ location[CHILD_LEFT_INDEX] = left;
+ location[CHILD_TOP_INDEX] = top;
- return mParent;
+ if (mLayerType != LAYER_TYPE_NONE) {
+ mPrivateFlags |= INVALIDATED;
+ mLocalDirtyRect.union(dirty);
}
+
+ return mParent;
+
} else {
mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
@@ -5204,7 +5206,7 @@ public boolean addStatesFromChildren() {
}
/**
- * If {link #addStatesFromChildren} is true, refreshes this group's
+ * If {@link #addStatesFromChildren} is true, refreshes this group's
* drawable state (to include the states from its children).
*/
public void childDrawableStateChanged(View child) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6959b84f87d..ad850daa6c3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -870,6 +870,8 @@ public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
if (dirty == null) {
invalidate();
return null;
+ } else if (dirty.isEmpty()) {
+ return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java
index 69a26c25490..a5dc3ae12bd 100644
--- a/core/java/android/view/ViewStub.java
+++ b/core/java/android/view/ViewStub.java
@@ -212,7 +212,8 @@ protected void dispatchDraw(Canvas canvas) {
/**
* When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
* {@link #inflate()} is invoked and this StubbedView is replaced in its parent
- * by the inflated layout resource.
+ * by the inflated layout resource. After that calls to this function are passed
+ * through to the inflated view.
*
* @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
*
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index cf9bcdd8616..b2ee41f4689 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -21,11 +21,13 @@
import android.app.Dialog;
import android.content.DialogInterface.OnDismissListener;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.AudioService;
import android.media.AudioSystem;
@@ -35,6 +37,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
+import android.provider.Settings;
import android.util.Log;
import android.view.WindowManager.LayoutParams;
import android.widget.ImageView;
@@ -102,7 +105,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
protected AudioService mAudioService;
private boolean mRingIsSilent;
private boolean mShowCombinedVolumes;
- private boolean mVoiceCapable;
/** Dialog containing all the sliders */
private final Dialog mDialog;
@@ -121,7 +123,34 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
/** Currently active stream that shows up at the top of the list of sliders */
private int mActiveStreamType = -1;
/** All the slider controls mapped by stream type */
- private HashMap mStreamControls;
+ private HashMap mStreamControls;
+
+ /** Used by the observer */
+ private Handler mHandler;
+
+ private boolean mRingerAndNotificationStreamsLinked = true;
+
+ /** Watch over the toggle in order to update when user changes preference */
+ class SettingsObserver extends ContentObserver {
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.ENABLE_VOLUME_OPTIONS), false, this);
+ resolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.VOLUME_LINK_NOTIFICATION), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateSettings();
+ }
+
+ }
private enum StreamResources {
BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
@@ -136,15 +165,10 @@ private enum StreamResources {
false),
VoiceStream(AudioManager.STREAM_VOICE_CALL,
R.string.volume_icon_description_incall,
- R.drawable.ic_audio_phone,
- R.drawable.ic_audio_phone,
- false),
- AlarmStream(AudioManager.STREAM_ALARM,
- R.string.volume_alarm,
- R.drawable.ic_audio_alarm,
- R.drawable.ic_audio_alarm_mute,
- false),
- MediaStream(AudioManager.STREAM_MUSIC,
+ R.drawable.ic_audio_phone, R.drawable.ic_audio_phone, false), AlarmStream(
+ AudioManager.STREAM_ALARM, R.string.volume_alarm,
+ R.drawable.ic_audio_alarm, R.drawable.ic_audio_alarm_mute, true), MediaStream(
+ AudioManager.STREAM_MUSIC,
R.string.volume_icon_description_media,
R.drawable.ic_audio_vol,
R.drawable.ic_audio_vol_mute,
@@ -213,16 +237,6 @@ public VolumePanel(final Context context, AudioService volumeService) {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mAudioService = volumeService;
- // For now, only show master volume if master volume is supported
- boolean useMasterVolume = context.getResources().getBoolean(
- com.android.internal.R.bool.config_useMasterVolume);
- if (useMasterVolume) {
- for (int i = 0; i < STREAMS.length; i++) {
- StreamResources streamRes = STREAMS[i];
- streamRes.show = (streamRes.streamType == STREAM_MASTER);
- }
- }
-
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = mView = inflater.inflate(R.layout.volume_adjust, null);
@@ -272,17 +286,39 @@ public void onDismiss(DialogInterface dialog) {
mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
- mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
- mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
- // If we don't want to show multiple volumes, hide the settings button and divider
- if (!mShowCombinedVolumes) {
+ mShowCombinedVolumes = Settings.System.getInt(
+ mContext.getContentResolver(),
+ Settings.System.ENABLE_VOLUME_OPTIONS, 0) == 1
+ || !context.getResources().getBoolean(R.bool.config_voice_capable);
+ toggleMore(mShowCombinedVolumes);
+
+ mHandler = new Handler();
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.observe();
+ listenToRingerMode();
+ }
+
+ /** Used by the observer to update the most recent settings */
+ public void updateSettings() {
+ ContentResolver resolver = mContext.getContentResolver();
+ mShowCombinedVolumes = Settings.System.getInt(
+ resolver,
+ Settings.System.ENABLE_VOLUME_OPTIONS, 0) == 1;
+ mRingerAndNotificationStreamsLinked = Settings.System.getInt(
+ resolver,
+ Settings.System.VOLUME_LINK_NOTIFICATION, 1) == 1;
+ toggleMore(mShowCombinedVolumes);
+ }
+
+ private void toggleMore(boolean toggle) {
+ // If we don't want to show multiple volumes, hide the settings button
+ // and divider
+ if (!toggle) {
mMoreButton.setVisibility(View.GONE);
mDivider.setVisibility(View.GONE);
} else {
mMoreButton.setOnClickListener(this);
}
-
- listenToRingerMode();
}
private void listenToRingerMode() {
@@ -349,9 +385,6 @@ private void createSliders() {
for (int i = 0; i < STREAMS.length; i++) {
StreamResources streamRes = STREAMS[i];
int streamType = streamRes.streamType;
- if (mVoiceCapable && streamRes == StreamResources.NotificationStream) {
- streamRes = StreamResources.RingerStream;
- }
StreamControl sc = new StreamControl();
sc.streamType = streamType;
sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
@@ -681,7 +714,11 @@ protected void onShowVolumeChanged(int streamType, int flags) {
}
protected void onPlaySound(int streamType, int flags) {
-
+ // If preference is no sound - just exit here
+ if (Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.VOLUME_ADJUST_SOUNDS_ENABLED, 1) == 0) {
+ return;
+ }
if (hasMessages(MSG_STOP_SOUNDS)) {
removeMessages(MSG_STOP_SOUNDS);
// Force stop right now
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index b0e90db0011..e08f27299b6 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -751,6 +751,9 @@ public void clearFlags(int flags) {
* @param mask Which of the window flag bits to modify.
*/
public void setFlags(int flags, int mask) {
+ if ((flags & mask & WindowManager.LayoutParams.PREVENT_POWER_KEY) != 0){
+ mContext.enforceCallingOrSelfPermission("android.permission.PREVENT_POWER_KEY", "No permission to prevent power key");
+ }
final WindowManager.LayoutParams attrs = getAttributes();
attrs.flags = (attrs.flags&~mask) | (flags&mask);
if ((mask&WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0) {
@@ -790,6 +793,9 @@ public void setDimAmount(float amount) {
* current values.
*/
public void setAttributes(WindowManager.LayoutParams a) {
+ if ((a.flags & WindowManager.LayoutParams.PREVENT_POWER_KEY) != 0){
+ mContext.enforceCallingOrSelfPermission("android.permission.PREVENT_POWER_KEY", "No permission to prevent power key");
+ }
mWindowAttributes.copyFrom(a);
if (mCallback != null) {
mCallback.onWindowAttributesChanged(mWindowAttributes);
diff --git a/core/java/android/view/WindowManager.aidl b/core/java/android/view/WindowManager.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d94275bcd61..0f2854d950d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -704,6 +704,10 @@ public static class LayoutParams extends ViewGroup.LayoutParams
* {@hide} */
public static final int FLAG_SYSTEM_ERROR = 0x40000000;
+ /** Window flag: Overrides default power key behavior
+ * {@hide} */
+ public static final int PREVENT_POWER_KEY = 0x80000000;
+
/**
* Various behavioral options/flags. Default is none.
*
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 365f97fc11b..661ea3badc6 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1011,6 +1011,11 @@ interface OnKeyguardExitResult {
*/
public void systemBooted();
+ /**
+ * name of package being worked on durring boot time message.
+ */
+ public void setPackageName(String pkgName);
+
/**
* Show boot time message to the user.
*/
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
old mode 100755
new mode 100644
diff --git a/core/java/android/view/animation/package.html b/core/java/android/view/animation/package.html
old mode 100755
new mode 100644
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 161e8fb3e45..81b36db50b2 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -91,11 +91,11 @@ public static TextServicesManager getInstance() {
/**
* Get a spell checker session for the specified spell checker
- * @param locale the locale for the spell checker. If {@param locale} is null and
+ * @param locale the locale for the spell checker. If {@code locale} is null and
* referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
- * returned. If {@param locale} is not null and referToSpellCheckerLanguageSettings is true,
- * the locale specified in Settings will be returned only when it is same as {@param locale}.
- * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@param locale} is
+ * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
+ * the locale specified in Settings will be returned only when it is same as {@code locale}.
+ * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is
* only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
* selected.
* @param listener a spell checker session lister for getting results from a spell checker.
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 51089909355..3e28baa78d0 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2011, 2012 Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +33,7 @@
import android.net.http.SslError;
import android.os.Handler;
import android.os.Message;
+import android.os.SystemProperties;
import android.util.Log;
import android.util.TypedValue;
import android.view.Surface;
@@ -212,11 +214,13 @@ public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy,
// set WebCore native cache size
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
- if (am.getMemoryClass() > 16) {
- sJavaBridge.setCacheSize(8 * 1024 * 1024);
- } else {
- sJavaBridge.setCacheSize(4 * 1024 * 1024);
+ int defCacheSize = am.getMemoryClass() > 16 ?
+ 8 * 1024 * 1024 : 4 * 1024 * 1024;
+ int cacheSize = SystemProperties.getInt("net.webkit.cache.size", defCacheSize);
+ if ((cacheSize < 0) || (cacheSize > (100 * 1024 * 1024))) {
+ cacheSize = defCacheSize;
}
+ sJavaBridge.setCacheSize(cacheSize);
// initialize CacheManager
CacheManager.init(appContext);
// create CookieSyncManager with current Context
diff --git a/core/java/android/webkit/DeviceMotionService.java b/core/java/android/webkit/DeviceMotionService.java
old mode 100755
new mode 100644
diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java
old mode 100755
new mode 100644
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
old mode 100755
new mode 100644
diff --git a/core/java/android/webkit/GeolocationPermissionsClassic.java b/core/java/android/webkit/GeolocationPermissionsClassic.java
old mode 100755
new mode 100644
diff --git a/core/java/android/webkit/GeolocationService.java b/core/java/android/webkit/GeolocationService.java
old mode 100755
new mode 100644
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index fc5df2d5f59..426c6f62a7f 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010 The Android Open Source Project
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,10 +71,15 @@ class HTML5Audio extends Handler
private boolean mLoopEnabled = false;
private boolean mProcessingOnEnd = false;
private Context mContext;
+ // The handler for WebCore thread messages;
+ private Handler mWebCoreHandler;
// Timer thread -> UI thread
private static final int TIMEUPDATE = 100;
+ // AudioManager callback thread -> Webcore thread
+ private static final int AUDIOFOCUS_CHANGED = 200;
+
private static final String COOKIE = "Cookie";
private static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
@@ -188,6 +194,7 @@ public void onSeekComplete(MediaPlayer mp) {
public HTML5Audio(WebViewCore webViewCore, int nativePtr) {
// Save the native ptr
mNativePointer = nativePtr;
+ createWebCoreHandler();
resetMediaPlayer();
mContext = webViewCore.getContext();
mIsPrivateBrowsingEnabledGetter = new IsPrivateBrowsingEnabledGetter(
@@ -240,8 +247,7 @@ private void setDataSource(String url) {
}
}
- @Override
- public void onAudioFocusChange(int focusChange) {
+ private void handleAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
@@ -254,10 +260,10 @@ public void onAudioFocusChange(int focusChange) {
break;
case AudioManager.AUDIOFOCUS_LOSS:
- // Lost focus for an unbounded amount of time: stop playback.
+ // Lost focus for an unbounded amount of time: pause playback.
if (mState != ERROR && mMediaPlayer.isPlaying()) {
- mMediaPlayer.stop();
- mState = STOPPED;
+ pause();
+ nativeOnPaused(mNativePointer);
}
break;
@@ -270,6 +276,25 @@ public void onAudioFocusChange(int focusChange) {
}
}
+ private void createWebCoreHandler() {
+ mWebCoreHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AUDIOFOCUS_CHANGED:
+ handleAudioFocusChange(msg.arg1);
+ break;
+ }
+ }
+ };
+ }
+
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Message msg = Message.obtain(mWebCoreHandler, AUDIOFOCUS_CHANGED);
+ msg.arg1 = focusChange;
+ mWebCoreHandler.sendMessage(msg);
+ }
private void play() {
if (mState == COMPLETE && mLoopEnabled == true) {
@@ -316,6 +341,12 @@ private void seek(int msec) {
}
}
+ private void setVolume(float volume) {
+ if (mState >= PREPARED) {
+ mMediaPlayer.setVolume(volume, volume);
+ }
+ }
+
/**
* Called only over JNI when WebKit is happy to
* destroy the media player.
@@ -337,6 +368,7 @@ private float getMaxTimeSeekable() {
private native void nativeOnBuffering(int percent, int nativePointer);
private native void nativeOnEnded(int nativePointer);
+ private native void nativeOnPaused(int nativePointer);
private native void nativeOnRequestPlay(int nativePointer);
private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
private native void nativeOnTimeupdate(int position, int nativePointer);
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
deleted file mode 100644
index 33eaad6bde4..00000000000
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ /dev/null
@@ -1,391 +0,0 @@
-
-package android.webkit;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.media.Metadata;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.MediaController;
-import android.widget.MediaController.MediaPlayerControl;
-
-
-/**
- * @hide This is only used by the browser
- */
-public class HTML5VideoFullScreen extends HTML5VideoView
- implements MediaPlayerControl, MediaPlayer.OnPreparedListener,
- View.OnTouchListener {
-
- // Add this sub-class to handle the resizing when rotating screen.
- private class VideoSurfaceView extends SurfaceView {
-
- public VideoSurfaceView(Context context) {
- super(context);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
- int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
- if (mVideoWidth > 0 && mVideoHeight > 0) {
- if ( mVideoWidth * height > width * mVideoHeight ) {
- height = width * mVideoHeight / mVideoWidth;
- } else if ( mVideoWidth * height < width * mVideoHeight ) {
- width = height * mVideoWidth / mVideoHeight;
- }
- }
- setMeasuredDimension(width, height);
- }
- }
-
- // This view will contain the video.
- private VideoSurfaceView mVideoSurfaceView;
-
- // We need the full screen state to decide which surface to render to and
- // when to create the MediaPlayer accordingly.
- static final int FULLSCREEN_OFF = 0;
- static final int FULLSCREEN_SURFACECREATING = 1;
- static final int FULLSCREEN_SURFACECREATED = 2;
-
- private int mFullScreenMode;
- // The Media Controller only used for full screen mode
- private MediaController mMediaController;
-
- // SurfaceHolder for full screen
- private SurfaceHolder mSurfaceHolder = null;
-
- // Data only for MediaController
- private boolean mCanSeekBack;
- private boolean mCanSeekForward;
- private boolean mCanPause;
- private int mCurrentBufferPercentage;
-
- // The progress view.
- private static View mProgressView;
- // The container for the progress view and video view
- private static FrameLayout mLayout;
-
- // The video size will be ready when prepared. Used to make sure the aspect
- // ratio is correct.
- private int mVideoWidth;
- private int mVideoHeight;
-
- SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
- {
- public void surfaceChanged(SurfaceHolder holder, int format,
- int w, int h)
- {
- if (mPlayer != null && mMediaController != null
- && mCurrentState == STATE_PREPARED) {
- if (mMediaController.isShowing()) {
- // ensure the controller will get repositioned later
- mMediaController.hide();
- }
- mMediaController.show();
- }
- }
-
- public void surfaceCreated(SurfaceHolder holder)
- {
- mSurfaceHolder = holder;
- mFullScreenMode = FULLSCREEN_SURFACECREATED;
-
- prepareForFullScreen();
- }
-
- public void surfaceDestroyed(SurfaceHolder holder)
- {
- // After we return from this we can't use the surface any more.
- // The current Video View will be destroy when we play a new video.
- pauseAndDispatch(mProxy);
- // TODO: handle full screen->inline mode transition without a reload.
- mPlayer.release();
- mPlayer = null;
- mSurfaceHolder = null;
- if (mMediaController != null) {
- mMediaController.hide();
- }
- }
- };
-
- MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
- new MediaPlayer.OnVideoSizeChangedListener() {
- @Override
- public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
- mVideoWidth = mp.getVideoWidth();
- mVideoHeight = mp.getVideoHeight();
- if (mVideoWidth != 0 && mVideoHeight != 0) {
- mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
- }
- }
- };
-
- private SurfaceView getSurfaceView() {
- return mVideoSurfaceView;
- }
-
- HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean skipPrepare) {
- mVideoSurfaceView = new VideoSurfaceView(context);
- mFullScreenMode = FULLSCREEN_OFF;
- mVideoWidth = 0;
- mVideoHeight = 0;
- init(videoLayerId, position, skipPrepare);
- }
-
- private void setMediaController(MediaController m) {
- mMediaController = m;
- attachMediaController();
- }
-
- private void attachMediaController() {
- if (mPlayer != null && mMediaController != null) {
- mMediaController.setMediaPlayer(this);
- mMediaController.setAnchorView(mVideoSurfaceView);
- //Will be enabled when prepared
- mMediaController.setEnabled(false);
- }
- }
-
- @Override
- public void decideDisplayMode() {
- mPlayer.setDisplay(mSurfaceHolder);
- }
-
- private void prepareForFullScreen() {
- MediaController mc = new FullScreenMediaController(mProxy.getContext(), mLayout);
- mc.setSystemUiVisibility(mLayout.getSystemUiVisibility());
- setMediaController(mc);
- mPlayer.setScreenOnWhilePlaying(true);
- mPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
- prepareDataAndDisplayMode(mProxy);
- }
-
-
- private void toggleMediaControlsVisiblity() {
- if (mMediaController.isShowing()) {
- mMediaController.hide();
- } else {
- mMediaController.show();
- }
- }
-
- @Override
- public void onPrepared(MediaPlayer mp) {
- super.onPrepared(mp);
-
- mVideoSurfaceView.setOnTouchListener(this);
- // Get the capabilities of the player for this stream
- Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
- MediaPlayer.BYPASS_METADATA_FILTER);
- if (data != null) {
- mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
- || data.getBoolean(Metadata.PAUSE_AVAILABLE);
- mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
- || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
- mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
- || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
- } else {
- mCanPause = mCanSeekBack = mCanSeekForward = true;
- }
-
- if (mProgressView != null) {
- mProgressView.setVisibility(View.GONE);
- }
-
- mVideoWidth = mp.getVideoWidth();
- mVideoHeight = mp.getVideoHeight();
- // This will trigger the onMeasure to get the display size right.
- mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
- // Call into the native to ask for the state, if still in play mode,
- // this will trigger the video to play.
- mProxy.dispatchOnRestoreState();
-
- if (getStartWhenPrepared()) {
- mPlayer.start();
- // Clear the flag.
- setStartWhenPrepared(false);
- }
-
- // mMediaController status depends on the Metadata result, so put it
- // after reading the MetaData.
- // And make sure mPlayer state is updated before showing the controller.
- if (mMediaController != null) {
- mMediaController.setEnabled(true);
- mMediaController.show();
- }
- }
-
- public boolean fullScreenExited() {
- return (mLayout == null);
- }
-
- private final WebChromeClient.CustomViewCallback mCallback =
- new WebChromeClient.CustomViewCallback() {
- public void onCustomViewHidden() {
- // It listens to SurfaceHolder.Callback.SurfaceDestroyed event
- // which happens when the video view is detached from its parent
- // view. This happens in the WebChromeClient before this method
- // is invoked.
- mProxy.dispatchOnStopFullScreen();
- mLayout.removeView(getSurfaceView());
-
- if (mProgressView != null) {
- mLayout.removeView(mProgressView);
- mProgressView = null;
- }
- mLayout = null;
- // Re enable plugin views.
- mProxy.getWebView().getViewManager().showAll();
-
- mProxy = null;
-
- // Don't show the controller after exiting the full screen.
- mMediaController = null;
- mCurrentState = STATE_RESETTED;
- }
- };
-
- @Override
- public void enterFullScreenVideoState(int layerId,
- HTML5VideoViewProxy proxy, WebViewClassic webView) {
- mFullScreenMode = FULLSCREEN_SURFACECREATING;
- mCurrentBufferPercentage = 0;
- mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
- mProxy = proxy;
-
- mVideoSurfaceView.getHolder().addCallback(mSHCallback);
- mVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- mVideoSurfaceView.setFocusable(true);
- mVideoSurfaceView.setFocusableInTouchMode(true);
- mVideoSurfaceView.requestFocus();
-
- // Create a FrameLayout that will contain the VideoView and the
- // progress view (if any).
- mLayout = new FrameLayout(mProxy.getContext());
- FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- Gravity.CENTER);
-
- mLayout.addView(getSurfaceView(), layoutParams);
-
- mLayout.setVisibility(View.VISIBLE);
- WebChromeClient client = webView.getWebChromeClient();
- if (client != null) {
- client.onShowCustomView(mLayout, mCallback);
- // Plugins like Flash will draw over the video so hide
- // them while we're playing.
- if (webView.getViewManager() != null)
- webView.getViewManager().hideAll();
-
- mProgressView = client.getVideoLoadingProgressView();
- if (mProgressView != null) {
- mLayout.addView(mProgressView, layoutParams);
- mProgressView.setVisibility(View.VISIBLE);
- }
- }
- }
-
- /**
- * @return true when we are in full screen mode, even the surface not fully
- * created.
- */
- public boolean isFullScreenMode() {
- return true;
- }
-
- // MediaController FUNCTIONS:
- @Override
- public boolean canPause() {
- return mCanPause;
- }
-
- @Override
- public boolean canSeekBackward() {
- return mCanSeekBack;
- }
-
- @Override
- public boolean canSeekForward() {
- return mCanSeekForward;
- }
-
- @Override
- public int getBufferPercentage() {
- if (mPlayer != null) {
- return mCurrentBufferPercentage;
- }
- return 0;
- }
-
- @Override
- public void showControllerInFullScreen() {
- if (mMediaController != null) {
- mMediaController.show(0);
- }
- }
-
- // Other listeners functions:
- private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
- new MediaPlayer.OnBufferingUpdateListener() {
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
- mCurrentBufferPercentage = percent;
- }
- };
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (mFullScreenMode >= FULLSCREEN_SURFACECREATED
- && mMediaController != null) {
- toggleMediaControlsVisiblity();
- }
- return false;
- }
-
- @Override
- protected void switchProgressView(boolean playerBuffering) {
- if (mProgressView != null) {
- if (playerBuffering) {
- mProgressView.setVisibility(View.VISIBLE);
- } else {
- mProgressView.setVisibility(View.GONE);
- }
- }
- return;
- }
-
- static class FullScreenMediaController extends MediaController {
-
- View mVideoView;
-
- public FullScreenMediaController(Context context, View video) {
- super(context);
- mVideoView = video;
- }
-
- @Override
- public void show() {
- super.show();
- if (mVideoView != null) {
- mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
- }
- }
-
- @Override
- public void hide() {
- if (mVideoView != null) {
- mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
- }
- super.hide();
- }
-
- }
-
-}
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
deleted file mode 100644
index 2c7ea5d9b85..00000000000
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ /dev/null
@@ -1,116 +0,0 @@
-
-package android.webkit;
-
-import android.Manifest.permission;
-import android.content.pm.PackageManager;
-import android.graphics.SurfaceTexture;
-import android.media.MediaPlayer;
-import android.webkit.HTML5VideoView;
-import android.webkit.HTML5VideoViewProxy;
-import android.view.Surface;
-import android.opengl.GLES20;
-import android.os.PowerManager;
-
-/**
- * @hide This is only used by the browser
- */
-public class HTML5VideoInline extends HTML5VideoView{
-
- // Due to the fact that the decoder consume a lot of memory, we make the
- // surface texture as singleton. But the GL texture (m_textureNames)
- // associated with the surface texture can be used for showing the screen
- // shot when paused, so they are not singleton.
- private static SurfaceTexture mSurfaceTexture = null;
- private static int[] mTextureNames = null;
- // Every time when the VideoLayer Id change, we need to recreate the
- // SurfaceTexture in order to delete the old video's decoder memory.
- private static int mVideoLayerUsingSurfaceTexture = -1;
-
- // Video control FUNCTIONS:
- @Override
- public void start() {
- if (!getPauseDuringPreparing()) {
- super.start();
- }
- }
-
- HTML5VideoInline(int videoLayerId, int position) {
- init(videoLayerId, position, false);
- }
-
- @Override
- public void decideDisplayMode() {
- SurfaceTexture surfaceTexture = getSurfaceTexture(getVideoLayerId());
- Surface surface = new Surface(surfaceTexture);
- mPlayer.setSurface(surface);
- surface.release();
- }
-
- // Normally called immediately after setVideoURI. But for full screen,
- // this should be after surface holder created
- @Override
- public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
- super.prepareDataAndDisplayMode(proxy);
- setFrameAvailableListener(proxy);
- // TODO: This is a workaround, after b/5375681 fixed, we should switch
- // to the better way.
- if (mProxy.getContext().checkCallingOrSelfPermission(permission.WAKE_LOCK)
- == PackageManager.PERMISSION_GRANTED) {
- mPlayer.setWakeMode(proxy.getContext(), PowerManager.FULL_WAKE_LOCK);
- }
- }
-
- // Pause the play and update the play/pause button
- @Override
- public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
- super.pauseAndDispatch(proxy);
- }
-
- // Inline Video specific FUNCTIONS:
-
- public static SurfaceTexture getSurfaceTexture(int videoLayerId) {
- // Create the surface texture.
- if (videoLayerId != mVideoLayerUsingSurfaceTexture
- || mSurfaceTexture == null
- || mTextureNames == null) {
- // The GL texture will store in the VideoLayerManager at native side.
- // They will be clean up when requested.
- // The reason we recreated GL texture name is for screen shot support.
- mTextureNames = new int[1];
- GLES20.glGenTextures(1, mTextureNames, 0);
- mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
- }
- mVideoLayerUsingSurfaceTexture = videoLayerId;
- return mSurfaceTexture;
- }
-
- public boolean surfaceTextureDeleted() {
- return (mSurfaceTexture == null);
- }
-
- @Override
- public void deleteSurfaceTexture() {
- cleanupSurfaceTexture();
- return;
- }
-
- public static void cleanupSurfaceTexture() {
- mSurfaceTexture = null;
- mVideoLayerUsingSurfaceTexture = -1;
- return;
- }
-
- @Override
- public int getTextureName() {
- if (mTextureNames != null) {
- return mTextureNames[0];
- } else {
- return 0;
- }
- }
-
- private void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) {
- mSurfaceTexture.setOnFrameAvailableListener(l);
- }
-
-}
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index 371feea7f5c..cbda6c60520 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -1,12 +1,59 @@
+/* Copyright (c) 2011, 2012, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
package android.webkit;
+import android.Manifest.permission;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
import android.graphics.SurfaceTexture;
+import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
+import android.media.Metadata;
import android.net.Uri;
+import android.opengl.GLES20;
+import android.os.PowerManager;
import android.util.Log;
-import android.view.SurfaceView;
-import android.webkit.HTML5VideoViewProxy;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.MediaController;
+import android.widget.MediaController.MediaPlayerControl;
+
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -16,12 +63,15 @@
/**
* @hide This is only used by the browser
*/
-public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
-
- protected static final String LOGTAG = "HTML5VideoView";
+public class HTML5VideoView implements MediaPlayer.OnPreparedListener,
+ MediaPlayerControl, View.OnTouchListener, TextureView.SurfaceTextureListener,
+ SurfaceTexture.OnFrameAvailableListener, MediaPlayer.OnVideoSizeChangedListener
+{
+ private static final String LOGTAG = "HTML5VideoView";
+ private static final String COOKIE = "Cookie";
+ private static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
- protected static final String COOKIE = "Cookie";
- protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
+ private static final long ANIMATION_DURATION = 750L; // in ms
// For handling the seekTo before prepared, we need to know whether or not
// the video is prepared. Therefore, we differentiate the state between
@@ -34,36 +84,72 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
static final int STATE_PREPARING = 1;
static final int STATE_PREPARED = 2;
static final int STATE_PLAYING = 3;
- static final int STATE_RESETTED = 4;
+ static final int STATE_BUFFERING = 4;
+ static final int STATE_RELEASED = 5;
- protected HTML5VideoViewProxy mProxy;
+ static final int ANIMATION_STATE_NONE = 0;
+ static final int ANIMATION_STATE_STARTED = 1;
+ static final int ANIMATION_STATE_FINISHED = 2;
+ private int mAnimationState;
+
+ private HTML5VideoViewProxy mProxy;
// Save the seek time when not prepared. This can happen when switching
// video besides initial load.
- protected int mSaveSeekTime;
-
- // This is used to find the VideoLayer on the native side.
- protected int mVideoLayerId;
+ private int mSaveSeekTime;
- // Given the fact we only have one SurfaceTexture, we cannot support multiple
- // player at the same time. We may recreate a new one and abandon the old
- // one at transition time.
- protected static MediaPlayer mPlayer = null;
- protected static int mCurrentState = -1;
+ private MediaPlayer mPlayer;
+ private int mCurrentState;
// We need to save such info.
- protected Uri mUri;
- protected Map mHeaders;
+ private Uri mUri;
+ private Map mHeaders;
// The timer for timeupate events.
// See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
- protected static Timer mTimer;
+ private Timer mTimer;
+
+ private boolean mIsFullscreen;
protected boolean mPauseDuringPreparing;
// The spec says the timer should fire every 250 ms or less.
private static final int TIMEUPDATE_PERIOD = 250; // ms
- private boolean mSkipPrepare = false;
+
+ private int mVideoWidth;
+ private int mVideoHeight;
+ private int mDuration;
+
+ private int mFullscreenWidth;
+ private int mFullscreenHeight;
+
+ private float mInlineX;
+ private float mInlineY;
+ private float mInlineWidth;
+ private float mInlineHeight;
+
+ private Point mDisplaySize;
+ private int[] mWebViewLocation;
+
+ // The Media Controller only used for full screen mode
+ private MediaController mMediaController;
+
+ // Data only for MediaController
+ private boolean mCanSeekBack;
+ private boolean mCanSeekForward;
+ private boolean mCanPause;
+ private int mCurrentBufferPercentage;
+
+ // The progress view.
+ private View mProgressView;
+ // The container for the progress view and video view
+ private FrameLayout mLayout;
+
+ private SurfaceTexture mSurfaceTexture;
+ private VideoTextureView mTextureView;
+ // m_textureNames is the texture bound with this SurfaceTexture.
+ private int[] mTextureNames;
+ private boolean mNeedsAttachToInlineGlContext;
// common Video control FUNCTIONS:
public void start() {
@@ -77,8 +163,18 @@ public void start() {
TIMEUPDATE_PERIOD);
}
mPlayer.start();
+
setPlayerBuffering(false);
- }
+ // Notify webkit MediaPlayer that video is playing to make sure
+ // webkit MediaPlayer is always synchronized with the proxy.
+ // This is particularly important when using the fullscreen
+ // MediaController.
+ mProxy.dispatchOnPlaying();
+
+ if (mMediaController != null)
+ mMediaController.show();
+ } else
+ setStartWhenPrepared(true);
}
public void pause() {
@@ -87,6 +183,15 @@ public void pause() {
} else if (mCurrentState == STATE_PREPARING) {
mPauseDuringPreparing = true;
}
+ // Notify webkit MediaPlayer that video is paused to make sure
+ // webkit MediaPlayer is always synchronized with the proxy
+ // This is particularly important when using the fullscreen
+ // MediaController.
+ mProxy.dispatchOnPaused();
+
+ if (mMediaController != null)
+ mMediaController.show(0);
+
// Delete the Timer to stop it since there is no stop call.
if (mTimer != null) {
mTimer.purge();
@@ -95,6 +200,22 @@ public void pause() {
}
}
+ public void attachToInlineGlContextIfNeeded() {
+ if (mNeedsAttachToInlineGlContext && !mIsFullscreen
+ && (mCurrentState == STATE_PREPARED ||
+ mCurrentState == STATE_PREPARING ||
+ mCurrentState == STATE_PLAYING)) {
+ // Attach the previous GL texture
+ try {
+ mSurfaceTexture.attachToGLContext(getTextureName());
+ mNeedsAttachToInlineGlContext = false;
+ } catch (RuntimeException e) {
+ // This can occur when the EGL context has been detached from this view.
+ // Just try to re-attach at a later time.
+ }
+ }
+ }
+
public int getDuration() {
if (mCurrentState == STATE_PREPARED) {
return mPlayer.getDuration();
@@ -125,11 +246,13 @@ public boolean isPlaying() {
}
}
- public void reset() {
- if (mCurrentState != STATE_RESETTED) {
- mPlayer.reset();
+ public void release() {
+ if (mCurrentState != STATE_RELEASED) {
+ stopPlayback();
+ mPlayer.release();
+ mSurfaceTexture.release();
}
- mCurrentState = STATE_RESETTED;
+ mCurrentState = STATE_RELEASED;
}
public void stopPlayback() {
@@ -142,28 +265,26 @@ public boolean getPauseDuringPreparing() {
return mPauseDuringPreparing;
}
+ public void setVolume(float volume) {
+ if (mCurrentState != STATE_RELEASED) {
+ mPlayer.setVolume(volume, volume);
+ }
+ }
+
// Every time we start a new Video, we create a VideoView and a MediaPlayer
- public void init(int videoLayerId, int position, boolean skipPrepare) {
- if (mPlayer == null) {
- mPlayer = new MediaPlayer();
- mCurrentState = STATE_INITIALIZED;
- }
- mSkipPrepare = skipPrepare;
- // If we want to skip the prepare, then we keep the state.
- if (!mSkipPrepare) {
- mCurrentState = STATE_INITIALIZED;
- }
- mProxy = null;
- mVideoLayerId = videoLayerId;
+ HTML5VideoView(HTML5VideoViewProxy proxy, int position) {
+ mPlayer = new MediaPlayer();
+ mCurrentState = STATE_INITIALIZED;
+ mProxy = proxy;
mSaveSeekTime = position;
mTimer = null;
mPauseDuringPreparing = false;
+ mIsFullscreen = false;
+ mDisplaySize = new Point();
+ mWebViewLocation = new int[2];
}
- protected HTML5VideoView() {
- }
-
- protected static Map generateHeaders(String url,
+ private static Map generateHeaders(String url,
HTML5VideoViewProxy proxy) {
boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled();
String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate);
@@ -178,10 +299,39 @@ protected static Map generateHeaders(String url,
return headers;
}
- public void setVideoURI(String uri, HTML5VideoViewProxy proxy) {
- // When switching players, surface texture will be reused.
+ public void setVideoURI(String uri) {
mUri = Uri.parse(uri);
- mHeaders = generateHeaders(uri, proxy);
+ mHeaders = generateHeaders(uri, mProxy);
+ }
+
+ // When there is a frame ready from surface texture, we should tell WebView
+ // to refresh.
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ // TODO: This should support partial invalidation too.
+ mProxy.getWebView().invalidate();
+ }
+
+ public void retrieveMetadata(HTML5VideoViewProxy proxy) {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ try {
+ retriever.setDataSource(mUri.toString(), mHeaders);
+ mVideoWidth = Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
+ mVideoHeight = Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
+ mDuration = Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_DURATION));
+ proxy.updateSizeAndDuration(mVideoWidth, mVideoHeight, mDuration);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ // RuntimeException occurs when connection is not available or
+ // the source type is not supported (e.g. HLS). Not calling
+ // e.printStackTrace() here since it occurs quite often.
+ } finally {
+ retriever.release();
+ }
}
// Listeners setup FUNCTIONS:
@@ -189,69 +339,45 @@ public void setOnCompletionListener(HTML5VideoViewProxy proxy) {
mPlayer.setOnCompletionListener(proxy);
}
- public void setOnErrorListener(HTML5VideoViewProxy proxy) {
- mPlayer.setOnErrorListener(proxy);
- }
-
- public void setOnPreparedListener(HTML5VideoViewProxy proxy) {
- mProxy = proxy;
- mPlayer.setOnPreparedListener(this);
- }
-
- public void setOnInfoListener(HTML5VideoViewProxy proxy) {
- mPlayer.setOnInfoListener(proxy);
- }
-
- public void prepareDataCommon(HTML5VideoViewProxy proxy) {
- if (!mSkipPrepare) {
- try {
- mPlayer.reset();
- mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders);
- mPlayer.prepareAsync();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- mCurrentState = STATE_PREPARING;
- } else {
- // If we skip prepare and the onPrepared happened in inline mode, we
- // don't need to call prepare again, we just need to call onPrepared
- // to refresh the state here.
- if (mCurrentState >= STATE_PREPARED) {
- onPrepared(mPlayer);
- }
- mSkipPrepare = false;
+ private void prepareDataCommon(HTML5VideoViewProxy proxy) {
+ try {
+ mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders);
+ mPlayer.prepareAsync();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
}
+ mCurrentState = STATE_PREPARING;
}
- public void reprepareData(HTML5VideoViewProxy proxy) {
- mPlayer.reset();
- prepareDataCommon(proxy);
- }
-
- // Normally called immediately after setVideoURI. But for full screen,
- // this should be after surface holder created
- public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
- // SurfaceTexture will be created lazily here for inline mode
+ public void prepareDataAndDisplayMode() {
decideDisplayMode();
- setOnCompletionListener(proxy);
- setOnPreparedListener(proxy);
- setOnErrorListener(proxy);
- setOnInfoListener(proxy);
-
- prepareDataCommon(proxy);
- }
+ mPlayer.setOnCompletionListener(mProxy);
+ mPlayer.setOnPreparedListener(this);
+ mPlayer.setOnErrorListener(mProxy);
+ mPlayer.setOnInfoListener(mProxy);
+ mPlayer.setOnVideoSizeChangedListener(this);
+ prepareDataCommon(mProxy);
- // Common code
- public int getVideoLayerId() {
- return mVideoLayerId;
+ // TODO: This is a workaround, after b/5375681 fixed, we should switch
+ // to the better way.
+ if (mProxy.getContext().checkCallingOrSelfPermission(permission.WAKE_LOCK)
+ == PackageManager.PERMISSION_GRANTED) {
+ mPlayer.setWakeMode(mProxy.getContext(), PowerManager.FULL_WAKE_LOCK);
+ }
+ if (!mIsFullscreen)
+ setInlineFrameAvailableListener();
}
+ // This configures the SurfaceTexture OnFrameAvailableListener in inline mode
+ private void setInlineFrameAvailableListener() {
+ getSurfaceTexture().setOnFrameAvailableListener(this);
+ }
public int getCurrentState() {
if (isPlaying()) {
@@ -261,7 +387,7 @@ public int getCurrentState() {
}
}
- private static final class TimeupdateTask extends TimerTask {
+ private final class TimeupdateTask extends TimerTask {
private HTML5VideoViewProxy mProxy;
public TimeupdateTask(HTML5VideoViewProxy proxy) {
@@ -274,77 +400,333 @@ public void run() {
}
}
- @Override
public void onPrepared(MediaPlayer mp) {
mCurrentState = STATE_PREPARED;
seekTo(mSaveSeekTime);
- if (mProxy != null) {
+
+ if (mProxy != null)
mProxy.onPrepared(mp);
- }
- if (mPauseDuringPreparing) {
- pauseAndDispatch(mProxy);
+
+ if (mPauseDuringPreparing || !getStartWhenPrepared())
mPauseDuringPreparing = false;
+ else
+ start();
+
+ if (mIsFullscreen) {
+ attachMediaController();
+ if (mProgressView != null)
+ mProgressView.setVisibility(View.GONE);
}
}
- // Pause the play and update the play/pause button
- public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
- pause();
- if (proxy != null) {
- proxy.dispatchOnPaused();
+ public void decideDisplayMode() {
+ SurfaceTexture surfaceTexture = getSurfaceTexture();
+ Surface surface = new Surface(surfaceTexture);
+ mPlayer.setSurface(surface);
+ surface.release();
+ }
+
+ // SurfaceTexture will be created lazily here
+ public SurfaceTexture getSurfaceTexture() {
+ // Create the surface texture.
+ if (mSurfaceTexture == null || mTextureNames == null) {
+ mTextureNames = new int[1];
+ GLES20.glGenTextures(1, mTextureNames, 0);
+ mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
}
+ return mSurfaceTexture;
}
- // Below are functions that are different implementation on inline and full-
- // screen mode. Some are specific to one type, but currently are called
- // directly from the proxy.
- public void enterFullScreenVideoState(int layerId,
- HTML5VideoViewProxy proxy, WebViewClassic webView) {
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ mVideoWidth = width;
+ mVideoHeight = height;
+ if (mTextureView != null) {
+ // Request layout now that mVideoWidth and mVideoHeight are known
+ // This will trigger onMeasure to get the display size right
+ mTextureView.requestLayout();
+ }
+ if (mProxy != null) {
+ mProxy.onVideoSizeChanged(mp, width, height);
+ }
}
- public boolean isFullScreenMode() {
- return false;
+ public int getTextureName() {
+ if (mTextureNames != null) {
+ return mTextureNames[0];
+ } else {
+ return 0;
+ }
}
- public void decideDisplayMode() {
+ // This is true only when the player is buffering and paused
+ private boolean mPlayerBuffering = false;
+
+ public boolean getPlayerBuffering() {
+ return mPlayerBuffering;
}
- public boolean getReadyToUseSurfTex() {
- return false;
+ public void setPlayerBuffering(boolean playerBuffering) {
+ mPlayerBuffering = playerBuffering;
+ if (mProgressView != null)
+ switchProgressView(playerBuffering);
}
- public void deleteSurfaceTexture() {
+ private void switchProgressView(boolean playerBuffering) {
+ if (playerBuffering)
+ mProgressView.setVisibility(View.VISIBLE);
+ else
+ mProgressView.setVisibility(View.GONE);
}
- public int getTextureName() {
- return 0;
+ class VideoTextureView extends TextureView {
+ public VideoTextureView(Context context, SurfaceTexture surface) {
+ super(context);
+ try {
+ // Detach the inline GL context
+ surface.detachFromGLContext();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ setSurfaceTexture(surface);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mFullscreenWidth = getDefaultSize(mVideoWidth, widthMeasureSpec);
+ mFullscreenHeight = getDefaultSize(mVideoHeight, heightMeasureSpec);
+ if (mVideoWidth > 0 && mVideoHeight > 0) {
+ if ( mVideoWidth * mFullscreenHeight > mFullscreenWidth * mVideoHeight ) {
+ mFullscreenHeight = mFullscreenWidth * mVideoHeight / mVideoWidth;
+ } else if ( mVideoWidth * mFullscreenHeight < mFullscreenWidth * mVideoHeight ) {
+ mFullscreenWidth = mFullscreenHeight * mVideoWidth / mVideoHeight;
+ }
+ }
+ setMeasuredDimension(mFullscreenWidth, mFullscreenHeight);
+
+ if (mAnimationState == ANIMATION_STATE_NONE) {
+ // Configuring VideoTextureView to inline bounds
+ mTextureView.setTranslationX(getInlineXOffset());
+ mTextureView.setTranslationY(getInlineYOffset());
+ mTextureView.setScaleX(getInlineXScale());
+ mTextureView.setScaleY(getInlineYScale());
+
+ // inline to fullscreen zoom out animation
+ mTextureView.animate().setListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ mAnimationState = ANIMATION_STATE_FINISHED;
+ attachMediaController();
+ }
+ });
+ mTextureView.animate().setDuration(ANIMATION_DURATION);
+ mAnimationState = ANIMATION_STATE_STARTED;
+ mTextureView.animate().scaleX(1.0f).scaleY(1.0f).translationX(0.0f).translationY(0.0f);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Attach to the previous GL texture
+ mNeedsAttachToInlineGlContext = true;
+ attachToInlineGlContextIfNeeded();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Needed to update the view during orientation change when video is paused
+ // Calling setOpaque() forces the layer to be updated
+ setOpaque(false);
+ setOpaque(true);
+ }
}
- // This is true only when the player is buffering and paused
- public boolean mPlayerBuffering = false;
+ // Note: Call this for fullscreen mode only
+ // If MediaPlayer is prepared, enable the buttons
+ private void attachMediaController() {
+ // Get the capabilities of the player for this stream
+ // This should only be called when MediaPlayer is in prepared state
+ // Otherwise data will return invalid values
+ if (mIsFullscreen && mCurrentState == STATE_PREPARED) {
+ if (mMediaController == null) {
+ MediaController mc = new FullscreenMediaController(mProxy.getContext(), mLayout);
+ mc.setSystemUiVisibility(mLayout.getSystemUiVisibility());
+ mMediaController = mc;
+ mMediaController.setMediaPlayer(this);
+ mMediaController.setAnchorView(mTextureView);
+ mMediaController.setEnabled(false);
+ }
- public boolean getPlayerBuffering() {
- return mPlayerBuffering;
+ Metadata data = mPlayer.getMetadata(MediaPlayer.METADATA_ALL,
+ MediaPlayer.BYPASS_METADATA_FILTER);
+ if (data != null) {
+ mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
+ || data.getBoolean(Metadata.PAUSE_AVAILABLE);
+ mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
+ || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
+ mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
+ || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
+ } else {
+ mCanPause = mCanSeekBack = mCanSeekForward = true;
+ }
+ // mMediaController status depends on the Metadata result, so put it
+ // after reading the MetaData
+ mMediaController.setEnabled(true);
+
+ if (mAnimationState == ANIMATION_STATE_FINISHED) {
+ // If paused, should show the controller for ever!
+ if (getStartWhenPrepared() || isPlaying())
+ mMediaController.show();
+ else
+ mMediaController.show(0);
+ }
+ }
}
- public void setPlayerBuffering(boolean playerBuffering) {
- mPlayerBuffering = playerBuffering;
- switchProgressView(playerBuffering);
+ private void toggleMediaControlsVisiblity() {
+ if (mMediaController.isShowing())
+ mMediaController.hide();
+ else
+ mMediaController.show();
+ }
+
+ public boolean fullscreenExited() {
+ return (mLayout == null);
+ }
+
+ private final WebChromeClient.CustomViewCallback mCallback =
+ new WebChromeClient.CustomViewCallback() {
+ public void onCustomViewHidden() {
+ mProxy.prepareExitFullscreen();
+ }
+ };
+
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
}
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
- protected void switchProgressView(boolean playerBuffering) {
- // Only used in HTML5VideoFullScreen
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
- public boolean surfaceTextureDeleted() {
- // Only meaningful for HTML5VideoInline
+ /**
+ * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
+ * If returns true, no rendering should happen inside the surface texture after this method
+ * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
+ *
+ * @param surface The surface about to be destroyed
+ */
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ // Tells the TextureView not to free the buffer
return false;
}
- public boolean fullScreenExited() {
- // Only meaningful for HTML5VideoFullScreen
- return false;
+ public void enterFullscreenVideoState(WebViewClassic webView, float x, float y, float w, float h) {
+ if (mIsFullscreen == true)
+ return;
+ mIsFullscreen = true;
+ mAnimationState = ANIMATION_STATE_NONE;
+ mCurrentBufferPercentage = 0;
+ mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
+ mInlineX = x;
+ mInlineY = y;
+ mInlineWidth = w;
+ mInlineHeight = h;
+
+ assert(mSurfaceTexture != null);
+ mTextureView = new VideoTextureView(mProxy.getContext(), getSurfaceTexture());
+ mTextureView.setOnTouchListener(this);
+ mTextureView.setFocusable(true);
+ mTextureView.setFocusableInTouchMode(true);
+ mTextureView.requestFocus();
+
+ mLayout = new FrameLayout(mProxy.getContext());
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER);
+ mTextureView.setVisibility(View.VISIBLE);
+ mTextureView.setSurfaceTextureListener(this);
+
+ mLayout.addView(mTextureView, layoutParams);
+
+ mLayout.setVisibility(View.VISIBLE);
+ WebChromeClient client = webView.getWebChromeClient();
+ if (client != null) {
+ client.onShowCustomView(mLayout, mCallback);
+ // Plugins like Flash will draw over the video so hide
+ // them while we're playing.
+ if (webView.getViewManager() != null)
+ webView.getViewManager().hideAll();
+
+ // Add progress view
+ mProgressView = client.getVideoLoadingProgressView();
+ if (mProgressView != null) {
+ mLayout.addView(mProgressView, layoutParams);
+ if (mCurrentState != STATE_PREPARED)
+ mProgressView.setVisibility(View.VISIBLE);
+ else
+ mProgressView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ public void exitFullscreenVideoState(float x, float y, float w, float h) {
+ if (mIsFullscreen == false) {
+ return;
+ }
+ mIsFullscreen = false;
+
+ mInlineX = x;
+ mInlineY = y;
+ mInlineWidth = w;
+ mInlineHeight = h;
+
+ // Don't show the controller after exiting the full screen.
+ if (mMediaController != null) {
+ mMediaController.hide();
+ mMediaController = null;
+ }
+
+ if (mAnimationState == ANIMATION_STATE_STARTED) {
+ mTextureView.animate().cancel();
+ finishExitingFullscreen();
+ } else {
+ // fullscreen to inline zoom in animation
+ mTextureView.animate().setListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ finishExitingFullscreen();
+ }
+ });
+
+ mTextureView.animate().setDuration(ANIMATION_DURATION);
+ mTextureView.animate().scaleX(getInlineXScale()).scaleY(getInlineYScale()).translationX(getInlineXOffset()).translationY(getInlineYOffset());
+ }
+ }
+
+ public boolean isFullscreenMode() {
+ return mIsFullscreen;
+ }
+
+ // MediaController FUNCTIONS:
+ public boolean canPause() {
+ return mCanPause;
+ }
+
+ public boolean canSeekBackward() {
+ return mCanSeekBack;
+ }
+
+ public boolean canSeekForward() {
+ return mCanSeekForward;
+ }
+
+ public int getBufferPercentage() {
+ if (mPlayer != null) {
+ return mCurrentBufferPercentage;
+ }
+ return 0;
}
private boolean mStartWhenPrepared = false;
@@ -357,7 +739,105 @@ public boolean getStartWhenPrepared() {
return mStartWhenPrepared;
}
- public void showControllerInFullScreen() {
+ public void showControllerInFullscreen() {
+ if (mMediaController != null) {
+ mMediaController.show(0);
+ }
+ }
+
+ // Other listeners functions:
+ private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
+ new MediaPlayer.OnBufferingUpdateListener() {
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ mCurrentBufferPercentage = percent;
+ }
+ };
+
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mIsFullscreen && mMediaController != null)
+ toggleMediaControlsVisiblity();
+ return false;
+ }
+
+ static class FullscreenMediaController extends MediaController {
+
+ View mVideoView;
+
+ public FullscreenMediaController(Context context, View video) {
+ super(context);
+ mVideoView = video;
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ if (mVideoView != null) {
+ mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ }
+ }
+
+ @Override
+ public void hide() {
+ if (mVideoView != null) {
+ mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+ }
+ super.hide();
+ }
+ }
+
+ private float getInlineXOffset() {
+ updateDisplaySize();
+ if (mInlineWidth < 0 || mInlineHeight < 0)
+ return 0;
+ else
+ return mInlineX + mWebViewLocation[0] - (mDisplaySize.x - mInlineWidth) / 2;
+ }
+
+ private float getInlineYOffset() {
+ updateDisplaySize();
+ if (mInlineWidth < 0 || mInlineHeight < 0)
+ return 0;
+ else
+ return mInlineY + mWebViewLocation[1] - (mDisplaySize.y - mInlineHeight) / 2;
+ }
+
+ private float getInlineXScale() {
+ if (mInlineWidth < 0 || mInlineHeight < 0 || mFullscreenWidth == 0)
+ return 0;
+ else
+ return mInlineWidth / mFullscreenWidth;
+ }
+
+ private float getInlineYScale() {
+ if (mInlineWidth < 0 || mInlineHeight < 0 || mFullscreenHeight == 0)
+ return 0;
+ else
+ return mInlineHeight / mFullscreenHeight;
+ }
+
+ private void updateDisplaySize() {
+ WindowManager wm = (WindowManager)mProxy.getContext().getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ display.getSize(mDisplaySize);
+
+ mProxy.getWebView().getWebView().getLocationOnScreen(mWebViewLocation);
+ mWebViewLocation[1] += mProxy.getWebView().getVisibleTitleHeight();
+ }
+
+ private void finishExitingFullscreen() {
+ mProxy.dispatchOnStopFullscreen();
+ mLayout.removeView(mTextureView);
+ mTextureView = null;
+ if (mProgressView != null) {
+ mLayout.removeView(mProgressView);
+ mProgressView = null;
+ }
+ mLayout = null;
+ // Re enable plugin views.
+ mProxy.getWebView().getViewManager().showAll();
+ // Set the frame available listener back to the inline listener
+ setInlineFrameAvailableListener();
}
}
diff --git a/core/java/android/webkit/HTML5VideoViewManager.java b/core/java/android/webkit/HTML5VideoViewManager.java
new file mode 100644
index 00000000000..3f850bda468
--- /dev/null
+++ b/core/java/android/webkit/HTML5VideoViewManager.java
@@ -0,0 +1,108 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package android.webkit;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @hide This is only used by the browser. Manager for HTML5 video views.
+ */
+class HTML5VideoViewManager
+{
+ private WebViewClassic mWebView;
+ private ArrayList mProxyList;
+ private final Thread mUiThread;
+
+ public HTML5VideoViewManager(WebViewClassic webView) {
+ mWebView = webView;
+ mUiThread = Thread.currentThread();
+ mProxyList = new ArrayList();
+ }
+
+ public boolean registerProxy(HTML5VideoViewProxy proxy) {
+ assert (mUiThread == Thread.currentThread());
+ boolean result = mProxyList.add(proxy);
+ return result;
+ }
+
+ public boolean unregisterProxy(HTML5VideoViewProxy proxy) {
+ assert (mUiThread == Thread.currentThread());
+ boolean result = mProxyList.remove(proxy);
+ return result;
+ }
+
+ public void setBaseLayer(int layer) {
+ assert (mUiThread == Thread.currentThread());
+ Iterator iter = mProxyList.iterator();
+ while (iter.hasNext()) {
+ HTML5VideoViewProxy proxy = iter.next();
+ proxy.setBaseLayer(layer);
+ }
+ }
+
+ public void suspend() {
+ assert (mUiThread == Thread.currentThread());
+ Iterator iter = mProxyList.iterator();
+ while (iter.hasNext()) {
+ HTML5VideoViewProxy proxy = iter.next();
+ proxy.suspend();
+ }
+ }
+
+ public void pauseAndDispatch() {
+ assert (mUiThread == Thread.currentThread());
+ Iterator iter = mProxyList.iterator();
+ while (iter.hasNext()) {
+ HTML5VideoViewProxy proxy = iter.next();
+ proxy.pauseAndDispatch();
+ }
+ }
+
+ public void enterFullscreenVideo(int layerId, String url) {
+ assert (mUiThread == Thread.currentThread());
+ Iterator iter = mProxyList.iterator();
+ while (iter.hasNext()) {
+ HTML5VideoViewProxy proxy = iter.next();
+ if (proxy.getVideoLayerId() == layerId)
+ proxy.webkitEnterFullscreen();
+ else
+ proxy.pauseAndDispatch();
+ }
+ }
+
+ public void exitFullscreenVideo() {
+ assert (mUiThread == Thread.currentThread());
+ Iterator iter = mProxyList.iterator();
+ while (iter.hasNext()) {
+ HTML5VideoViewProxy proxy = iter.next();
+ proxy.webKitExitFullscreen();
+ }
+ }
+}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index ab884dfbdd8..3f82adf780a 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2009 The Android Open Source Project
+ * Copyright (c) 2011, 2012, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,14 +41,14 @@
import java.util.Map;
/**
- * Proxy for HTML5 video views.
+ *
Proxy for HTML5 video views.
*/
class HTML5VideoViewProxy extends Handler
implements MediaPlayer.OnPreparedListener,
MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener,
MediaPlayer.OnInfoListener,
- SurfaceTexture.OnFrameAvailableListener {
+ MediaPlayer.OnVideoSizeChangedListener {
// Logging tag.
private static final String LOGTAG = "HTML5VideoViewProxy";
@@ -59,6 +60,13 @@ class HTML5VideoViewProxy extends Handler
private static final int LOAD_DEFAULT_POSTER = 104;
private static final int BUFFERING_START = 105;
private static final int BUFFERING_END = 106;
+ private static final int INIT = 107;
+ private static final int TERM = 108;
+ private static final int SET_VOLUME = 109;
+ private static final int LOAD = 110;
+ private static final int LOAD_METADATA = 111;
+ private static final int ENTER_FULLSCREEN = 112;
+ private static final int EXIT_FULLSCREEN = 113;
// Message Ids to be handled on the WebCore thread
private static final int PREPARED = 200;
@@ -66,7 +74,8 @@ class HTML5VideoViewProxy extends Handler
private static final int POSTER_FETCHED = 202;
private static final int PAUSED = 203;
private static final int STOPFULLSCREEN = 204;
- private static final int RESTORESTATE = 205;
+ private static final int SIZE_CHANGED = 205;
+ private static final int PLAYING = 206;
// Timer thread -> UI thread
private static final int TIMEUPDATE = 300;
@@ -84,106 +93,90 @@ class HTML5VideoViewProxy extends Handler
private PosterDownloader mPosterDownloader;
// The seek position.
private int mSeekPosition;
- // A helper class to control the playback. This executes on the UI thread!
- private static final class VideoPlayer {
- // The proxy that is currently playing (if any).
- private static HTML5VideoViewProxy mCurrentProxy;
- // The VideoView instance. This is a singleton for now, at least until
- // http://b/issue?id=1973663 is fixed.
- private static HTML5VideoView mHTML5VideoView;
-
- private static boolean isVideoSelfEnded = false;
- // By using the baseLayer and the current video Layer ID, we can
- // identify the exact layer on the UI thread to use the SurfaceTexture.
- private static int mBaseLayer = 0;
+ // The video layer ID
+ private int mVideoLayerId;
- private static void setPlayerBuffering(boolean playerBuffering) {
+ // A helper class to control the playback. This executes on the UI thread!
+ private final class VideoPlayer {
+ private HTML5VideoViewProxy mProxy;
+ private HTML5VideoView mHTML5VideoView;
+
+ private boolean isVideoSelfEnded = false;
+ // The cached volume before HTML5VideoView is initialized.
+ // This should be set back to -1.0f every time after the
+ // function mHTML5VideoView.setVolume is called.
+ private float mCachedVolume = -1.0f;
+ // Cached media position used to preserve playback position when
+ // resuming suspended video
+ private int mCachedPosition;
+
+ private void setPlayerBuffering(boolean playerBuffering) {
mHTML5VideoView.setPlayerBuffering(playerBuffering);
}
+ VideoPlayer(HTML5VideoViewProxy proxy) {
+ mProxy = proxy;
+ }
+
// Every time webView setBaseLayer, this will be called.
// When we found the Video layer, then we set the Surface Texture to it.
- // Otherwise, we may want to delete the Surface Texture to save memory.
- public static void setBaseLayer(int layer) {
- // Don't do this for full screen mode.
- if (mHTML5VideoView != null
- && !mHTML5VideoView.isFullScreenMode()
- && !mHTML5VideoView.surfaceTextureDeleted()) {
- mBaseLayer = layer;
-
- int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
- SurfaceTexture surfTexture =
- HTML5VideoInline.getSurfaceTexture(currentVideoLayerId);
- int textureName = mHTML5VideoView.getTextureName();
-
- if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
- int playerState = mHTML5VideoView.getCurrentState();
- if (mHTML5VideoView.getPlayerBuffering())
- playerState = HTML5VideoView.STATE_PREPARING;
- boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
- layer, currentVideoLayerId, textureName,
- playerState);
- if (playerState >= HTML5VideoView.STATE_PREPARED
- && !foundInTree) {
- mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
- mHTML5VideoView.deleteSurfaceTexture();
- }
- }
+ // By using the baseLayer and the current video Layer ID, we can
+ // identify the exact layer on the UI thread to use the SurfaceTexture.
+ // We should never save the base layer handle since its lifetime is not
+ // guaranteed outside of the function call from WebView::setBaseLayer.
+ //
+ // This function allows layer value to be null. If layer is null, only
+ // the player state will be set in native code. This allows the proxy to
+ // save the player state in the native video layer.
+ public void setBaseLayer(int layer) {
+ if (mHTML5VideoView != null) {
+ int playerState = mHTML5VideoView.getCurrentState();
+ if (mHTML5VideoView.getPlayerBuffering())
+ playerState = HTML5VideoView.STATE_BUFFERING;
+
+ nativeSendSurfaceTexture(mHTML5VideoView.getSurfaceTexture(),
+ layer, mVideoLayerId, mHTML5VideoView.getTextureName(),
+ playerState, mNativePointer);
+
+ // Re-attach the inline GL context
+ // TODO: Find a better place to call this.
+ mHTML5VideoView.attachToInlineGlContextIfNeeded();
}
}
- // When a WebView is paused, we also want to pause the video in it.
- public static void pauseAndDispatch() {
+ public void suspend() {
if (mHTML5VideoView != null) {
- mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
- // When switching out, clean the video content on the old page
- // by telling the layer not readyToUseSurfTex.
- setBaseLayer(mBaseLayer);
- }
- }
-
- public static void enterFullScreenVideo(int layerId, String url,
- HTML5VideoViewProxy proxy, WebViewClassic webView) {
- // Save the inline video info and inherit it in the full screen
- int savePosition = 0;
- boolean canSkipPrepare = false;
- boolean forceStart = false;
- if (mHTML5VideoView != null) {
- // We don't allow enter full screen mode while the previous
- // full screen video hasn't finished yet.
- if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
- Log.w(LOGTAG, "Try to reenter the full screen mode");
- return;
- }
- int playerState = mHTML5VideoView.getCurrentState();
- // If we are playing the same video, then it is better to
- // save the current position.
- if (layerId == mHTML5VideoView.getVideoLayerId()) {
- savePosition = mHTML5VideoView.getCurrentPosition();
- canSkipPrepare = (playerState == HTML5VideoView.STATE_PREPARING
- || playerState == HTML5VideoView.STATE_PREPARED
- || playerState == HTML5VideoView.STATE_PLAYING)
- && !mHTML5VideoView.isFullScreenMode();
- }
- if (!canSkipPrepare) {
- mHTML5VideoView.reset();
- } else {
- forceStart = playerState == HTML5VideoView.STATE_PREPARING
- || playerState == HTML5VideoView.STATE_PLAYING;
- }
- }
- mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
- layerId, savePosition, canSkipPrepare);
- mHTML5VideoView.setStartWhenPrepared(forceStart);
- mCurrentProxy = proxy;
- mHTML5VideoView.setVideoURI(url, mCurrentProxy);
- mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
- }
-
- public static void exitFullScreenVideo(HTML5VideoViewProxy proxy,
- WebViewClassic webView) {
- if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
- WebChromeClient client = webView.getWebChromeClient();
+ mHTML5VideoView.pause();
+ mCachedPosition = getCurrentPosition();
+ mHTML5VideoView.release();
+ // Call setBaseLayer to update VideoLayerAndroid player state
+ // This is important for flagging the associated texture for recycling
+ setBaseLayer(0);
+ mHTML5VideoView = null;
+ // isVideoSelfEnded is false when video playback
+ // has ended but is not complete.
+ // isVideoSelfEnded is true only when playback is complete.
+ isVideoSelfEnded = false;
+ end();
+ }
+ }
+
+ public void enterFullscreenVideo(String url, float x, float y, float w, float h) {
+ if (ensureHTML5VideoView(url, mCachedPosition, false)) {
+ mHTML5VideoView.prepareDataAndDisplayMode();
+ }
+ mHTML5VideoView.enterFullscreenVideoState(mWebView, x, y, w, h);
+ }
+
+ public void exitFullscreenVideo(float x, float y, float w, float h) {
+ if (mHTML5VideoView != null) {
+ mHTML5VideoView.exitFullscreenVideoState(x, y, w, h);
+ }
+ }
+
+ public void webkitExitFullscreenVideo() {
+ if (!mHTML5VideoView.fullscreenExited() && mHTML5VideoView.isFullscreenMode()) {
+ WebChromeClient client = mWebView.getWebChromeClient();
if (client != null) {
client.onHideCustomView();
}
@@ -191,70 +184,36 @@ public static void exitFullScreenVideo(HTML5VideoViewProxy proxy,
}
// This is on the UI thread.
- // When native tell Java to play, we need to check whether or not it is
- // still the same video by using videoLayerId and treat it differently.
- public static void play(String url, int time, HTML5VideoViewProxy proxy,
- WebChromeClient client, int videoLayerId) {
- int currentVideoLayerId = -1;
- boolean backFromFullScreenMode = false;
- if (mHTML5VideoView != null) {
- currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
- backFromFullScreenMode = mHTML5VideoView.fullScreenExited();
-
- // When playing video back to back in full screen mode,
- // javascript will switch the src and call play.
- // In this case, we can just reuse the same full screen view,
- // and play the video after prepared.
- if (mHTML5VideoView.isFullScreenMode()
- && !backFromFullScreenMode
- && currentVideoLayerId != videoLayerId
- && mCurrentProxy != proxy) {
- mCurrentProxy = proxy;
- mHTML5VideoView.setStartWhenPrepared(true);
- mHTML5VideoView.setVideoURI(url, proxy);
- mHTML5VideoView.reprepareData(proxy);
- return;
- }
+ public void loadMetadata(String url) {
+ if (ensureHTML5VideoView(url, 0, false)) {
+ mHTML5VideoView.retrieveMetadata(mProxy);
}
+ }
- if (backFromFullScreenMode
- || currentVideoLayerId != videoLayerId
- || mHTML5VideoView.surfaceTextureDeleted()) {
- // Here, we handle the case when switching to a new video,
- // either inside a WebView or across WebViews
- // For switching videos within a WebView or across the WebView,
- // we need to pause the old one and re-create a new media player
- // inside the HTML5VideoView.
- if (mHTML5VideoView != null) {
- if (!backFromFullScreenMode) {
- mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
- }
- mHTML5VideoView.reset();
- }
- mCurrentProxy = proxy;
- mHTML5VideoView = new HTML5VideoInline(videoLayerId, time);
+ public void load(String url) {
+ if (ensureHTML5VideoView(url, 0, false)) {
+ mHTML5VideoView.prepareDataAndDisplayMode();
+ }
+ }
- mHTML5VideoView.setVideoURI(url, mCurrentProxy);
- mHTML5VideoView.prepareDataAndDisplayMode(proxy);
- } else if (mCurrentProxy == proxy) {
+ public void play(String url, int time) {
+ if (ensureHTML5VideoView(url, time, true)) {
+ mHTML5VideoView.prepareDataAndDisplayMode();
+ mHTML5VideoView.seekTo(time);
+ } else {
// Here, we handle the case when we keep playing with one video
if (!mHTML5VideoView.isPlaying()) {
- mHTML5VideoView.seekTo(time);
mHTML5VideoView.start();
+ setBaseLayer(0);
}
- } else if (mCurrentProxy != null) {
- // Some other video is already playing. Notify the caller that
- // its playback ended.
- proxy.dispatchOnEnded();
}
}
- public static boolean isPlaying(HTML5VideoViewProxy proxy) {
- return (mCurrentProxy == proxy && mHTML5VideoView != null
- && mHTML5VideoView.isPlaying());
+ public boolean isPlaying() {
+ return (mHTML5VideoView != null && mHTML5VideoView.isPlaying());
}
- public static int getCurrentPosition() {
+ public int getCurrentPosition() {
int currentPosMs = 0;
if (mHTML5VideoView != null) {
currentPosMs = mHTML5VideoView.getCurrentPosition();
@@ -262,43 +221,69 @@ public static int getCurrentPosition() {
return currentPosMs;
}
- public static void seek(int time, HTML5VideoViewProxy proxy) {
- if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) {
+ public void seek(int time) {
+ if (time >= 0 && mHTML5VideoView != null) {
mHTML5VideoView.seekTo(time);
}
}
- public static void pause(HTML5VideoViewProxy proxy) {
- if (mCurrentProxy == proxy && mHTML5VideoView != null) {
+ public void pause() {
+ if (mHTML5VideoView != null) {
mHTML5VideoView.pause();
}
}
- public static void onPrepared() {
- if (!mHTML5VideoView.isFullScreenMode()) {
- mHTML5VideoView.start();
- }
- if (mBaseLayer != 0) {
- setBaseLayer(mBaseLayer);
+ public void onPrepared() {
+ if (mCachedVolume >= 0.0f) {
+ mHTML5VideoView.setVolume(mCachedVolume);
+ mCachedVolume = -1.0f;
}
+ setBaseLayer(0);
}
- public static void end() {
- mHTML5VideoView.showControllerInFullScreen();
- if (mCurrentProxy != null) {
+ public void end() {
+ if (mHTML5VideoView != null)
+ mHTML5VideoView.showControllerInFullscreen();
+ if (mProxy != null) {
if (isVideoSelfEnded)
- mCurrentProxy.dispatchOnEnded();
+ mProxy.dispatchOnEnded();
else
- mCurrentProxy.dispatchOnPaused();
+ mProxy.dispatchOnPaused();
}
isVideoSelfEnded = false;
}
+
+ public void setVolume(float volume) {
+ if (mHTML5VideoView != null) {
+ mHTML5VideoView.setVolume(volume);
+ mCachedVolume = -1.0f;
+ } else {
+ mCachedVolume = volume;
+ }
+ }
+
+ // Return true if we have to allocate a new HTML5VideoView.
+ // Otherwise return false and we can reuse the previously allocated HTML5VideoView
+ private boolean ensureHTML5VideoView(String url, int time, boolean willPlay) {
+ if (mHTML5VideoView == null) {
+ mHTML5VideoView = new HTML5VideoView(mProxy, time);
+ mHTML5VideoView.setStartWhenPrepared(willPlay);
+ mHTML5VideoView.setVideoURI(url);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isPrepared() {
+ return mHTML5VideoView.getCurrentState() >= HTML5VideoView.STATE_PREPARED;
+ }
}
+ private VideoPlayer mVideoPlayer;
// A bunch event listeners for our VideoView
// MediaPlayer.OnPreparedListener
public void onPrepared(MediaPlayer mp) {
- VideoPlayer.onPrepared();
+ mVideoPlayer.onPrepared();
Message msg = Message.obtain(mWebCoreHandler, PREPARED);
Map map = new HashMap();
map.put("dur", new Integer(mp.getDuration()));
@@ -308,6 +293,20 @@ public void onPrepared(MediaPlayer mp) {
mWebCoreHandler.sendMessage(msg);
}
+ //MediaPlayer.OnVideoSizeChangedListener
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ Message msg = Message.obtain(mWebCoreHandler, SIZE_CHANGED);
+ Map map = new HashMap();
+ if (mVideoPlayer.isPrepared())
+ map.put("dur", new Integer(mp.getDuration()));
+ else
+ map.put("dur", new Integer(0));
+ map.put("width", new Integer(width));
+ map.put("height", new Integer(height));
+ msg.obj = map;
+ mWebCoreHandler.sendMessage(msg);
+ }
+
// MediaPlayer.OnCompletionListener;
public void onCompletion(MediaPlayer mp) {
// The video ended by itself, so we need to
@@ -333,13 +332,23 @@ public void dispatchOnPaused() {
mWebCoreHandler.sendMessage(msg);
}
- public void dispatchOnStopFullScreen() {
+ public void dispatchOnPlaying() {
+ Message msg = Message.obtain(mWebCoreHandler, PLAYING);
+ mWebCoreHandler.sendMessage(msg);
+ }
+
+ public void dispatchOnStopFullscreen() {
Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN);
mWebCoreHandler.sendMessage(msg);
}
- public void dispatchOnRestoreState() {
- Message msg = Message.obtain(mWebCoreHandler, RESTORESTATE);
+ public void updateSizeAndDuration(int width, int height, int duration) {
+ Message msg = Message.obtain(mWebCoreHandler, SIZE_CHANGED);
+ Map map = new HashMap();
+ map.put("dur", new Integer(duration));
+ map.put("width", new Integer(width));
+ map.put("height", new Integer(height));
+ msg.obj = map;
mWebCoreHandler.sendMessage(msg);
}
@@ -347,14 +356,6 @@ public void onTimeupdate() {
sendMessage(obtainMessage(TIMEUPDATE));
}
- // When there is a frame ready from surface texture, we should tell WebView
- // to refresh.
- @Override
- public void onFrameAvailable(SurfaceTexture surfaceTexture) {
- // TODO: This should support partial invalidation too.
- mWebView.invalidate();
- }
-
// Handler for the messages from WebCore or Timer thread to the UI thread.
@Override
public void handleMessage(Message msg) {
@@ -362,27 +363,34 @@ public void handleMessage(Message msg) {
switch (msg.what) {
case PLAY: {
String url = (String) msg.obj;
- WebChromeClient client = mWebView.getWebChromeClient();
- int videoLayerID = msg.arg1;
- if (client != null) {
- VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID);
- }
+ int seekPosition = msg.arg1;
+ mVideoPlayer.play(url, seekPosition);
+ break;
+ }
+ case LOAD_METADATA: {
+ String url = (String) msg.obj;
+ mVideoPlayer.loadMetadata(url);
+ break;
+ }
+ case LOAD: {
+ String url = (String) msg.obj;
+ mVideoPlayer.load(url);
break;
}
case SEEK: {
Integer time = (Integer) msg.obj;
mSeekPosition = time;
- VideoPlayer.seek(mSeekPosition, this);
+ mVideoPlayer.seek(mSeekPosition);
break;
}
case PAUSE: {
- VideoPlayer.pause(this);
+ mVideoPlayer.pause();
break;
}
case ENDED:
if (msg.arg1 == 1)
- VideoPlayer.isVideoSelfEnded = true;
- VideoPlayer.end();
+ mVideoPlayer.isVideoSelfEnded = true;
+ mVideoPlayer.end();
break;
case ERROR: {
WebChromeClient client = mWebView.getWebChromeClient();
@@ -399,17 +407,46 @@ public void handleMessage(Message msg) {
break;
}
case TIMEUPDATE: {
- if (VideoPlayer.isPlaying(this)) {
+ if (mVideoPlayer.isPlaying()) {
sendTimeupdate();
}
break;
}
case BUFFERING_START: {
- VideoPlayer.setPlayerBuffering(true);
+ mVideoPlayer.setPlayerBuffering(true);
break;
}
case BUFFERING_END: {
- VideoPlayer.setPlayerBuffering(false);
+ mVideoPlayer.setPlayerBuffering(false);
+ break;
+ }
+ case INIT: {
+ // Pass Proxy into webview, such that every time we have a setBaseLayer
+ // call, we tell this Proxy to call the native to update the layer tree
+ // for the Video Layer's surface texture info
+ mWebView.registerHTML5VideoViewProxy(this);
+ break;
+ }
+ case TERM: {
+ mVideoPlayer.suspend();
+ mWebView.unregisterHTML5VideoViewProxy(this);
+ break;
+ }
+ case SET_VOLUME: {
+ float vol = ((Float)msg.obj).floatValue();
+ mVideoPlayer.setVolume(vol);
+ break;
+ }
+ case ENTER_FULLSCREEN: {
+ InlineVideoInfo info = (InlineVideoInfo)msg.obj;
+ mVideoPlayer.enterFullscreenVideo(info.getUrl(),
+ info.getX(), info.getY(), info.getWidth(), info.getHeight());
+ break;
+ }
+ case EXIT_FULLSCREEN: {
+ InlineVideoInfo info = (InlineVideoInfo)msg.obj;
+ mVideoPlayer.exitFullscreenVideo(info.getX(), info.getY(),
+ info.getWidth(), info.getHeight());
break;
}
}
@@ -569,19 +606,21 @@ private void releaseQueue() {
* @param webView is the WebView that hosts the video.
* @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
*/
- private HTML5VideoViewProxy(WebViewClassic webView, int nativePtr) {
+ private HTML5VideoViewProxy(WebViewClassic webView, int nativePtr, int videoLayerId) {
// This handler is for the main (UI) thread.
super(Looper.getMainLooper());
// Save the WebView object.
mWebView = webView;
- // Pass Proxy into webview, such that every time we have a setBaseLayer
- // call, we tell this Proxy to call the native to update the layer tree
- // for the Video Layer's surface texture info
- mWebView.setHTML5VideoViewProxy(this);
// Save the native ptr
mNativePointer = nativePtr;
+ // Save the videoLayerId. This is needed early in order to support fullscreen mode
+ // before video playback
+ mVideoLayerId = videoLayerId;
// create the message handler for this thread
createWebCoreHandler();
+ mVideoPlayer = new VideoPlayer(this);
+ Message message = obtainMessage(INIT);
+ sendMessage(message);
}
private void createWebCoreHandler() {
@@ -598,6 +637,15 @@ public void handleMessage(Message msg) {
height.intValue(), mNativePointer);
break;
}
+ case SIZE_CHANGED: {
+ Map map = (Map) msg.obj;
+ Integer duration = (Integer) map.get("dur");
+ Integer width = (Integer) map.get("width");
+ Integer height = (Integer) map.get("height");
+ nativeOnSizeChanged(duration.intValue(), width.intValue(),
+ height.intValue(), mNativePointer);
+ break;
+ }
case ENDED:
mSeekPosition = 0;
nativeOnEnded(mNativePointer);
@@ -605,6 +653,9 @@ public void handleMessage(Message msg) {
case PAUSED:
nativeOnPaused(mNativePointer);
break;
+ case PLAYING:
+ nativeOnPlaying(mNativePointer);
+ break;
case POSTER_FETCHED:
Bitmap poster = (Bitmap) msg.obj;
nativeOnPosterFetched(poster, mNativePointer);
@@ -615,9 +666,6 @@ public void handleMessage(Message msg) {
case STOPFULLSCREEN:
nativeOnStopFullscreen(mNativePointer);
break;
- case RESTORESTATE:
- nativeOnRestoreState(mNativePointer);
- break;
}
}
};
@@ -636,7 +684,7 @@ private void doSetPoster(Bitmap poster) {
private void sendTimeupdate() {
Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
- msg.arg1 = VideoPlayer.getCurrentPosition();
+ msg.arg1 = mVideoPlayer.getCurrentPosition();
mWebCoreHandler.sendMessage(msg);
}
@@ -649,16 +697,38 @@ public Context getContext() {
* Play a video stream.
* @param url is the URL of the video stream.
*/
- public void play(String url, int position, int videoLayerID) {
+ public void play(String url, int position) {
if (url == null) {
return;
}
+ Message message = obtainMessage(PLAY);
+ message.arg1 = position;
+ message.obj = url;
+ sendMessage(message);
+ }
- if (position > 0) {
- seek(position);
+ /**
+ * Load a video stream.
+ * @param url is the URL of the video stream.
+ */
+ public void loadVideo(String url) {
+ if (url == null) {
+ return;
}
- Message message = obtainMessage(PLAY);
- message.arg1 = videoLayerID;
+ Message message = obtainMessage(LOAD);
+ message.obj = url;
+ sendMessage(message);
+ }
+
+ /**
+ * Load video metadata.
+ * @param url is the URL of the video stream.
+ */
+ public void loadMetadata(String url) {
+ if (url == null) {
+ return;
+ }
+ Message message = obtainMessage(LOAD_METADATA);
message.obj = url;
sendMessage(message);
}
@@ -690,6 +760,8 @@ public void teardown() {
if (mPosterDownloader != null) {
mPosterDownloader.cancelAndReleaseQueue();
}
+ Message message = obtainMessage(TERM);
+ sendMessage(message);
mNativePointer = 0;
}
@@ -712,21 +784,94 @@ public void loadPoster(String url) {
mPosterDownloader.start();
}
- // These three function are called from UI thread only by WebView.
+ public void enterFullscreen(String url, float x, float y, float w, float h) {
+ if (url == null)
+ return;
+ Message message = obtainMessage(ENTER_FULLSCREEN);
+ message.obj = new InlineVideoInfo(url, x, y, w, h);
+ sendMessage(message);
+ }
+
+ public void exitFullscreen(float x, float y, float w, float h) {
+ Message message = obtainMessage(EXIT_FULLSCREEN);
+ message.obj = new InlineVideoInfo(null, x, y, w, h);
+ sendMessage(message);
+ }
+
+ private static final class InlineVideoInfo {
+ private String mUrl;
+ private float mX;
+ private float mY;
+ private float mWidth;
+ private float mHeight;
+
+ public InlineVideoInfo(String url, float x, float y, float w, float h) {
+ mUrl = url;
+ mX = x;
+ mY = y;
+ mWidth = w;
+ mHeight = h;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public float getX() {
+ return mX;
+ }
+
+ public float getY() {
+ return mY;
+ }
+
+ public float getWidth() {
+ return mWidth;
+ }
+
+ public float getHeight() {
+ return mHeight;
+ }
+ }
+
+ // These functions are called from UI thread only by WebView.
public void setBaseLayer(int layer) {
- VideoPlayer.setBaseLayer(layer);
+ mVideoPlayer.setBaseLayer(layer);
}
public void pauseAndDispatch() {
- VideoPlayer.pauseAndDispatch();
+ // mVideoPlayer.pause will always dispatch notification
+ mVideoPlayer.pause();
+ }
+
+ public void suspend() {
+ mVideoPlayer.suspend();
}
- public void enterFullScreenVideo(int layerId, String url) {
- VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
+ public void webkitEnterFullscreen() {
+ nativePrepareEnterFullscreen(mNativePointer);
}
- public void exitFullScreenVideo() {
- VideoPlayer.exitFullScreenVideo(this, mWebView);
+ public void prepareExitFullscreen() {
+ nativePrepareExitFullscreen(mNativePointer);
+ }
+
+ public void webKitExitFullscreen() {
+ mVideoPlayer.webkitExitFullscreenVideo();
+ }
+
+ public int getVideoLayerId() {
+ return mVideoLayerId;
+ }
+ // End functions called from UI thread only by WebView
+
+ /**
+ * Change the volume of the playback
+ */
+ public void setVolume(float volume) {
+ Message message = obtainMessage(SET_VOLUME);
+ message.obj = new Float(volume);
+ sendMessage(message);
}
/**
@@ -735,8 +880,8 @@ public void exitFullScreenVideo() {
*
* @return a new HTML5VideoViewProxy object.
*/
- public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
- return new HTML5VideoViewProxy(webViewCore.getWebViewClassic(), nativePtr);
+ public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr, int videoLayerId) {
+ return new HTML5VideoViewProxy(webViewCore.getWebViewClassic(), nativePtr, videoLayerId);
}
/* package */ WebViewClassic getWebView() {
@@ -744,15 +889,18 @@ public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativ
}
private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
+ private native void nativeOnSizeChanged(int duration, int width, int height, int nativePointer);
private native void nativeOnEnded(int nativePointer);
private native void nativeOnPaused(int nativePointer);
+ private native void nativeOnPlaying(int nativePointer);
private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
private native void nativeOnTimeupdate(int position, int nativePointer);
private native void nativeOnStopFullscreen(int nativePointer);
- private native void nativeOnRestoreState(int nativePointer);
private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
int baseLayer, int videoLayerId, int textureName,
- int playerState);
+ int playerState, int nativePointer);
+ private native void nativePrepareEnterFullscreen(int nativePointer);
+ private native void nativePrepareExitFullscreen(int nativePoint);
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index f9f5b033274..6618fa4942c 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -21,7 +21,9 @@
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.provider.Browser;
+import android.util.Patterns;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
@@ -30,6 +32,7 @@ class SelectActionModeCallback implements ActionMode.Callback {
private WebViewClassic mWebView;
private ActionMode mActionMode;
private boolean mIsTextSelected = true;
+ private Menu mMenu;
void setWebView(WebViewClassic webView) {
mWebView = webView;
@@ -52,7 +55,7 @@ void finish() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_copy, menu);
-
+ mMenu = menu;
final Context context = mWebView.getContext();
mode.setTitle(context.getString(com.android.internal.R.string.textSelectionCABTitle));
mode.setTitleOptionalHint(true);
@@ -76,10 +79,16 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
setMenuVisibility(menu, canCut, com.android.internal.R.id.cut);
setMenuVisibility(menu, canCopy, com.android.internal.R.id.copy);
setMenuVisibility(menu, canWebSearch, com.android.internal.R.id.websearch);
+ setOpenUrlVisibility();
mActionMode = mode;
return true;
}
+ protected void setOpenUrlVisibility() {
+ boolean isUrl = Patterns.WEB_URL.matcher(mWebView.getSelection()).matches();
+ setMenuVisibility(mMenu, isUrl, com.android.internal.R.id.openurl);
+ }
+
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
@@ -128,7 +137,13 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
}
mWebView.getContext().startActivity(i);
break;
-
+ case com.android.internal.R.id.openurl:
+ String url = mWebView.getSelection();
+ if (!url.startsWith("https://") && !url.startsWith("http://")){
+ url = "http://" + url;
+ }
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ mWebView.getContext().startActivity(browserIntent);
default:
return false;
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index fa3cb2047a0..a2231ebcbb6 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -750,6 +750,17 @@ public synchronized int getDefaultFixedFontSize() {
throw new MustOverrideException();
}
+ /**
+ * Sets whether the WebView should preload media resources.
+ *
+ * @param flag whether the WebView should preload media resources.
+ *
+ * @hide
+ */
+ public synchronized void setMediaPreloadEnabled(boolean flag) {
+ throw new MustOverrideException();
+ }
+
/**
* Sets whether the WebView should load image resources. Note that this method
* controls loading of all images, including those embedded using the data
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index 1288613a391..3c8db1807e6 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 The Android Open Source Project
+ * Copyright (c) 2011, 2012, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -86,6 +87,7 @@ public class WebSettingsClassic extends WebSettings {
private long mMaximumDecodedImageSize = 0; // 0 means default
private boolean mPrivateBrowsingEnabled = false;
private boolean mSyntheticLinksEnabled = true;
+ private boolean mMediaPreloadEnabled = true;
// HTML5 API flags
private boolean mAppCacheEnabled = false;
private boolean mDatabaseEnabled = false;
@@ -124,6 +126,7 @@ public class WebSettingsClassic extends WebSettings {
private boolean mEnableSmoothTransition = false;
private boolean mForceUserScalable = false;
private boolean mPasswordEchoEnabled = true;
+ private boolean mWebGLEnabled = true;
// AutoFill Profile data
public static class AutoFillProfile {
@@ -1245,7 +1248,7 @@ public synchronized void setAppCacheEnabled(boolean flag) {
@Override
public synchronized void setAppCachePath(String path) {
// We test for a valid path and for repeated setting on the native
- // side, but we can avoid syncing in some simple cases.
+ // side, but we can avoid syncing in some simple cases.
if (mAppCachePath == null && path != null && !path.isEmpty()) {
mAppCachePath = path;
postSync();
@@ -1621,6 +1624,24 @@ public boolean forceUserScalable() {
return mForceUserScalable;
}
+ /**
+ * @hide
+ */
+ public synchronized boolean isWebGLAvailable() {
+ return nativeIsWebGLAvailable();
+ }
+
+ /**
+ * Sets whether WebGL is enabled.
+ * @param flag Set to true to enable WebGL.
+ */
+ public synchronized void setWebGLEnabled(boolean flag) {
+ if (mWebGLEnabled != flag) {
+ mWebGLEnabled = flag;
+ postSync();
+ }
+ }
+
/**
* Sets whether viewport metatag can disable zooming.
* @param flag Whether or not to forceably enable user scalable.
@@ -1636,6 +1657,13 @@ synchronized void setSyntheticLinksEnabled(boolean flag) {
}
}
+ public synchronized void setMediaPreloadEnabled(boolean flag) {
+ if (mMediaPreloadEnabled != flag) {
+ mMediaPreloadEnabled = flag;
+ postSync();
+ }
+ }
+
public synchronized void setAutoFillEnabled(boolean enabled) {
// AutoFill is always disabled in private browsing mode.
boolean autoFillEnabled = enabled && !mPrivateBrowsingEnabled;
@@ -1733,4 +1761,5 @@ private synchronized void postSync() {
// Synchronize the native and java settings.
private native void nativeSync(int nativeFrame);
+ private native boolean nativeIsWebGLAvailable();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 119fcd37f84..e4936532a0b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -65,8 +65,8 @@
* href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code }
* element.
*
- * See the Web View
- * tutorial.
+ * For more information, read
+ * Building Web Apps in WebView.
*
* Basic usage
*
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 84a6129e8c4..17565927001 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 The Android Open Source Project
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -686,6 +687,10 @@ protected void measureContent() {
// It's used to dismiss the dialog in destroy if not done before.
private AlertDialog mListBoxDialog = null;
+ // Reference to the save password dialog so it can be dimissed in
+ // destroy if not done before.
+ private AlertDialog mSavePasswordDialog = null;
+
static final String LOGTAG = "webview";
private ZoomManager mZoomManager;
@@ -734,13 +739,6 @@ public void onTrimMemory(int level) {
if (DebugFlags.WEB_VIEW) {
Log.d("WebView", "onTrimMemory: " + level);
}
- // When framework reset EGL context during high memory pressure, all
- // the existing GL resources for the html5 video will be destroyed
- // at native side.
- // Here we just need to clean up the Surface Texture which is static.
- if (level >= TRIM_MEMORY_UI_HIDDEN) {
- HTML5VideoInline.cleanupSurfaceTexture();
- }
WebViewClassic.nativeOnTrimMemory(level);
}
@@ -975,9 +973,9 @@ public void onTrimMemory(int level) {
private int mTouchHighlightY;
private boolean mShowTapHighlight;
- // Basically this proxy is used to tell the Video to update layer tree at
+ // The HTML5VideoViewManager is used to tell the Video to update layer tree at
// SetBaseLayer time and to pause when WebView paused.
- private HTML5VideoViewProxy mHTML5VideoViewProxy;
+ private HTML5VideoViewManager mHTML5VideoViewManager;
// If we are using a set picture, don't send view updates to webkit
private boolean mBlockWebkitViewMessages = false;
@@ -1336,20 +1334,40 @@ private void onHandleUiEvent(MotionEvent event, int eventType, int flags) {
private void onHandleUiTouchEvent(MotionEvent ev) {
final ScaleGestureDetector detector =
- mZoomManager.getMultiTouchGestureDetector();
+ mZoomManager.getScaleGestureDetector();
- float x = ev.getX();
- float y = ev.getY();
+ int action = ev.getActionMasked();
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ float x = sumX / div;
+ float y = sumY / div;
+
+ if (configChanged) {
+ mLastTouchX = Math.round(x);
+ mLastTouchY = Math.round(y);
+ mLastTouchTime = ev.getEventTime();
+ mWebView.cancelLongPress();
+ mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+ }
if (detector != null) {
detector.onTouchEvent(ev);
if (detector.isInProgress()) {
mLastTouchTime = ev.getEventTime();
- x = detector.getFocusX();
- y = detector.getFocusY();
- mWebView.cancelLongPress();
- mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
if (!mZoomManager.supportsPanDuringZoom()) {
return;
}
@@ -1360,14 +1378,9 @@ private void onHandleUiTouchEvent(MotionEvent ev) {
}
}
- int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
cancelTouch();
action = MotionEvent.ACTION_DOWN;
- } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() >= 2) {
- // set mLastTouchX/Y to the remaining points for multi-touch.
- mLastTouchX = Math.round(x);
- mLastTouchY = Math.round(y);
} else if (action == MotionEvent.ACTION_MOVE) {
// negative x or y indicate it is on the edge, skip it.
if (x < 0 || y < 0) {
@@ -1655,7 +1668,7 @@ private void init() {
// Initially use a size of two, since the user is likely to only hold
// down two keys at a time (shift + another key)
mKeysPressed = new Vector(2);
- mHTML5VideoViewProxy = null ;
+ mHTML5VideoViewManager = null;
}
@Override
@@ -1811,7 +1824,7 @@ public void setOverScrollMode(int mode) {
neverRemember.getData().putString("password", password);
neverRemember.obj = resumeMsg;
- new AlertDialog.Builder(mContext)
+ mSavePasswordDialog = new AlertDialog.Builder(mContext)
.setTitle(com.android.internal.R.string.save_password_label)
.setMessage(com.android.internal.R.string.save_password_message)
.setPositiveButton(com.android.internal.R.string.save_password_notnow,
@@ -1822,6 +1835,7 @@ public void onClick(DialogInterface dialog, int which) {
resumeMsg.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setNeutralButton(com.android.internal.R.string.save_password_remember,
@@ -1832,6 +1846,7 @@ public void onClick(DialogInterface dialog, int which) {
remember.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setNegativeButton(com.android.internal.R.string.save_password_never,
@@ -1842,6 +1857,7 @@ public void onClick(DialogInterface dialog, int which) {
neverRemember.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setOnCancelListener(new OnCancelListener() {
@@ -1851,6 +1867,7 @@ public void onCancel(DialogInterface dialog) {
resumeMsg.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
}).show();
// Return true so that WebViewCore will pause while the dialog is
@@ -2090,6 +2107,10 @@ private void destroyJava() {
mListBoxDialog.dismiss();
mListBoxDialog = null;
}
+ if (mSavePasswordDialog != null) {
+ mSavePasswordDialog.dismiss();
+ mSavePasswordDialog = null;
+ }
if (mWebViewCore != null) {
// Tell WebViewCore to destroy itself
synchronized (this) {
@@ -2470,6 +2491,8 @@ public void loadUrl(String url, Map additionalHttpHeaders) {
private void loadUrlImpl(String url, Map extraHeaders) {
switchOutDrawHistory();
+ if (mHTML5VideoViewManager != null)
+ mHTML5VideoViewManager.suspend();
WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
arg.mUrl = url;
arg.mExtraHeaders = extraHeaders;
@@ -2603,6 +2626,8 @@ public void stopLoading() {
public void reload() {
clearHelpers();
switchOutDrawHistory();
+ if (mHTML5VideoViewManager != null)
+ mHTML5VideoViewManager.suspend();
mWebViewCore.sendMessage(EventHub.RELOAD);
}
@@ -2682,6 +2707,8 @@ private void goBackOrForwardImpl(int steps) {
private void goBackOrForward(int steps, boolean ignoreSnapshot) {
if (steps != 0) {
+ if (mHTML5VideoViewManager != null)
+ mHTML5VideoViewManager.suspend();
clearHelpers();
mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
ignoreSnapshot ? 1 : 0);
@@ -3430,8 +3457,8 @@ public void onPause() {
mWebViewCore.sendMessage(EventHub.ON_PAUSE);
// We want to pause the current playing video when switching out
// from the current WebView/tab.
- if (mHTML5VideoViewProxy != null) {
- mHTML5VideoViewProxy.pauseAndDispatch();
+ if (mHTML5VideoViewManager != null) {
+ mHTML5VideoViewManager.pauseAndDispatch();
}
if (mNativeClass != 0) {
nativeSetPauseDrawing(mNativeClass, true);
@@ -4345,7 +4372,7 @@ public boolean performLongClick() {
// A multi-finger gesture can look like a long press; make sure we don't take
// long press actions if we're scaling.
- final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
if (detector != null && detector.isInProgress()) {
return false;
}
@@ -4432,8 +4459,8 @@ void setBaseLayer(int layer, boolean showVisualIndicator,
mWebViewCore.resumeWebKitDraw();
}
- if (mHTML5VideoViewProxy != null) {
- mHTML5VideoViewProxy.setBaseLayer(layer);
+ if (mHTML5VideoViewManager != null) {
+ mHTML5VideoViewManager.setBaseLayer(layer);
}
}
@@ -4513,11 +4540,11 @@ private void endSelectingText() {
private void ensureSelectionHandles() {
if (mSelectHandleCenter == null) {
mSelectHandleCenter = mContext.getResources().getDrawable(
- com.android.internal.R.drawable.text_select_handle_middle);
+ com.android.internal.R.drawable.text_select_handle_middle).mutate();
mSelectHandleLeft = mContext.getResources().getDrawable(
- com.android.internal.R.drawable.text_select_handle_left);
+ com.android.internal.R.drawable.text_select_handle_left).mutate();
mSelectHandleRight = mContext.getResources().getDrawable(
- com.android.internal.R.drawable.text_select_handle_right);
+ com.android.internal.R.drawable.text_select_handle_right).mutate();
mHandleAlpha.setAlpha(mHandleAlpha.getAlpha());
mSelectHandleCenterOffset = new Point(0,
-mSelectHandleCenter.getIntrinsicHeight());
@@ -5752,7 +5779,7 @@ private float calculateDragAngle(int dx, int dy) {
* and the middle point for multi-touch.
*/
private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
- ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
long eventTime = event.getEventTime();
@@ -7294,14 +7321,14 @@ public void handleMessage(Message msg) {
int layerId = msg.arg1;
String url = (String) msg.obj;
- if (mHTML5VideoViewProxy != null) {
- mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url);
+ if (mHTML5VideoViewManager != null) {
+ mHTML5VideoViewManager.enterFullscreenVideo(layerId, url);
}
break;
case EXIT_FULLSCREEN_VIDEO:
- if (mHTML5VideoViewProxy != null) {
- mHTML5VideoViewProxy.exitFullScreenVideo();
+ if (mHTML5VideoViewManager != null) {
+ mHTML5VideoViewManager.exitFullscreenVideo();
}
break;
@@ -7915,6 +7942,9 @@ private void updateTextSelectionFromMessage(int nodePointer,
syncSelectionCursors();
} else {
adjustSelectionCursors();
+ if (mSelectCallback != null) {
+ mSelectCallback.setOpenUrlVisibility();
+ }
}
if (mIsCaretSelection) {
resetCaretTimer();
@@ -8389,8 +8419,21 @@ public void debugDump() {
*
* only used by the Browser
*/
- public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
- mHTML5VideoViewProxy = proxy;
+ public void registerHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
+ if (mHTML5VideoViewManager == null)
+ mHTML5VideoViewManager = new HTML5VideoViewManager(this);
+ mHTML5VideoViewManager.registerProxy(proxy);
+ }
+
+ /**
+ * Clean up method for registerHTML5VideoViewProxy
+ *
+ * @hide only used by the Browser
+ */
+ public void unregisterHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
+ if (mHTML5VideoViewManager != null) {
+ mHTML5VideoViewManager.unregisterProxy(proxy);
+ }
}
/**
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 728ddbfc9fe..77669238639 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2545,8 +2545,12 @@ private void setupViewport(boolean updateViewState) {
// adjust the default scale to match the densityDpi
float adjust = 1.0f;
if (mViewportDensityDpi == -1) {
- adjust = mContext.getResources().getDisplayMetrics().density;
- } else if (mViewportDensityDpi > 0) {
+ if( mWebViewClassic != null && mWebViewClassic.getDefaultZoomScale() * mSettings.getDefaultZoom().value != mContext.getResources().getDisplayMetrics().density * 100)
+ mWebViewClassic.adjustDefaultZoomDensity( mSettings.getDefaultZoom().value);
+ if( mWebViewClassic != null && (int)( mWebViewClassic.getDefaultZoomScale() * 100) != 100) {
+ adjust = mWebViewClassic.getDefaultZoomScale();
+ }
+ } else if (mViewportDensityDpi > 0) {
adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
/ mViewportDensityDpi;
}
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 88301191c65..80a67826e1d 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -204,7 +204,7 @@ class ZoomManager {
*/
private boolean mAllowPanAndScale;
- // use the framework's ScaleGestureDetector to handle multi-touch
+ // use the framework's ScaleGestureDetector to handle scaling gestures
private ScaleGestureDetector mScaleDetector;
private boolean mPinchToZoomAnimating = false;
@@ -768,7 +768,7 @@ public boolean isPreventingWebkitUpdates() {
return isZoomAnimating();
}
- public ScaleGestureDetector getMultiTouchGestureDetector() {
+ public ScaleGestureDetector getScaleGestureDetector() {
return mScaleDetector;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 19aef8eaf05..8ce393bef6a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,8 +16,6 @@
package android.widget;
-import com.android.internal.R;
-
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -69,6 +67,8 @@
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.R;
+
import java.util.ArrayList;
import java.util.List;
@@ -581,6 +581,7 @@ public abstract class AbsListView extends AdapterView implements Te
Runnable mPositionScrollAfterLayout;
private int mMinimumVelocity;
private int mMaximumVelocity;
+ private int mDecacheThreshold;
private float mVelocityScale = 1.0f;
final boolean[] mIsScrap = new boolean[1];
@@ -810,6 +811,7 @@ private void initAbsListView() {
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mDecacheThreshold = mMaximumVelocity / 2;
mOverscrollDistance = configuration.getScaledOverscrollDistance();
mOverflingDistance = configuration.getScaledOverflingDistance();
@@ -1813,6 +1815,10 @@ public Parcelable onSaveInstanceState() {
}
ss.checkedItemCount = mCheckedItemCount;
+ if (mRemoteAdapter != null) {
+ mRemoteAdapter.saveRemoteViewsCache();
+ }
+
return ss;
}
@@ -4027,7 +4033,7 @@ public void run() {
// Keep the fling alive a little longer
postDelayed(this, FLYWHEEL_TIMEOUT);
} else {
- endFling();
+ endFling(false); // Don't disable the scrolling cache right after it was enabled
mTouchMode = TOUCH_MODE_SCROLL;
reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
@@ -4041,6 +4047,11 @@ public void run() {
}
void start(int initialVelocity) {
+ if (Math.abs(initialVelocity) > mDecacheThreshold) {
+ // For long flings, scrolling cache causes stutter, so don't use it
+ clearScrollingCache();
+ }
+
int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0;
mLastFlingY = initialY;
mScroller.setInterpolator(null);
@@ -4113,13 +4124,18 @@ void startScroll(int distance, int duration, boolean linear) {
}
void endFling() {
+ endFling(true);
+ }
+
+ void endFling(boolean clearCache) {
mTouchMode = TOUCH_MODE_REST;
removeCallbacks(this);
removeCallbacks(mCheckFlywheel);
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- clearScrollingCache();
+ if (clearCache)
+ clearScrollingCache();
mScroller.abortAnimation();
if (mFlingStrictSpan != null) {
@@ -5974,6 +5990,9 @@ public void setRemoteViewsAdapter(Intent intent) {
mDeferNotifyDataSetChanged = false;
// Otherwise, create a new RemoteViewsAdapter for binding
mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this);
+ if (mRemoteAdapter.isDataReady()) {
+ setAdapter(mRemoteAdapter);
+ }
}
/**
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index c557963be75..2266cea8a92 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -813,6 +813,9 @@ public SavedState[] newArray(int size) {
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
+ if (mRemoteViewsAdapter != null) {
+ mRemoteViewsAdapter.saveRemoteViewsCache();
+ }
return new SavedState(superState, mWhichChild);
}
@@ -984,6 +987,9 @@ public void setRemoteViewsAdapter(Intent intent) {
mDeferNotifyDataSetChanged = false;
// Otherwise, create a new RemoteViewsAdapter for binding
mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
+ if (mRemoteViewsAdapter.isDataReady()) {
+ setAdapter(mRemoteViewsAdapter);
+ }
}
@Override
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e5199f6fdf2..30dd17d6f74 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -75,8 +75,8 @@
* }
*
*
- * See the Auto Complete
- * tutorial.
+ * See the Text Fields
+ * guide.
*
* @attr ref android.R.styleable#AutoCompleteTextView_completionHint
* @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 99f4cae493c..2ac56ac1c9c 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -83,8 +83,8 @@
* href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">State List
* Drawable.
*
- * Also see the Form Stuff
- * tutorial for an example implementation of a button.
+ * See the Buttons
+ * guide.
*
* XML attributes
*
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 858c415d0f0..f1804f8b97b 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -44,8 +44,8 @@
* }
*
*
- * See the Form Stuff - * tutorial.
+ *See the Checkboxes + * guide.
* *XML attributes
*diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 108b7200f0b..ac3bedb02d4 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -53,8 +53,8 @@ * displayed. Also the minimal and maximal date from which dates to be selected * can be customized. *
- * See the Date - * Picker tutorial. + * See the Pickers + * guide. *
** For a dialog using this view, see {@link android.app.DatePickerDialog}. diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 2fd87682236..57e51c25925 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -38,8 +38,8 @@ * EditText is a thin veneer over TextView that configures itself * to be editable. * - *
See the Form Stuff - * tutorial.
+ *See the Text Fields + * guide.
** XML attributes *
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 89751094072..37e0b905510 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -37,8 +37,8 @@ * A view that shows items in two-dimensional scrolling grid. The items in the * grid come from the {@link ListAdapter} associated with this view. * - *
See the Grid - * View tutorial.
+ *See the Grid + * View guide.
* * @attr ref android.R.styleable#GridView_horizontalSpacing * @attr ref android.R.styleable#GridView_verticalSpacing @@ -2207,8 +2207,13 @@ protected int computeVerticalScrollOffset() { int height = view.getHeight(); if (height > 0) { final int numColumns = mNumColumns; - final int whichRow = mFirstPosition / numColumns; final int rowCount = (mItemCount + numColumns - 1) / numColumns; + // In case of stackFromBottom the calculation of whichRow needs + // to take into account that counting from the top the first row + // might not be entirely filled. + final int oddItemsOnFirstRow = isStackFromBottom() ? ((rowCount * numColumns) - + mItemCount) : 0; + final int whichRow = (mFirstPosition + oddItemsOnFirstRow) / numColumns; return Math.max(whichRow * 100 - (top * 100) / height + (int) ((float) mScrollY / getHeight() * rowCount * 100), 0); } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 18c4fe631af..ff0579cd78f 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; import android.view.FocusFinder; import android.view.InputDevice; import android.view.KeyEvent; @@ -62,6 +63,7 @@ public class HorizontalScrollView extends FrameLayout { private static final float MAX_SCROLL_FACTOR = ScrollView.MAX_SCROLL_FACTOR; + private static final String TAG = "HorizontalScrollView"; private long mLastScroll; @@ -456,6 +458,12 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { } final int pointerIndex = ev.findPointerIndex(activePointerId); + if (pointerIndex == -1) { + Log.e(TAG, "Invalid pointerId=" + activePointerId + + " in onInterceptTouchEvent"); + break; + } + final int x = (int) ev.getX(pointerIndex); final int xDiff = (int) Math.abs(x - mLastMotionX); if (xDiff > mTouchSlop) { @@ -557,6 +565,11 @@ public boolean onTouchEvent(MotionEvent ev) { } case MotionEvent.ACTION_MOVE: final int activePointerIndex = ev.findPointerIndex(mActivePointerId); + if (activePointerIndex == -1) { + Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); + break; + } + final int x = (int) ev.getX(activePointerIndex); int deltaX = mLastMotionX - x; if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) { diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java index 59a8f280463..379354caeb0 100644 --- a/core/java/android/widget/ImageButton.java +++ b/core/java/android/widget/ImageButton.java @@ -64,8 +64,8 @@ * it will only be applied after {@code android:state_pressed} and {@code * android:state_focused} have both evaluated false. * - *See the Form Stuff - * tutorial.
+ *See the Buttons + * guide.
* *XML attributes
*diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 2391898dfa3..09c01295610 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -41,8 +41,8 @@ * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}. * The default orientation is horizontal. * - *
See the Linear Layout - * tutorial.
+ *See the Linear Layout + * guide.
* ** Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams} diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index d2e55d90546..e011c13a9c8 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -57,8 +57,8 @@ * A view that shows items in a vertically scrolling list. The items * come from the {@link ListAdapter} associated with this view. * - *
See the List View - * tutorial.
+ *See the List View + * guide.
* * @attr ref android.R.styleable#ListView_entries * @attr ref android.R.styleable#ListView_divider diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java index 1c72a0d08a0..e94589b6ac5 100644 --- a/core/java/android/widget/OverScroller.java +++ b/core/java/android/widget/OverScroller.java @@ -18,11 +18,13 @@ import android.content.Context; import android.hardware.SensorManager; +import android.os.PowerManager; import android.util.FloatMath; import android.util.Log; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import java.io.*; /** * This class encapsulates scrolling with the ability to overshoot the bounds @@ -42,6 +44,26 @@ public class OverScroller { private static final int DEFAULT_DURATION = 250; private static final int SCROLL_MODE = 0; private static final int FLING_MODE = 1; + private static final String scalingMaxFreqFile = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"; + + private final PowerManager mPm; + + private int maxFreq = readFileIntoInt(scalingMaxFreqFile); + + // Reads file into variable + private int readFileIntoInt(String fileToBeRead) { + try { + File fileObject = new File(fileToBeRead); + Reader readerObject = new FileReader(fileObject); + BufferedReader bufferedReaderObject = new BufferedReader(readerObject); + String stringDataFromFile = bufferedReaderObject.readLine(); + readerObject.close(); + bufferedReaderObject.close(); + return Integer.parseInt(stringDataFromFile.trim()); + } catch (IOException e) { + return 1000000; + } + } /** * Creates an OverScroller with a viscous fluid scroll interpolator and flywheel. @@ -72,9 +94,12 @@ public OverScroller(Context context, Interpolator interpolator) { public OverScroller(Context context, Interpolator interpolator, boolean flywheel) { mInterpolator = interpolator; mFlywheel = flywheel; + mScrollerX = new SplineOverScroller(); mScrollerY = new SplineOverScroller(); + mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + SplineOverScroller.initFromContext(context); } @@ -375,6 +400,7 @@ public void startScroll(int startX, int startY, int dx, int dy) { */ public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; + mPm.cpuBoost(maxFreq); mScrollerX.startScroll(startX, dx, duration); mScrollerY.startScroll(startY, dy, duration); } @@ -445,6 +471,7 @@ public void fling(int startX, int startY, int velocityX, int velocityY, } } + mPm.cpuBoost(maxFreq); mMode = FLING_MODE; mScrollerX.fling(startX, velocityX, minX, maxX, overX); mScrollerY.fling(startY, velocityY, minY, maxY, overY); diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java index b1bb1c06b1a..a0fef7dafc6 100644 --- a/core/java/android/widget/RadioButton.java +++ b/core/java/android/widget/RadioButton.java @@ -38,8 +38,8 @@ * a radio group, checking one radio button unchecks all the others. * * - *See the Form Stuff - * tutorial.
+ *See the Radio Buttons + * guide.
* *XML attributes
*diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java index 524d27271aa..4d3c56c1f12 100644 --- a/core/java/android/widget/RatingBar.java +++ b/core/java/android/widget/RatingBar.java @@ -43,9 +43,6 @@ *
* The secondary progress should not be modified by the client as it is used * internally as the background for a fractionally filled star. - * - *
See the Form Stuff - * tutorial.
* * @attr ref android.R.styleable#RatingBar_numStars * @attr ref android.R.styleable#RatingBar_rating diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 29cf00071e8..569cf993eed 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -56,8 +56,8 @@ * {@link #ALIGN_PARENT_BOTTOM}. * * - *See the Relative - * Layout tutorial.
+ *See the Relative + * Layout guide.
* *
* Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 46ec923a39f..9d853f594d3 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -17,10 +17,10 @@
package android.widget;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
-
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +31,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.MeasureSpec;
@@ -47,7 +48,7 @@
public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
private static final String TAG = "RemoteViewsAdapter";
- // The max number of items in the cache
+ // The max number of items in the cache
private static final int sDefaultCacheSize = 40;
// The delay (in millis) to wait until attempting to unbind from a service after a request.
// This ensures that we don't stay continually bound to the service and that it can be destroyed
@@ -58,7 +59,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
private static final int sDefaultLoadingViewHeight = 50;
// Type defs for controlling different messages across the main and worker message queues
- private static final int sDefaultMessageType = 0;
+ private static final int sDefaultMessageType = 0;
private static final int sUnbindServiceMessageType = 1;
private final Context mContext;
@@ -83,6 +84,26 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
private Handler mWorkerQueue;
private Handler mMainQueue;
+ // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
+ // structures;
+ private static final HashMap See the Spinner
- * tutorial. See the Spinners guide. A spinner does not support item click events. Calling this method
* will raise an exception. Instead use {@link AdapterView#setOnItemSelectedListener}.
*
* @param l this listener will be ignored
*/
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 471f259cc95..56f66510fe1 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -53,6 +53,17 @@
* {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
* the related seSwitchTypeface() methods control that of the thumb.
*
+ * See the Toggle Buttons
+ * guide.
- * See the Time Picker
- * tutorial.
+ * See the Pickers
+ * guide.
* See the Form Stuff
- * tutorial. See the Toggle Buttons
+ * guide. When an operation of interest to the user is taking place over a relatively long period of time,
-provide visual feedback that it's still happening and in the process of being completed. Progress bars and activity indicators signal to users that something is happening that will take a moment. If you know the percentage of the operation that has been completed, use a determinate progress bar
-to give the user a sense of how much longer it will take. Progress bars are for situations where the percentage completed can be determined. They give users a quick sense of how much longer an operation will take. The progress bar should always travel from 0% to 100% completion. Avoid setting the bar to a lower
-value than a previous value, or using the same progress bar to represent the progress of multiple
-events, since doing so makes the display meaningless. If you're not sure how long a particular
-operation will take, use an indeterminate progress indicator. A progress bar should always fill from 0% to 100% and never move backwards to a lower value. If multiple operations are happening in sequence, use the progress bar to represent the delay as a whole, so that when the bar reaches 100%, it doesn't return back to 0%.Progress
+Progress bars
-
-
If you don't know how much longer an operation will continue, use an indeterminate progress -indicator. There are two styles available: a flat bar and a circle. Use the one that best fits the -available space.
+Activity indicators are for operations of an indeterminate length. They ask users to wait a moment while something finishes up, without getting into specifics about what's happening behind the scenes.
+Two styles are available: a bar and a circle. Each is offered in a variety of sizes, in both Holo Light and Holo Dark themes. Choose the appropriate style and size for the surrounding context. For example, the largest activity circle works well when displayed in a blank content area, but not in a smaller dialog box. Each operation should only be represented by one activity indicator.
- -An indeterminate activity bar is used at the start of an application download because the Play Store -app hasn't been able to contact the server yet, and it's not possible to determine how long it will -take for the download to begin. - -
+In this example, an activity bar (in Holo Dark) appears when a user first requests a download. There's an unknown period of time when the download has not yet started. As soon as the download starts, this activity bar transforms into a progress bar.
In this example, an activity circle (in Holo Light) is used in the Gmail application when a message is being loaded because it's not possible to determine how long it will take to download the email.
+When displaying an activity circle, do not include text to communicate what the app is doing. The moving circle alone provides sufficient feedback about the delay, and does so in an understated way that minimizes the impact.
- -An indeterminate activity circle is used in the Gmail application when a message is being -loaded because it's not possible to determine how long it will take to download the email. - +
+
+ You should only use one activity indicator on screen per activity, and it should appropriately sized -for the surrounding context. For example, the largest activity circle works well when displayed in a -blank content area, but not in a smaller dialog box.
+The standard progress bar and activity indicators work well for most situations and should be used whenever possible to provide a consistent experience across Android. However, some situations may call for something more custom.
+ +Here's an example:
+In all of the Google Play apps (Music, Books, Movies, Magazines), we wanted the current download state of each item to be visible at all times at the top-level screen. These states are:
+
We also needed to indicate progress from one download state to another, because downloading is not instantaneous.
+This presented a challenge, because the Google Play apps use a variety of different layouts, and some of them are highly space-constrained. We didn't want this information to clutter the top-level screens, or compete too much with the cover art.
+So we designed a custom indicator that could show all of the information in a tiny footprint, with the flexibility to appear on top of content if necessary.
+ +
+
+The color indicates whether it's downloaded (blue) or not (gray). The appearance of the pin indicates whether the download is permanent (white, upright) or temporary (gray, diagonal). And when state is in the process of changing, progress is indicated by a moving pie chart.
+ +
+ If you find that the standard indicators aren't meeting your needs (due to space constraints, state complexities), by all means design your own. Make it feel like part of the Android family by injecting some of the visual characteristics of the standard indicators. In this example, we carried over the circular shape, the same shade of blue, and the flat and simple style.
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd index 19ed1c3ff13..fe05f80a627 100644 --- a/docs/html/design/building-blocks/tabs.jd +++ b/docs/html/design/building-blocks/tabs.jd @@ -6,6 +6,7 @@ page.title=TabsTabs in the action bar make it easy to explore and switch between different views or functional aspects of your app, or to browse categorized data sets.
+For details on using gestures to move between tabs, see the Swipe Views pattern.
Fixed tabs display all items concurrently. To navigate to a different view, touch the tab.
+Fixed tabs display all items concurrently. To navigate to a different view, touch the tab, or swipe left or right.
+Fixed tabs are displayed with equal width, based on the width of the widest tab label. If there is insufficient room to display all tabs, the tab labels themselves will be scrollable. For this reason, fixed tabs are best suited for displaying 3 or fewer tabs.
- Adobe® Fireworks® PNG Stencil - Omni® OmniGraffle® Stencil - Adobe® Photoshop® Sources + Adobe® Fireworks® PNG Stencil + Adobe® Illustrator® Stencil + Omni® OmniGraffle® Stencil + Adobe® Photoshop® Sources
- Roboto - Specimen Book + Roboto + Specimen Book
Ice Cream Sandwich (Android 4.0) marks a major milestone for Android design. We touched nearly every -pixel of the system as we expanded the new design approaches introduced in Honeycomb tablets to all -types of mobile devices. Starting with the most basic elements, we introduced a new font, Roboto, -designed for high-resolution displays. Other big changes include framework-level action bars on -phones and support for new phones without physical buttons.
-We focused the design work with three overarching goals for our core apps and the system at large. -As you design apps to work with Android, consider these goals:
+We focused the design of Android around three overarching goals, which apply to our core apps as well as the system at large. As you design apps to work with Android, consider these goals:
Most notifications have a one-line title and a one-line message. The recommended layout for a -notification includes two lines. If necessary, you can add a third line. Timestamps are optional.
+Notifications can be expanded to uncover more details and relevant actions. When collapsed, notifications have a one-line title and a one-line message.The recommended layout for a notification includes two lines. If necessary, you can add a third line.
Swiping a notification right or left removes it from the notification drawer.
One of Android's missions is to organize the world's information and make it universally accessible and useful. Accessibility is the measure of how successfully a product can be used by people with varying abilities. Our mission applies to all users-including people with disabilities such as visual impairment, color deficiency, hearing loss, and limited dexterity.
+Universal design is the practice of making products that are inherently accessible to all users, regardless of ability. The Android design patterns were created in accordance with universal design principles, and following them will help your app meet basic usability standards. Adhering to universal design and enabling Android's accessibility tools will make your app as accessible as possible.
+Robust support for accessibility will increase your app's user base. It may also be required for adoption by some organizations.
+Learn more about Google and accessibility.
+ +Android includes several features that support access for users with visual impairments; they don't require drastic visual changes to your app.
+ +Some users use hardware or software directional controllers (such as a D-pad, trackball, keyboard) to jump from selection to selection on a screen. They interact with the structure of your app in a linear fashion, similar to 4-way remote control navigation on a television.
+ +The Android design principle "I should always know where I am" is key for accessibility concerns. As a user navigates through an application, they need feedback and a mental model of where they are. All users benefit from a strong sense of information hierarchy and an architecture that makes sense. Most users benefit from visual and haptic feedback during their navigation (such as labels, colors, icons, touch feedback) Low vision users benefit from explicit verbal descriptions and large visuals with high contrast.
+As you design your app, think about the labels and notations needed to navigate your app by sound. When using Explore by Touch, the user enables an invisible but audible layer of structure in your application. Like any other aspect of app design, this structure can be simple, elegant, and robust. The following are Android's recommended guidelines to enable effective navigation for all users.
+ +Design well-defined, clear task flows with minimal navigation steps, especially for major user tasks. Make sure those tasks are navigable via focus controls.
+ +48 dp is the recommended touch target size for on screen elements. Read about Android Metrics and Grids to learn about implementation strategies to help most of your users. For certain users, it may be appropriate to use larger touch targets. An example of this is educational apps, where buttons larger than the minimum recommendations are appropriate for children with developing motor skills and people with manual dexterity challenges.
+ + +In your wireframes, label functional UI components that have no visible text. Those components might be buttons, icons, tabs with icons, and icons with state (like stars). Developers can use the contentDescription attribute to set the label.
+ Your app may have icons or controls that disappear after a certain amount of time. For example, five seconds after starting a video, playback controls may fade from the screen.
+ +Due to the way that TalkBack works, those controls are not read out loud unless they are focused on. If they fade out from the screen quickly, your user may not even be aware that they are available. Therefore, make sure that you are not relying on timed out controls for high priority task flows. (This is a good universal design guideline too.) If the controls enable an important function, make sure that the user can turn on the controls again and/or their function is duplicated elsewhere. You can also change the behavior of your app when accessibility services are turned on. Your developer may be able to make sure that timed-out controls won't disappear.
+ +Standard Android framework controls work automatically with accessibility services and have ContentDescriptions built in by default.
+ +An oft-overlooked system control is font size. Users can turn on a system-wide large font size in Settings; using the default system font size in your application will enable the user's preferences in your app as well. To enable system font size in your app, mark text and their associated containers to be measured in scale pixels.
+ +Also, keep in mind that when users have large fonts enabled or speak a different language than you, their type might be larger than the space you've allotted for it. Read Devices and Displays and Supporting Multiple Screens for design strategies.
+ +If you use custom controls, Android has the developer tools in place to allow adherence to the above guidelines and provide meaningful descriptions about the UI. Provide adequate notation on your wireframes and direct your developer to the Custom Views documentation.
+ +Turn on the TalkBack service in Settings > Accessibility and navigate your application using directional controls or eyes-free navigation.
+ +
-The action bar is arguably the most important structural element of an Android app. It's a -dedicated piece of real estate at the top of each screen that is generally persistent throughout the -app.
-The main purpose of the action bar is to:
+The action bar is a dedicated piece of real estate at the top of each screen that is generally persistent throughout the app.
+It provides several key functions:
If you're new to writing Android apps, note that the action bar is one of the most important design -elements you can implement. Following the guidelines described here will go a long way toward making -your app's interface consistent with the core Android apps.
+If you're new to writing Android apps, note that the action bar is one of the most important design elements you can implement. Following the guidelines described here will go a long way toward making your app's interface consistent with the core Android apps.
The action bar is split into four different functional areas that apply to most apps.
@@ -66,7 +61,7 @@ content, such as an app title or longer branding information.Show the most important actions of your app in the actions section. Actions that don't fit in the -action bar are moved automatically to the action overflow. +action bar are moved automatically to the action overflow. Long-press on an icon to view the action's name.
@@ -141,29 +136,47 @@ selected data or text.Use CABs whenever you allow the user to select data via long press. You can control the action content of a CAB in order to insert the actions you would like the user to be able to perform.
-For more information, refer to the "Selection" pattern.
-For more information, refer to the Selection +pattern.
+ +If your app displays data in different views, the action bar has three different controls to allow users to switch between them: tabs, spinners, and drawers.
Tabs display app views concurrently and make it easy to explore and switch between them. Use tabs -if you expect your users to switch views frequently.
+Tabs display app views concurrently and make it easy to explore and switch between them. Tabs may be fixed, where all tabs are simultaneously displayed, or may scroll, allowing a larger number of views to be presented.
-There are two types of tabs: fixed and scrollable.
+Use tabs if:
+Fixed tabs are always visible on the screen, and can't be moved out of the way like scrollable +tabs. Fixed tabs in the main action bar can move to the top bar when the screen orientation changes.
+ +Use fixed tabs to support quick changes between two or three app views. Fixed tabs should always allow the user to navigate between the views by swiping left or right on the content area.
+ +
+ Scrollable tabs always take up the entire width of the bar, with the currently active view item in -the center, and therefore need to live in a dedicated bar. Scrollable tabs can themselves be -scrolled horizontally to bring more tabs into view.
-Use scrollable tabs if you have a large number of views or if you're unsure how many views will be -displayed because your app inserts views dynamically (for example, open chats in a messaging app -that the user can navigate between). Scrollable tabs should always allow the user to navigate -between the views by swiping left or right on the content area as well as swiping the tabs -themselves.
+Scrollable tabs always take up the entire width of the bar, with the currently active view item in the center, and therefore need to live in a dedicated bar. Scrollable tabs can themselves be scrolled horizontally to bring more tabs into view.
+Use scrollable tabs if you have a large number of views or if you're unsure how many views will be displayed because your app inserts views dynamically (for example, open chats in a messaging app that the user can navigate between). Scrollable tabs should always allow the user to navigate between the views by swiping left or right on the content area as well as swiping the tabs themselves.
Fixed tabs are always visible on the screen, and can't be moved out of the way like scrollable -tabs. Fixed tabs in the main action bar can move to the top bar when the screen orientation changes.
+A spinner is a drop-down menu that allows users to switch between views of your app.
+Use a spinner in the main action bar if:
+
+
A drawer is a slide-out menu that allows users to switch between views of your app. It can be opened by touching the action bar's app icon (decorated with the Up caret.) Additionally, a drawer can be revealed by an edge swipe from the left of the screen, and dismissed by swiping from the right edge of the drawer. However, because many users will rely on Up navigation to open a drawer, it is only suitable for use at the topmost level of your app's hierarchy.
-A spinner is a drop-down menu that allows users to switch between views of your app.
-Use spinners rather than tabs in the main action bar if:
+Open a drawer from the main action bar if:
-
Action buttons on the action bar surface your app's most important activities. Think about which buttons will get used most often, and order them accordingly. Depending on available screen real estate, the system shows your most important actions as action buttons and moves the rest to the @@ -281,7 +293,7 @@ files for further customization.
-Download the Action Bar Icon Pack +Download the Action Bar Icon Pack
diff --git a/docs/html/design/patterns/app-structure.jd b/docs/html/design/patterns/app-structure.jd index e2398ed1720..a483522fd1d 100644 --- a/docs/html/design/patterns/app-structure.jd +++ b/docs/html/design/patterns/app-structure.jd @@ -185,28 +185,18 @@ collections of items. Then use multi-select to allow application of those action in a category view.The detail view allows you to view and act on your data. The layout of the detail view depends on -the data type being displayed, and therefore differs widely among apps.
+The detail view allows you to view and act on your data. The layout of the detail view depends on the data type being displayed, and therefore differs widely among apps.
Consider the activities people will perform in the detail view and arrange the layout accordingly. -For immersive content, make use of the lights-out mode to allow for distraction-free viewing of -full-screen content.
- -
+Consider the activities people will perform in the detail view and arrange the layout accordingly.
-
Immersive content like media and games is best experienced full screen without distractions. But that doesn't mean you can't also offer actions on the content like sharing, commenting, or searching. If the user hasn't interacted with any of the controls after a short period of time, automatically fade away the action bar and all system UI affordances so the user can lean back and enjoy the content. We call this lights-out mode. Later, if the user wants to take some action, they can touch anywhere on the screen to exit lights-out mode and bring back the controls.
+ +
+ If your users are likely to want to look at multiple items in sequence, allow them to navigate -between items from within the detail view. Use swipe views or other techniques, such as filmstrips, +between items from within the detail view. Use swipe views or other techniques, such as thumbnail view controls, to achieve this.
@@ -229,8 +235,8 @@ to achieve this.
In some situations, when a user invokes an action in your app, it's a good idea to confirm or acknowledge that action through text.
+ +
+ Confirming is asking the user to verify that they truly want to proceed with an action they just invoked. In some cases, the confirmation is presented along with a warning or critical information related to the action that they need to consider.
+
+ Acknowledging is displaying text to let the user know that the action they just invoked has been completed. This removes uncertainty about implicit operations that the system is taking. In some cases, the acknowledgment is presented along with an option to undo the action.
+Communicating to users in these ways can help alleviate uncertainty about things that have happened or will happen. Confirming or acknowledging can also prevent users from making mistakes they might regret.
+ +Not all actions warrant a confirmation or an acknowledgment. Use this flowchart to guide your design decisions.
+
+
+
+ In this example, the user has requested to delete a book from their Google Play library. An alert appears to confirm this action because it's important to understand that the book will no longer be available from any device.
+When crafting a confirmation dialog, make the title meaningful by echoing the requested action.
+
+ Confirmations don't necessarily have to be presented in an alert with two buttons. After initiating Android Beam, the user is prompted to touch the content to be shared (in this example, it's a photo). If they decide not to proceed, they simply move their phone away.
+
+ In this example, if the user navigates back or up from the Gmail compose screen, something possibly unexpected happens: the current draft is automatically saved. An acknowledgment in the form of a toast makes that apparent. It fades after a few seconds.
+Undo isn't appropriate here because saving was initiated by the app, not the user. And it's quick and easy to resume composing the message by navigating to the list of drafts.
+ +
+ After the user deletes a conversation from the list in Gmail, an acknowledgment appears with an undo option. The acknowledgment remains until the user takes an unrelated action, such as scrolling the list.
+
+ Confirmation is unnecessary. If the user +1'd by accident, it's not a big deal. They can just touch the button again to undo the action.
+Acknowledgment is unnecessary. The user will see the +1 button bounce and turn red. That's a very clear signal.
+
+ Confirmation is unnecessary. This is a deliberate action: the user must drag and drop an item onto a relatively large and isolated target. Therefore, accidents are highly unlikely. But if the user regrets the decision, it only takes a few seconds to bring it back again.
+Acknowledgment is unnecessary. The user will know the app is gone from the Home Screen because they made it disappear by dragging it away.
+ +We wish we could guarantee that if you follow every piece of advice on this website, everyone will be able to learn and use your app without a hitch. Sadly, that's not the case.
+ +Some of your users will run into questions or problems along the way. They'll be looking for answers within your app, and if they don't find them quickly, they may leave and never come back.
+ +This page covers design patterns for making help accessible in your app and tips for creating help content for users who are eager for assistance.
+ +Naturally, you want everyone to quickly learn the ropes, discover the cool features, and get the most out of your app. So you might be tempted to present a one-time introductory slideshow, video, or splash screen to all new users when they first open the app. Or you might be drawn to the idea of displaying helpful text bubbles or dialogs when users interact with certain features for the first time.
+In almost all cases, we advise against approaches like these because:
+The only reason for showing pure help content to new users unsolicited is:
+To teach high value functionality that's only available through a gesture.
For example, we use help content to teach users how to place apps on their Home Screen. This functionality is:
+Without it, users wouldn't be able to customize the most frequently visited Android screen to meet their needs.
Because there's no button or menu for it, users might not ever discover it on their own.
However, not all high value gesture-only functionality needs a tutorial. For example, don't teach users how to scroll content. They already know how because it's a fundamental, system-wide interaction.
+
+ Bottom line: when it comes to offering help in your app, it's much better to let users come to you when they need it.
+On every screen in your app, offer help in the action overflow. Always make it the very last item in the menu and label it "Help".
+ +
+
+ We've established this standard design so that when users are desperate for help, they won't have to hunt to find it (see design principle: Give me tricks that work everywhere).
+In addition to help, you might want to expose other information, such as copyright info, credits, terms of service, and privacy policy.
+ +Let users access this information through the Help menu item, but optimize the flow for people with urgent questions about how to do something or why something is happening in your app. The smaller subset of users who are looking for legal fine print or the names of the people who created the app won't be as burdened by taking a few extra steps.
+ +The same is true for any communication options you might want to provide, such as contacting customer support or submitting feedback. Offer these options in a way that doesn't add an extra step before users see help. When you put the help content forward, you increase the likelihood that users will find the answers on their own, which in turn reduces your support costs.
+ +When someone chooses "Help":
+ +
+
+
+ Present a dialog asking them to choose between help and other options.
+Immediately launch a web browser with help content. Place other options in a footer.
+Build a help screen in your app and offer other options in the action bar. For example, you could let users contact you with questions or feedback through an action button. The action overflow is the ideal place for non-help information that users rarely need.
+This requires more development work than launching a web browser, but it's a nicer experience for users because they don't leave your app to get the help they need and doesn't require a network connection.
+On-screen help is an extension of your app's UI, not a description of it. All words on the screen from the core app to the help should follow our Writing Style principles so that the end-to-end experience feels seamless and cohesive.
+ +It's not necessary to document every single detail about your app, especially things that are extremely apparent just by looking at the UI, or behaviors that are standard for the platform. Surface just the key additional information that the on-screen text doesn't have room to describe, in a way that makes it easy to map to the screen.
+ +In describing key UI elements and providing step-by-step instructions, consider combining text with icons, partial screenshots with callouts, and other imagery. You'll need fewer words to explain things, and users will absorb the information more quickly.
+ +People don't read help from start to finish. They scan around, looking for a piece of information containing the answer they need. Make it less burdensome with friendly formatting and layout choices like bold headings, bulleted and numbered lists, tables, and white space between paragraphs. And if you have a large amount of content, divide it into multiple screens to cut down on scrolling.
+ +What's better than a screen that's easy to scan? A screen that requires no scanning at all because the answer's right there. Consider having each screen in your app navigate to help that's relevant just to that screen. We call this contextual help, and it's the holy grail of user assistance. If you take this approach, be sure to also provide a way to get to the rest of the help content.
\ No newline at end of file diff --git a/docs/html/design/patterns/index.jd b/docs/html/design/patterns/index.jd index 6f88e6d99fd..4416de1daa5 100644 --- a/docs/html/design/patterns/index.jd +++ b/docs/html/design/patterns/index.jd @@ -20,10 +20,10 @@ footer.hide=1 - +
Screens should have the same functionality regardless of orientation. If you use a compound view in -one orientation, don't split it up when the user rotates the screen. There are several techniques +
Screens should strive to have the same functionality regardless of orientation. If you use a compound view in +one orientation, try not to split it up when the user rotates the screen. There are several techniques you can use to adjust the layout after orientation change while keeping functional parity intact.
When the device rotates, collapse the left pane view to only show the most important information. -Provide an expand control that allows the user to bring the left pane content back to its original -width and vice versa.
+When the device rotates, collapse the left pane view to only show the most important information.
After rotating the device, show the right pane in fullscreen view. Use the Up icon in the action bar -to show the left panel and allow navigation to a different email. Hide the left panel by touching -the content in the detail panel.
+If your screen cannot accommodate the compound view on rotation show the right pane in full screen view on rotation to portrait. Use the Up icon in action bar to show the parent screen.
Look for opportunities to consolidate your views into multi-panel compound views.
Make sure that your screens provide functional parity after the screen orientation +
Make sure that your screens try to provide functional parity after the screen orientation changes.
Android 4.0 removes the need for traditional hardware keys on phones by replacing them with a -virtual navigation bar that houses the Back, Home and Recents buttons. Read the -Compatibility pattern to learn how the OS adapts to -phones with hardware buttons and how pre-Android 3.0 apps that rely on menu keys are supported.
- -
-
- The action bar is the most important structural element of an Android app. It provides consistent -navigation across the platform and allows your app to surface actions.
- -
-
- Creating apps that scale well across different form factors and screen sizes is important in the -Android world. Multi-pane layouts allow you to combine different activities that show separately on -smaller devices into richer compound views for tablets.
- -
-
- The long press gesture which was traditionally used to show contextual actions for objects is now -used for data selection. When selecting data, contextual action bars allow you to surface actions.
- -
-
- Notifications have received some notable enhancements in Android 4.1:
+The base notification layout has not changed, so app notifications designed for versions earlier than Jelly Bean still look and work the same. Check the updated Notifications page for more details.
+
+ Widgets are an essential aspect of home screen customization, allowing "at-a-glance" views of an app's most important data and functionality right from the user's home screen. Android 4.1 introduces improved App Widgets that can automatically resize and load different content based upon a number of factors including:
+You can supply separate landscape and portrait layouts for your widgets, which the system inflates as appropriate when the screen orientation changes. The Application Widgets has useful details about widget types, limitations, and design considerations.
+
+ One of Android's missions is to organize the world's information and make it universally accessible and useful. Our mission applies to all users-including people with disabilities such as visual impairment, color deficiency, hearing loss, and limited dexterity.
+The new Accessibility page provides details on how to design your app to be as accessible as possible by:
+You can supply separate landscape and portrait layouts for your widgets, which the system inflates as appropriate when the screen orientation changes. The [Application Widgets] (should be link) has useful details about widget types, limitations, and design considerations.
+
+ Android 4.0 removes the need for traditional hardware keys on phones by replacing them with a + virtual navigation bar that houses the Back, Home and Recents buttons. Read the Compatibility pattern to learn how the OS adapts to phones with hardware buttons and how pre-Android 3.0 apps that rely on menu keys are supported.
+
+ The action bar is the most important structural element of an Android app. It provides consistent navigation across the platform and allows your app to surface actions.
+
+ Creating apps that scale well across different form factors and screen sizes is important in the Android world. Multi-pane layouts allow you to combine different activities that show separately on smaller devices into richer compound views for tablets.
+
+ The long press gesture which was traditionally used to show contextual actions for objects is now used for data selection. When selecting data, contextual action bars allow you to surface actions.
+
+ The notification system allows your app to keep the user informed about important events, such as -new messages in a chat app or a calendar event.
-To create an app that feels streamlined, pleasant, and respectful, it is important to design your -notifications carefully. Notifications embody your app's voice, and contribute to your app's -personality. Unwanted or unimportant notifications can annoy the user, so use them judiciously.
-To create an application that people love, it's important to recognize that the user's attention and -focus is a resource that must be protected. To use an analogy that might resonate with software -developers, the user is not a method that can be invoked to return a value. The user's focus is a -resource more akin to a thread, and creating a notification momentarily blocks the user thread as -they process and then dismiss the interruptive notification.
-Android's notification system has been designed to quickly inform users of events while they focus -on a task, but it is nonetheless still important to be conscientious when deciding to create a -notification.
-While well behaved apps generally only speak when spoken to, there are some limited cases where an -app actually should interrupt the user with an unprompted notification.
-Notifications should be used primarily for time sensitive events, and especially if these -synchronous events involve other people. For instance, an incoming chat is a real time and -synchronous form of communication: there is another user actively waiting on you to respond. -Calendar events are another good example of when to use a notification and grab the user's -attention, because the event is imminent, and calendar events often involve other people.
- -
-
-The notification system allows your app to keep the user informed about events, such as new chat messages or a calendar event. Think of notifications as a news channel that alerts the user to important events as they happen or a log that chronicles events while the user is not paying attention.
-There are however many other cases where notifications should not be used:
+In Jelly Bean, notifications received their most important structural and functional update since the beginning of Android.
Don't notify the user of information that is not directed specifically at them, or information -that is not truly time sensitive. For instance the asynchronous and undirected updates flowing -through a social network do not warrant a real time interruption.
-Don't create a notification if the relevant new information is currently on screen. Instead, use -the UI of the application itself to notify the user of new information directly in context. For -instance, a chat application should not create system notifications while the user is actively -chatting with another user.
-Don't interrupt the user for low level technical operations, like saving or syncing information, -or updating an application, if it is possible for the system to simply take care of itself without -involving the user.
-Don't interrupt the user to inform them of an error if it is possible for the application to -quickly recover from the error on its own without the user taking any action.
-Don't use notifications for services that the user cannot manually start or stop.
-Don't create superfluous notifications just to get your brand in front of users. Such -notifications will only frustrate and likely alienate your audience. The best way to provide the -user with a small amount of updated information and to keep them engaged with your application is to -develop a widget that they can choose to place on their home screen.
-At a minimum, all notifications consist of a base layout, including:
+The information arrangement of the base layout has not changed in Jelly Bean, so app notifications designed for versions earlier than Jelly Bean still look and work the same.
-
+
+ With Jelly Bean you have the option to provide more event detail. You can use this to show the first few lines of a message or show a larger image preview. This provides the user with additional context, and - in some cases - may allow the user to read a message in its entirety. The user can pinch-zoom or two-finger glide in order to toggle between base and expanded layouts. For single event notifications, Android provides two expanded layout templates (text and image) for you to re-use in your application.
+ +
+
-
+ Starting with Jelly Bean, Android supports optional actions that are displayed at the bottom of the notification. With actions, users can handle the most common tasks for a particular notification from within the notification shade without having to open the originating application. This speeds up interaction and, in conjunction with "swipe-to-dismiss", helps users to streamline their notification triaging experience.
+Be judicious with how many actions you include with a notification. The more actions you include, the more cognitive complexity you create. Limit yourself to the fewest number of actions possible by only including the most imminently important and meaningful ones.
+Good candidates for actions on notifications are actions that are:
+Avoid actions that are:
+
+ For notifications of items sent by another user (such as a message or status update), include that -person's image.
-Remember to include the app icon as a secondary icon in the notification, so that the user can -still identify which app posted it.
+You can specify a maximum of three actions, each consisting of an action icon and an action name. Adding actions to a simple base layout will make the notification expandable, even if the notification doesn't have an expanded layout. Since actions are only shown for expanded notifications and are otherwise hidden, you must make sure that any action a user can invoke from a notification is available from within the associated application as well.
+
+ For notifications of items sent by another user (such as a message or status update), include that person's image.
+Remember to include the app icon as a secondary icon in the notification, so that the user can still identify which app posted it.
When the user touches a notification, be open your app to the place where the user can consume and -act upon the data referenced in the notification. In most cases this will be the detail view of a -single data item (e.g. a message), but it might also be a summary view if the notification is -stacked (see Stacked notifications below) and references multiple items. If in any of those cases -the user is taken to a hierarchy level below your app's top-level, insert navigation into your app's -back stack to allow them to navigate to your app's top level using the system back key. For more -information, see the chapter on System-to-app navigation in the -Navigation design pattern.
-By default, standard Android notifications include a timestamp in the upper right corner. Consider -whether the timestamp is valuable in the context of your notification. If the timestamp is not -valuable, consider if the event is important enough to warrant grabbing the user's attention with a -notification. If the notification is important enough, decide if you would like to opt out of -displaying the timestamp.
-Include a timestamp if the user likely needs to know how long ago the notification occurred. Good -candidates for timestamps include communication notifications (email, messaging, chat, voicemail) -where the user may need the timestamp information to understand the context of a message or to -tailor a response.
+When the user touches the body of a notification (outside of the action buttons), open your app to the place where the user can consume and act upon the data referenced in the notification. In most cases this will be the detail view of a +single data item such as a message, but it might also be a summary view if the notification is stacked (see Stacked notifications below) and references multiple items. If in any of those cases the user is taken to a hierarchy level below your app's top-level, insert navigation into your app's back stack to allow them to navigate to your app's top level using the system back key. For more +information, see the chapter on System-to-app navigation in the Navigation design pattern.
+ +Starting with Jelly Bean, Android now supports a priority flag for notifications. It allows you to influence where your notification will appear in comparison to other notifications and help to make sure that users always see their most important notifications first. You can choose from the following priority levels when posting a notification:
+ +| Priority | +Use | +
|---|---|
| MAX | +Use for critical and urgent notifications that alert the user to a condition that is time-critical or needs to be resolved before they can continue with a particular task. | +
| HIGH | +Use high priority notifications primarily for important communication, such as message or chat events with content that is particularly interesting for the user. | +
| DEFAULT | +The default priority. Keep all notifications that don't fall into any of the other categories at this priority level. | +
| LOW | +Use for notifications that you still want the user to be informed about, but that rate low in urgency. | +
| MIN | +Contextual/background information (e.g. weather information, contextual location information). Minimum priority notifications will not show in the status bar. The user will only discover them when they expand the notification tray. | +
+
If your app creates a notification while another of the same type is still pending, avoid creating an altogether new notification object. Instead, stack the notification.
@@ -123,51 +126,82 @@ notifications of a particular kind are pending.
-If you keep the summary and detail information on different screens, a stacked notification may need -to open to a different place in the app than a single notification.
-For example, a single email notification should always open to the content of the email, whereas a -stacked email notification opens to the Inbox view.
-Just like calendar events, some notifications alert the user to an event that happens at a -particular point in time. After that moment has passed, the notification is likely not important to -the user anymore, and you should consider removing it automatically. The same is true for active -chat conversations or voicemail messages the user has listened to, users should not have to manually -dismiss notifications independently from taking action on them.
+You can provide more detail about the individual notifications that make up a stack by using the expanded digest layout. This allows users to gain a better sense of which notifications are pending and if they are interesting enough to be read in detail within the associated app.
+ +
+
+Users should always be in control of notifications. Allow the user to disable your apps notifications or change their alert properties, such as alert sound and whether to use vibration, by adding a notification settings item to your application settings.
+By glancing at the notification area, the user should be able to discern what kinds of notifications are currently pending.
+ +Look at the notification icons the Android apps already provide and create notification icons for your app that are sufficiently distinct in appearance.
+Use the proper notification icon style for small icons, and the Holo Dark action bar icon style for your action icons.
+Keep your icons visually simple and avoid excessive detail that is hard to discern.
+Use color to distinguish your app from others.
+ +Many Android devices contain a tiny lamp, called the notification LED, which is used to keep the user informed about events while the screen is off. Notifications with a priority level of MAX, HIGH, or DEFAULT should cause the LED to glow, while those with lower priority (LOW and MIN) should not.
+ +The user's control over notifications should extend to the LED. By default, the LED will glow with a white color. Your notifications shouldn't use a different color unless the user has explicitly customized it.
+ +To create an app that feels streamlined, pleasant, and respectful, it is important to design your notifications carefully. Notifications embody your app's voice, and contribute to your app's personality. Unwanted or unimportant notifications can annoy the user, so use them judiciously.
+ +To create an application that people love, it's important to recognize that the user's attention and focus is a resource that must be protected. While Android's notification system has been designed to minimize the impact of notifications on the users attention, it is nonetheless still important to be aware of the fact that notifications are potentially interrupting the users task flow. As you plan your notifications, ask yourself if they are important enough to warrant an interruption. If you are unsure, allow the user to opt into a notification using your apps notification settings or adjust the notifications priority flag.
+Time sensitive events are great opportunities for valuable notifications with high priority, especially if these synchronous events involve other people. For instance, an incoming chat is a real time and synchronous form of communication: there is another user actively waiting on you to respond. Calendar events are another good example of when to use a notification and grab the user's attention, because the event is imminent, and calendar events often involve other people.
+While well behaved apps generally only speak when spoken to, there are some limited cases where an +app actually should interrupt the user with an unprompted notification.
+Notifications should be used primarily for time sensitive events, and especially if these +synchronous events involve other people. For instance, an incoming chat is a real time and +synchronous form of communication: there is another user actively waiting on you to respond. +Calendar events are another good example of when to use a notification and grab the user's +attention, because the event is imminent, and calendar events often involve other people.
+ +
-You can provide a short preview of your notification's content by providing optional ticker text. -The ticker text is shown for a short amount of time when the notification enters the system and then -hides automatically.
+There are however many other cases where notifications should not be used:
+Avoid notifying the user of information that is not directed specifically at them, or information that is not truly time sensitive. For instance the asynchronous and undirected updates flowing through a social network generally do not warrant a real time interruption. For the users that do care about them, allow them to opt-in.
+Don't create a notification if the relevant new information is currently on screen. Instead, use the UI of the application itself to notify the user of new information directly in context. For instance, a chat application should not create system notifications while the user is actively chatting with another user.
+Don't interrupt the user for low level technical operations, like saving or syncing information, or updating an application, if it is possible for the system to simply take care of itself without involving the user.
+Don't interrupt the user to inform them of an error if it is possible for the application to quickly recover from the error on its own without the user taking any action.
+Don't create notifications that have no true notification content and merely advertise your app. A notification should inform the user about a state and should not be used to merely launch an app.
+Don't create superfluous notifications just to get your brand in front of users. Such +notifications will only frustrate and likely alienate your audience. The best way to provide the +user with a small amount of updated information and to keep them engaged with your application is to +develop a widget that they can choose to place on their home screen.
+
-
+
Users should always be in control of notifications. Allow the user to silence the notifications from -your app by adding a notification settings item to your application settings.
-By glancing at the notification area, the user should be able to discern what notification types are -currently pending.
-Do:
-Don't:
-Notifications are indicated by icons in the notification area and can be accessed by opening the -notification drawer.
-Inside the drawer, notifications are chronologically sorted with the latest one on top. Touching a -notification opens the associated app to detailed content matching the notification. Swiping left or -right on a notification removes it from the drawer.
+Notifications are indicated by icons in the notification area and can be accessed by opening the notification drawer.
+Inside the drawer, notifications are chronologically sorted with the latest one on top. Touching a notification opens the associated app to detailed content matching the notification. Swiping left or right on a notification removes it from the drawer.
On tablets, the notification area is integrated with the system bar at the bottom of the screen. The -notification drawer is opened by touching anywhere inside the notification area.
+On tablets, the notification area is integrated with the system bar at the bottom of the screen. The notification drawer is opened by touching anywhere inside the notification area.
Ongoing notifications keep users informed about an ongoing process in the background. For example, -music players announce the currently playing track in the notification system and continue to do so -until the user stops the playback. They can also be used to show the user feedback for longer tasks -like downloading a file, or encoding a video. Ongoing notifications cannot be manually removed from -the notification drawer.
+Ongoing notifications keep users informed about an ongoing process in the background. For example, music players announce the currently playing track in the notification system and continue to do so until the user stops the playback. They can also be used to show the user feedback for longer tasks like downloading a file, or encoding a video. Ongoing notifications cannot be manually removed from the notification drawer.
Your app should not create a dialog or toast if it is not currently on screen. Dialogs and Toasts -should only be displayed as the immediate response to the user taking an action inside of your app. -For instance, dialogs can be used to confirm that the user understands the severity of an action, -and toasts can echo back that an action has been successfully taken.
- +Your app should not create a dialog or toast if it is not currently on screen. Dialogs and Toasts should only be displayed as the immediate response to the user taking an action inside of your app. For further guidance on the use of dialogs and toasts, refer to Confirming & Acknowledging.
+Android 3.0 introduced the long press gesture—that is, a touch that's held in the same -position for a moment—as the global gesture to select data. This affects the way you should +
Android 3.0 changed the long press gesture—that is, a touch that's held in the same position for a moment—to be the global gesture to select data.. This affects the way you should handle multi-select and contextual actions in your apps.
+If your app uses action bar tabs, use swipe to navigate between the different views.
+If your app uses action bar tabs, use swipe to navigate between the different views.
-Use swipe to quickly navigate between detail views or tabs.
-Transition between the views as the user performs the swipe gesture. Do not wait for the - gesture to complete and then transition between views.
-If you used buttons in the past for previous/next navigation, replace them with - the swipe gesture.
-Consider adding contextual information in your detail view that informs the user about the - relative list position of the currently visible item.
-Use swipe to quickly navigate between detail views or tabs.
+Transition between the views as the user performs the swipe gesture. Do not wait for the + gesture to complete and then transition between views.
+If you used buttons in the past for previous/next navigation, replace them with + the swipe gesture.
+Consider adding contextual information in your detail view that informs the user about the + relative list position of the currently visible item.
+For more details on how to build swipe views, read the developer documentation on Implementing Lateral Navigation.
+Widgets are an essential aspect of home screen customization. You can imagine them as "at-a-glance" views of an app's most important data and functionality that is accessible right from the user's home screen. Users can move widgets across their home screen panels, and, if supported, resize them to tailor the amount of information within a widget to their preference.
+ +As you begin planning your widget, think about what kind of widget you're trying to build. Widgets typically fall into one of the following categories:
+ +
+Information widgets typically display a few crucial information elements that are important to a user and track how that information changes over time. Good examples for information widgets are weather widgets, clock widgets or sports score trackers. Touching information widgets typically launches the associated app and opens a detail view of the widget information.
+ +As the name implies, collection widgets specialize on displaying multitude elements of the same type, such as a collection of pictures from a gallery app, a collection of articles from a news app or a collection of emails/messages from a communication app. Collection widgets typically focus on two use cases: browsing the collection, and opening an element of the collection to its detail view for consumption. Collection widgets can scroll vertically.
+
+
+
+The main purpose of a control widget is to display often used functions that the user can trigger right from the home screen without having to open the app first. Think of them as remote controls for an app. A typical example of control widgets are music app widgets that allow the user to play, pause or skip music tracks from outside the actual music app.
+Interacting with control widgets may or may not progress to an associated detail view depending on if the control widget's function generated a data set, such as in the case of a search widget.
+ +
+While all widgets tend to gravitate towards one of the three types described above, many widgets in reality are hybrids that combine elements of different types.
+For the purpose of your widget planning, center your widget around one of the base types and add elements of other types if needed.
+While widgets could be understood as "mini apps", there are certain limitations that are important to understand before you start to embark on designing your widget:
+ +Because widgets live on the home screen, they have to co-exist with the navigation that is established there. This limits the gesture support that is available in a widget compared to a full-screen app. While apps for example may support a view pager that allows the user to navigate between screens laterally, that gesture is already taken on the home screen for the purpose of navigating between home panels.
+The only gestures available for widgets are:
+
+
+Given the above interaction limitations, some of the UI building blocks that rely on restricted gestures are not available for widgets. For a complete list of supported building blocks and more information on layout restrictions, please refer to the "Creating App Widget Layouts" section in the App Widgets API Guide.
+ +Widgets are a great mechanism to attract a user to your app by "advertising" new and interesting content that is available for consumption in your app.
+Just like the teasers on the front page of a newspaper, widgets should consolidate and concentrate an app's information and then provide a connection to richer detail within the app; or in other words: the widget is the information "snack" while the app is the "meal." As a bottom line, always make sure that your app shows more detail about an information item than what the widget already displays.
+ +Besides the pure information content, you should also consider to round out your widget's offering by providing navigation links to frequently used areas of your app. This lets users complete tasks quicker and extends the functional reach of the app to the home screen.
+Good candidates for navigation links to surface on widgets are:
+With version 3.1, Android introduced resizable widgets to the platform. Resizing allows users to adjust the height and/or the width of a widget within the constraints of the home panel placement grid. You can decide if your widget is freely resizable or if it is constrained to horizontal or vertical size changes. You do not have to support resizing if your particular widget is inherently fixed-size.
+Allowing users to resize widgets has important benefits:
+
+ Planning a resize strategy for your widget depends on the type of widget you're creating. List or grid-based collection widgets are usually straightforward because resizing the widget will simply expand or contract the vertical scrolling area. Regardless of the of the widget's size, the user can still scroll all information elements into view. Information widgets on the other hand require a bit more hands-on planning, since they are not scrollable and all content has to fit within a given size. You will have to dynamically adjust your widget's content and layout to the size the user defined through the resize operation.
+
+In this simple example the user can horizontally resize a weather widget in 4 steps and expose richer information about the weather at the current location as the widget grows.
+For each widget size determine how much of your app's information should surface. For smaller sizes concentrate on the essential and then add more contextual information as the widget grows horizontally and vertically.
+ +It will be tempting to layout your widgets according to the dimensions of the placement grid of a particular device that you own and develop with. This can be a useful initial approximation as you layout your widget, but keep the following in mind:
+Sometimes widgets need to be setup before they can become useful. Think of an email widget for example, where you need to provide an account before the inbox can be displayed. Or a static photo widget where the user has to assign the picture that is to be displayed from the gallery.
+Android widgets display their configuration choices right after the widget is dropped onto a home panel. Keep the widget configuration light and don't present more than 2-3 configuration elements. Use dialog-style instead of full-screen activities to present configuration choices and retain the user's context of place, even if doing so requires use of multiple dialogs.
+Once setup, there typically is not a lot of reason to revisit the setup. Therefore Android widgets do not show a "Setup" or "Configuration" button.
+
+ Blue is the standard accent color in Android's color palette. Each color has a corresponding darker shade that can be used as a complement when needed.
- +
diff --git a/docs/html/design/style/iconography.jd b/docs/html/design/style/iconography.jd
index 775e45d9fdb..31274c51d5e 100644
--- a/docs/html/design/style/iconography.jd
+++ b/docs/html/design/style/iconography.jd
@@ -110,7 +110,7 @@ files for further customization.
-Download the Action Bar Icon Pack +Download the Action Bar Icon Pack
diff --git a/docs/html/design/style/typography.jd b/docs/html/design/style/typography.jd index db2fb5ff330..a699bed88b3 100644 --- a/docs/html/design/style/typography.jd +++ b/docs/html/design/style/typography.jd @@ -18,8 +18,8 @@ italic weights by default.
-
-
+
+
The Android Design Team was pleased to present five fantastic design-oriented sessions at Google I/O 2012. Visit these pages to view the videos and presentations from the conference.
+
+
+You have a great idea for an Android app. You want it to stand out among hundreds of thousands. You want your users to love it and tell everyone they know. The Android User Experience team is here to help. We talk about the Android Design guide and other tricks of the trade for creating apps that delight users and help them accomplish their goals. No design background is required.
+Design isn't black magic, it's a field that people can learn. In this talk two elite designers from Google give you an advanced crash course in interactive and visual design. Topics include mental models, natural mappings, metaphors, mode errors, visual hierarchies, typography and gestalt principles. Correctly applied, this knowledge can drastically improve the quality of your work.
+An app is useless if people can't find their way around it. Android introduced big navigation-support changes in 3.0 and 4.0. The Action Bar offers a convenient control for Up navigation, the Back key's behavior became more consistent within tasks, and the Recent Tasks UI got an overhaul. In this talk, we discuss how and why we got where we are today, how to think about navigation when designing your app's user experience, and how to write apps that offer effortless navigation in multiple Android versions.
+The Android Design Guide describes how to design beautiful Android apps, but not how to build them. In this talk we give practical tips for how to apply fit & finish as you implement your design, we show you how to avoid some common pitfalls, we describe some useful patterns, and show how tools can help.
+Best-in-class application designers and developers talk about their experience in developing for Android, showing screenshots from their app, exploring the challenges they faced, and offering creative solutions congruent with the Android Design guide. Guests are invited to show examples of visual and interaction patterns in their application that manage to keep it simultaneously consistent and personal.
+Videos for the entire Design Track can also be found on the Android Developers Channel on YouTube.
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd index 6e541ee5221..14ab5d58753 100644 --- a/docs/html/develop/index.jd +++ b/docs/html/develop/index.jd @@ -24,6 +24,7 @@ position:absolute;top:50%;left:0;width:100%;z-index:-1;text-align:center;displayUsers can purchase your products on Google Play using several convenient -payment methods—credit cards, Direct Carrier Billing, and Google Play balance..
+payment methods—credit cards, Direct Carrier Billing, and Google Play balance.Credit card is the most common method of payment. Users can pay using any credit card that they’ve registered in Google Play. To make it easy for users to get started, diff --git a/docs/html/distribute/googleplay/promote/brand.jd b/docs/html/distribute/googleplay/promote/brand.jd index 8d049030b67..76ed6196144 100644 --- a/docs/html/distribute/googleplay/promote/brand.jd +++ b/docs/html/distribute/googleplay/promote/brand.jd @@ -150,7 +150,7 @@ any way you want, provided that you follow the guidelines described below.
http://play.google.com/store/search?q=yourCompanyNameAny other brands or icons depicted on this site are not are the property of their -repective owners and usage is reserved. You must seek the developer for appropriate permission to use them.
+respective owners and usage is reserved. You must seek the developer for appropriate permission to use them. diff --git a/docs/html/distribute/googleplay/publish/preparing.jd b/docs/html/distribute/googleplay/publish/preparing.jd index ab8fadf4869..6ea0f53946e 100644 --- a/docs/html/distribute/googleplay/publish/preparing.jd +++ b/docs/html/distribute/googleplay/publish/preparing.jd @@ -435,7 +435,6 @@ the page is complete and ready for publishing.Related resources:
A great way to improve UI performance is to minimize the complexity of your layouts. If you open up hierarchyviewer and see that your layouts are more than 5 levels deep, it may be time to simplify your layout. Consider refactoring those deeply nested LinearLayouts into RelativeLayout. The impact of View objects is cumulative — each one costs about 1 to 2 KB of memory, so large view hierarchies can be a recipe for disaster, causing frequent VM garbage collection passes which block the main (UI) thread. You can learn more in World of ListView, another session at Google I/O.
-Lastly, pointed out in the blog post Traceview War Story, tools like traceview and ddms can be your best friends in improving your app by profiling method calls and monitoring VM memory allocations, respectively.
+Lastly, pointed out in the blog post Traceview War Story, tools like traceview and ddms can be your best friends in improving your app by profiling method calls and monitoring VM memory allocations, respectively.There's no substitute for a real user interface designer — ideally one who's well-versed in mobile and Android, and ideally handy with both interaction and visual design. One popular venue to post openings for designers is jobs.smashingmagazine.com, and leveraging social connections on Twitter and LinkedIn can surface great talent.
-If you don't have the luxury of working with a UI designer, there are some ways in which you can improve your app's appearance yourself. First, get familiar with Adobe Photoshop, Adobe Fireworks, or some other raster image editing tool. Mastering the art of the pixel in these apps takes time, but honing this skill can help build polish across your interface designs. Also, master the resources framework by studying the framework UI assets and layouts and reading through the new resources documentation. Techniques such as 9-patches and resource directory qualifiers are somewhat unique to Android, and are crucial in building flexible yet aesthetic UIs.
+If you don't have the luxury of working with a UI designer, there are some ways in which you can improve your app's appearance yourself. First, get familiar with Adobe Photoshop, Adobe Fireworks, or some other raster image editing tool. Mastering the art of the pixel in these apps takes time, but honing this skill can help build polish across your interface designs. Also, master the resources framework by studying the framework UI assets and layouts and reading through the new resources documentation. Techniques such as 9-patches and resource directory qualifiers are somewhat unique to Android, and are crucial in building flexible yet aesthetic UIs.Before you get too far in designing your app and writing the code, make sure to visit the Android Design site and learn about the vision, the building blocks, and the tools of designing beautiful and inspiring user interfaces.
@@ -105,7 +105,7 @@ Again, listen to your users by collecting and responding to feature requests. Be-A great way to deliver a delightful user experience is to integrate tightly with the operating system. Features like Home screen widgets, rich notifications, global search integration, and {@link android.widget.QuickContactBadge Quick Contacts} are fairly low-hanging fruit in this regard.
+A great way to deliver a delightful user experience is to integrate tightly with the operating system. Features like Home screen widgets, rich notifications, global search integration, and {@link android.widget.QuickContactBadge Quick Contacts} are fairly low-hanging fruit in this regard.For some app categories, basic features like home screen widgets are par for the course. Not including them is a sure-fire way to tarnish an otherwise positive user experience. Some apps can achieve even tighter OS integration with Android's contacts, accounts, and sync APIs.
diff --git a/docs/html/distribute/index.jd b/docs/html/distribute/index.jd
index c27bc6260a4..c0ed6a89590 100644
--- a/docs/html/distribute/index.jd
+++ b/docs/html/distribute/index.jd
@@ -13,10 +13,10 @@ header.hide=1
The most visited store in the world for Android apps. Cloud-connected and always synced, it's never been easier for users to find and download your apps.
- +The device art generator allows you to quickly wrap your app screenshots in real device artwork. +This provides better visual context for your app screenshots on your web site or in other +promotional materials.
+ +Note: Do not use graphics created here in your 1024x500 +feature image or screenshots for your Google Play app listing.
+ +Drag a screenshot from your desktop onto a device to the right.
+Customize the generated image and drag it to your desktop to save.
+
+
+
+
+
+ Rotate
+
Caution: If you need a {@link android.content.Context} object +within your {@link android.app.Fragment}, you can call {@link android.app.Fragment#getActivity()}. +However, be careful to call {@link android.app.Fragment#getActivity()} only when the fragment is +attached to an activity. When the fragment is not yet attached, or was detached during the end of +its lifecycle, {@link android.app.Fragment#getActivity()} will return null.
+For more samples using fragments (and complete source files for this example), -see the sample code available in ApiDemos (available for download from the Samples SDK component).
diff --git a/docs/html/guide/components/services.jd b/docs/html/guide/components/services.jd index ba5e1f03b27..b89914a60e0 100644 --- a/docs/html/guide/components/services.jd +++ b/docs/html/guide/components/services.jd @@ -49,13 +49,6 @@ perform interprocess communication LocalService} -false.
+Note that it might take a while for the registration ID be completely removed from GCM. Thus it is possible that messages sent during step 7 above gets a valid message ID as response, even though the message will not be delivered to the device. Eventually, the registration ID will be removed and the server will get a NotRegistered error, without any further action being required from the 3rd-party server (this scenario happens frequently while an application is being developed and tested).
Every message sent in GCM, regardless of its other characteristics, is either a "send-to-sync" (collapsible) message or a "message with payload" (non-collapsible message).
+ +Every message sent in GCM has the following characteristics:
+But despite these similarities, messages can behave very differently depending on their particular settings. One major distinction between messages is whether they are collapsed (where each new message replaces the preceding message) or not collapsed (where each individual message is delivered). Every message sent in GCM is either a "send-to-sync" (collapsible) message or a "message with payload" (non-collapsible message). These concepts are described in more detail in the following sections.
+A send-to-sync (collapsible) message is typically a "tickle" that tells a mobile application to sync data from the server. For example, suppose you have an email application. When a user receives new email on the server, the server pings the mobile application with a "New mail" message. This tells the application to sync to the server to pick up the new email. The server might send this message multiple times as new mail continues to accumulate, before the application has had a chance to sync. But if the user has received 25 new emails, there's no need to preserve every "New mail" message. One is sufficient. This is a case where you would use the GCM collapse_key parameter. A collapse key is an arbitrary string that is used to collapse a group of like messages when the device is offline, so that only the last message gets sent to the client. For example, "New mail," "Updates available," and so on
GCM allows a maximum of 4 different collapse keys to be used by the GCM server at any given time. In other words, the GCM server can simultaneously store 4 different send-to-sync messages, each with a different collapse key.
+ +A send-to-sync (collapsible) message is often a "tickle" that tells a mobile application to sync data from the server. For example, suppose you have an email application. When a user receives new email on the server, the server pings the mobile application with a "New mail" message. This tells the application to sync to the server to pick up the new email. The server might send this message multiple times as new mail continues to accumulate, before the application has had a chance to sync. But if the user has received 25 new emails, there's no need to preserve every "New mail" message. One is sufficient. Another example would be a sports application that updates users with the latest score. Only the most recent message is relevant, so it makes sense to have each new message replace the preceding message.
+ +The email and sports applications are cases where you would probably use the GCM collapse_key parameter. A collapse key is an arbitrary string that is used to collapse a group of like messages when the device is offline, so that only the most recent message gets sent to the client. For example, "New mail," "Updates available," and so on
GCM allows a maximum of 4 different collapse keys to be used by the GCM server at any given time. In other words, the GCM server can simultaneously store 4 different send-to-sync messages, each with a different collapse key. If you exceed this number GCM will only keep 4 collapse keys, with no guarantees about which ones they will be.
+Unlike a send-to-sync message, every "message with payload" (non-collapsible message) is delivered. The payload the message contains can be up to 4K. For example, here is a JSON-formatted message in an IM application in which spectators are discussing a sporting event:
+Unlike a send-to-sync message, every "message with payload" (non-collapsible message) is delivered. The payload the message contains can be up to 4kb. For example, here is a JSON-formatted message in an IM application in which spectators are discussing a sporting event:
{
"registration_id" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",
@@ -208,9 +222,10 @@ registerReceiver(mRetryReceiver, filter);
total_deleted—The value is a string with the number of deleted messages.The application should respond by syncing with the server to recover the discarded messages.
-Note: If your application does not need to use non-collapsible messages, collapsible messages are a better choice from a performance standpoint, because they put less of a burden on the device battery. - -
+ +If your application does not need to use non-collapsible messages, collapsible messages are a better choice from a performance standpoint, because they put less of a burden on the device battery.
+The Time to Live (TTL) feature lets the sender specify the maximum lifespan of a message using the time_to_live parameter in the send request. The value of this parameter must be a duration from 0 to 2,419,200 seconds, and it corresponds to the maximum period of time for which GCM will store and try to deliver the message. Requests that don't contain this field default to the maximum period of 4 weeks.
Here are some possible uses for this feature:
diff --git a/docs/html/guide/google/gcm/c2dm.jd b/docs/html/guide/google/gcm/c2dm.jd index 91c6ac59f67..ecc08c1ec29 100644 --- a/docs/html/guide/google/gcm/c2dm.jd +++ b/docs/html/guide/google/gcm/c2dm.jd @@ -17,7 +17,11 @@ page.title=MigrationGCM also provides helper libraries (client and server) to make writing your code easier.
+ +C2DM and GCM are not interoperable. For example, you cannot post notifications from GCM to C2DM registration IDs, nor can you use C2DM registration IDs as GCM registration IDs. From your server-side application, you must keep keep track of whether a registration ID is from C2DM or GCM and use the proper endpoint.
+ +As you transition from C2DM to GCM, your server needs to be aware of whether a given registration ID +contains an old C2DM sender or a new GCM project ID. This is the approach we recommend: have the new app version (the one that uses GCM) send a bit along with the registration ID. This bit tells your server that this registration ID is for GCM. If you don't get the extra bit, you mark the registration ID as C2DM. Once no more valid registration IDs are marked as C2DM, you can complete the migration.
+This section describes how to move existing C2DM apps to GCM.
To start developing apps for Android, download the free Android SDK.
- -The "Android" name and
logo are trademarks of Google Inc.
+
The "Android" name, the
logo, and
+other trademarks are property of Google Inc.
You may not use the logo or the logo's custom typeface.
You may use the word "Android" in a product name only as a descriptor, such as "for Android" @@ -42,7 +41,7 @@ use of it must be attributed as such.
For more information about Android brands, see the Android Branding Guidelines.
- +All other trademarks are the property of their respective owners.
OnLoadCanceledListener<D>)int, int, int, int, int)int, int, int, int, int)int, int, float)Drawable)int)int, int, int, int, int)VisibilityListener)boolean)OnLoadCanceledListener<D>)int, int, int, int, int)int, int, int, int, int)int, int, float)Drawable)int)int, int, int, int, int)VisibilityListener)boolean)int mediaRouteTypesint FLAG_ACTIVITY_CLOSE_SYSTEM_DIALOGSboolean isVisible()boolean overridesItemVisibility()void refreshVisibility()void setVisibilityListener(VisibilityListener)diff --git a/docs/html/sdk/api_diff/16/changes/android.view.Display.html b/docs/html/sdk/api_diff/16/changes/android.view.Display.html index e1db224511c..c519edfc95f 100644 --- a/docs/html/sdk/api_diff/16/changes/android.view.Display.html +++ b/docs/html/sdk/api_diff/16/changes/android.view.Display.html @@ -54,7 +54,7 @@
void setTextViewCompoundDrawablesRelative(int,void setTextViewTextSize(int,void setViewPadding(int,int)int)int)String, String[], CancellationSignal)CursorFactory, String, String[], String, CancellationSignal)OnLoadCanceledListener<D>)int)int, int, int, int, int)int, int, int, int, int)int, int, float)Drawable)int, FieldPacker, Element, int[])int)int, int, int, int, int)VisibilityListener)boolean)int)SurfaceTexture)int)String, String[], CancellationSignal)CursorFactory, String, String[], String, CancellationSignal)OnLoadCanceledListener<D>)boolean)int, int, int, int, int)int, int, int, int, int)int, int, float)Drawable)int, int)int)int, int, int, int, int)VisibilityListener)boolean)MediaRouter.VolumeCallbackActionProvider.VisibilityListenerPlease carefully review the Android SDK License Agreement before downloading the SDK. +The License Agreement constitutes a contract between you and Google with respect to your use of the +SDK.
+Note: You must agree to this license agreement in order to +download one of the archived SDKs, because these SDK packages contain Google software (whereas, the +current SDK packages do not require a +license agreement, because they contain only the open sourced SDK tools).
+ + + ++ + +
++ +
++ +
+The Android SDK separates different parts of the SDK into separately downloadable packages. The -SDK starter package that you've installed includes only the SDK Tools. To develop an Android app, +
The Android SDK separates tools, platforms, and other components into packages you can + download using the Android SDK Manager. The original +SDK package you've downloaded includes only the SDK Tools. To develop an Android app, you also need to download at least one Android platform and the latest SDK Platform-tools.
You can update and install SDK packages at any time using the Android SDK Manager.
@@ -48,28 +49,32 @@ you keep this up to date.To get started, download the latest Android version, plus the lowest version you plan + to support (we recommend Android 2.2 for your lowest version).
Android offers a custom plugin for the Eclipse IDE, called Android Development Tools (ADT). This plugin is designed to give you a powerful, integrated -environment in which to develop Android apps. It extends the capabilites +environment in which to develop Android apps. It extends the capabilities of Eclipse to let you quickly set up new Android projects, build an app UI, debug your app, and export signed (or unsigned) app packages (APKs) for distribution.
@@ -39,21 +39,21 @@ the Next link on the right.https://dl-ssl.google.com/android/eclipse/
Note: If you have trouble acquiring the plugin, try using "http" in the Location URL, +
If you have trouble acquiring the plugin, try using "http" in the Location URL, instead of "https" (https is preferred for security reasons).
Note: If you get a security warning saying that the authenticity or validity of +
If you get a security warning saying that the authenticity or validity of the software can't be established, click OK.
| Name | Package | Size | MD5 Checksum | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ADT {@adtZipVersion} | {@adtZipDownload} | @@ -169,16 +167,20 @@ manually install it: -|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Category | +Option | +Description | +Comments | +
|---|---|---|---|
| Checking | +--disable <list> |
+Disable checking for a specific list of issues. | +The <list> must be a comma-separated list of {@code lint} issue IDs or categories. |
+
--enable <list> |
+Check for all the default issues supported by {@code lint} as well as the specifically enabled list of issues. | +The <list> must be a comma-separated list of {@code lint} issue IDs or categories. |
+|
--check <list> |
+Check for a specific list of issues. | +The <list> must be a comma-separated list of {@code lint} issue IDs or categories. |
+|
-w or --nowarn |
+Only check for errors and ignore warnings | ++ | |
-Wall |
+Check for all warnings, including those that are disabled by default | ++ | |
-Werror |
+Report all warnings as errors | ++ | |
--config <filename> |
+Use the specified configuration file to determine if issues are enabled or disabled for {@code lint} checking | +If the project contains a {@code lint.xml} file, the {@code lint.xml} file will be used as the configuration file by default. | +|
| Reporting | +--html <filename> |
+Generate an HTML report. | +The report is saved in the output file specified in the argument. The HTML output includes code snippets of the source code where {@code lint} detected an issue, a verbose description of the issue found, and links to the source file. | +
--url <filepath>=<url> |
+In the HTML output, replace a local path prefix <filepath> with a url prefix <url>. |
+The {@code --url} option only applies when you are generating an HTML report with the {@code --html} option. You can specify multiple <filepath>=<url> mappings in the argument by separating each mapping with a comma. To turn off linking to files, use {@code --url none} |
+|
--simplehtml <filename> |
+Generate a simple HTML report | +The report is saved in the output file specified in the argument. | +|
--xml <filename> |
+Generate an XML report | +The report is saved in the output file specified in the argument. | +|
--fullpath |
+Show the full file paths in the {@code lint} checking results. | ++ | |
--showall |
+Don't truncate long messages or lists of alternate locations. | ++ | |
--nolines |
+Don't include code snippets from the source files in the output. | ++ | |
--exitcode |
+Set the exit code to 1 if errors are found. | ++ | |
--quiet |
+Don't show the progress indicator. | ++ | |
| Help | +--help |
+List the command-line arguments supported by the {@code lint} tool. | +Use {@code --help <topic>} to see help information for a specific topic, such as "suppress". | +
--list |
+List the ID and short description for issues that can be checked by {@code lint} | ++ | |
--show |
+List the ID and verbose description for issues that can be checked by {@code lint} | +Use {@code --show <ids>} to see descriptions for a specific list of {@code lint} issue IDs. | +|
--version |
+Show the {@code lint} version | ++ |
To configure lint checking, you can apply the following annotation or attribute to the source files in your Android project.
+@SuppressLint annotation. tools:ignore attribute. You can also specify your lint checking preferences for a specific Android project in the lint.xml file. For more information on configuring lint, see Improving Your Code with lint.
diff --git a/docs/html/tools/help/monitor.jd b/docs/html/tools/help/monitor.jd index 8e2ea36ae25..18fb49a3b3f 100644 --- a/docs/html/tools/help/monitor.jd +++ b/docs/html/tools/help/monitor.jd @@ -1,7 +1,7 @@ -page.title=Debug Monitor +page.title=Device Monitor @jd:body -Android Debug Monitor is a stand-alone tool that provides a graphical user interface for +
Android Device Monitor is a stand-alone tool that provides a graphical user interface for several Android application debugging and analysis tools. The Monitor tool does not require installation of a integrated development environment, such as Eclipse, and encapsulates the following tools:
@@ -16,9 +16,9 @@ following tools:To start Debug Monitor, enter the following command from the SDK tools/
+
To start Device Monitor, enter the following command from the SDK tools/
directory:
monitor-
Start an Android emulator or connect an Android device via USB cable, and connect the Debug +
Start an Android emulator or connect an Android device via USB cable, and connect Device Monitor to the device by selecting it in the Devices window.
diff --git a/docs/html/tools/index.jd b/docs/html/tools/index.jd index 3fc9bfebec1..04e0d3b7fd2 100644 --- a/docs/html/tools/index.jd +++ b/docs/html/tools/index.jd @@ -34,7 +34,7 @@ page.title=Developer ToolsImportant: To download the new Android +4.0.x system components from the Android SDK Manager, you must first update the +SDK tools to revision 20 or later and restart the Android SDK Manager. If you do not, +the Android 4.1 system components will not be available for download.
+ +
+
Revision 2 (July 2012)
+
Maintenance update. The system version is 4.1.1.
+
+
Revision 1 (June 2012)
+
Initial release. The system version is 4.1.0.
+The downloadable platform includes the following emulator skins:
+ +To test your application on an emulator that represents the Nexus 7 tablet device, you can create an AVD with +the new WXGA800-7in skin. For best performance, make sure to enable graphics acceleration in the +emulator configuration.
+ + + + + + + + + + + + + +
-
- Revision 3 (March 2012)
-
Revision 3 (March 2012)
+
- Maintenance update. The system version is 4.0.4.
Note: This system image includes support for emulator @@ -70,11 +170,10 @@ hardware graphics acceleration when used with SDK Tools r17 or higher.
- Revision 2 (January 2012)
-
Maintenance update. The system version is 4.0.3.
- Revision 1 (December 2011)
-
Initial release. The system version is 4.0.3.
To test your application on an emulator that represents the latest Android device, you can create -an AVD with the new WXGA720 skin (it's an xhdpi, normal screen device). Note that the emulator -currently doesn't support the new on-screen navigation bar for devices without hardware navigation -buttons, so when using this skin, you must use keyboard keys Home for the Home button, -ESC for the Back button, and F2 or Page-up for the Menu button.
- -However, due to performance issues in the emulator when running high-resolution screens such as -the one for the WXGA720 skin, we recommend that you primarily use the traditional WVGA800 skin -(hdpi, normal screen) to test your application.
@@ -169,11 +258,10 @@ the one for the WXGA720 skin, we recommend that you primarily use the traditiona
- Android 4.0, Revision 2 (December 2011)
-
Maintenance update. The system version is 4.0.2.
- Android 4.0, Revision 1 (October 2011)
-
Initial release. The system version is 4.0.1.
- Android 3.2, Revision 1 (July 2011)
-
- Android 3.1, Revision 3 (July 2011)
-
- Android 3.1, Revision 2 (May 2011)
-
- Android 3.1, Revision 1 (May 2011)
-
- Android 3.0, Revision 2 (July 2011)
-
-
- Android 3.0, Revision 1 (February 2011)
-
Android 3.0, Revision 1 (February 2011)
+
-
-
- Android 2.3.4, Revision 1 (May 2011)
-
Android 2.3.4, Revision 1 (May 2011)
+
-
- Android 2.3.3, Revision 2 (July 2011)
-
- Android 2.3.3, Revision 1 (February 2011)
-
-
- Android 2.3, Revision 1 (December 2010)
-
Android 2.3, Revision 1 (December 2010)
+
-
- Android {@sdkPlatformVersion}, Revision 3 (July 2011)
-
- Android {@sdkPlatformVersion}, Revision 2 (July 2010)
-
Requires SDK Tools r6 or higher.
@@ -781,11 +859,9 @@ Backup.
- Android {@sdkPlatformVersion}, Revision 1 (May 2010)
-
Adds support for building with Android library projects. See SDK
+ Adds support for building with Android library projects. See SDK
Tools, r6 for information.
To help you understand some fundamental Android APIs and coding practices, a variety of sample -code is available from the Android SDK Manager.
+code is available from the Android SDK Manager. Each version of the Android platform available +from the SDK Manager offers its own set of sample apps.To download the samples:
When the download is complete, you can find the samples sources at this location:
+When the download is complete, you can find the source code for all samples at this location:
-<sdk>/platforms/<android-version>/samples/
+<sdk>/samples/android-<version>/
The {@code <version>} number corresponds to the platform's + API level.
+You can easily create new Android projects with the downloaded samples, modify them -if you'd like, and then run them on an emulator or device.
- \ No newline at end of file +if you'd like, and then run them on an emulator or device. \ No newline at end of file diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd index aa21423fad9..10c622b84f8 100644 --- a/docs/html/tools/sdk/eclipse-adt.jd +++ b/docs/html/tools/sdk/eclipse-adt.jd @@ -95,7 +95,113 @@ padding: 5px 0 0;
+ADT 20.0.3 (August 2012)
+
+ADT 20.0.2 (July 2012)
+
+ADT 20.0.1 (July 2012)
+
ADT 20.0.0 (June 2012)
docs/offline.html in a web browser.index.html in a web browser.
platform-tools/The NDK is a toolset that allows you to implement parts of your app using native-code languages such as C and C++. For certain types of apps, this can be helpful so that you may reuse existing code libraries written in these @@ -62,12 +62,12 @@ page.title=Android NDK
The sections below provide information and notes about successive releases of @@ -115,12 +115,204 @@ padding: 5px 0 0;
+ Android NDK, Revision 8b (July 2012)
+
+ The main features of this release are a new GNU Compiler Collection (GCC) 4.6 toolchain and +GNU Debugger (GDB) 7.3.x which adds debugging support for the Android 4.1 (API Level 16) system +image.
+ ++LOCAL_DISABLE_NO_EXECUTE=true # disable "--noexecstack" and "-z noexecstack" +DISABLE_RELRO=true # disable "-z relro" and "-z now"
See {@code docs/ANDROID-MK.html} for more details.
+
+static const struct {
+ int32_t namesz; /* = 4, sizeof ("GNU") */
+ int32_t descsz; /* = 6 * sizeof(int32_t) */
+ int32_t type; /* = 1 */
+ char name[sizeof "GNU"]; /* = "GNU" */
+ int32_t os; /* = 0 */
+ int32_t major; /* = 2 */
+ int32_t minor; /* = 6 */
+ int32_t teeny; /* = 15 */
+ int32_t os_variant; /* = 1 */
+ int32_t android_api; /* = 3, 4, 5, 8, 9, 14 */
+}
+ This release of the NDK includes support for MIPS ABI and a few additional fixes.
- +.apk file.native-plasma and native-activity,
+
+ native-plasma and native-activity,
to demonstrate how to write a native activity.Installing the NDK on your development computer is straightforward and involves extracting the NDK from its download package.
@@ -1339,7 +1531,7 @@ Software RequirementsYou are now ready to start working with the NDK.
- +Once you've installed the NDK successfully, take a few minutes to read the documentation @@ -1402,13 +1594,13 @@ included with the NDK package.
to use them or any other framework API, you can still write JNI code to do so. - - - - - + + + + +The NDK contains the APIs, documentation, and sample applications that help you write your native code. Specifically:
@@ -1478,9 +1670,9 @@ information)Additionally, the package includes detailed information about the "bionic" C library provided @@ -1562,10 +1754,10 @@ information) offers. - - - - + + + +
The NDK includes sample applications that illustrate how to use native code in your Android @@ -1624,10 +1816,10 @@ information)
android tool to create the build file
for each of the sample projects at <ndk>/samples/<name>/.
Then set up an AVD, if necessary, build your project in the usual way, and run it in the
- emulator.For more information about developing with the Android SDK tools and what you need to do to create, build, and run your applications, see the Overview diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd index 14d1aa48606..f8b5d256b28 100644 --- a/docs/html/tools/sdk/tools-notes.jd +++ b/docs/html/tools/sdk/tools-notes.jd @@ -24,51 +24,82 @@ Tools you are using, refer to the "Installed Packages" listing in the Android SD
For a summary of all known issues in SDK Tools, see http://tools.android.com/knownissues.
- - - -
- SDK Tools, Revision 20 (June 2012)
-
-
+
SDK Tools, Revision 20.0.3 (August 2012)
+
+
SDK Tools, Revision 20.0.1 (July 2012)
+
+
SDK Tools, Revision 20 (June 2012)
+
- SDK Tools, Revision 19 (April 2012)
+
+
SDK Tools, Revision 19 (April 2012)
+
Note: This update of SDK Tools is only available through the Android SDK Manager. Use this tool to download and install this update.
@@ -171,13 +202,13 @@ acceleration.
- SDK Tools, Revision 18 (April 2012)
+
+
SDK Tools, Revision 18 (April 2012)
+
Important: To download the new Android 4.0 system components from the Android SDK Manager, you must first update the SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not, @@ -213,13 +244,13 @@ in some cases.
- SDK Tools, Revision 17 (March 2012)
+
+
SDK Tools, Revision 17 (March 2012)
+
Important: To download the new Android 4.0 system components from the Android SDK Manager, you must first update the SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not, @@ -317,13 +348,13 @@ ignore attribute. (
- SDK Tools, Revision 16 (December 2011)
+
+
SDK Tools, Revision 16 (December 2011)
+
Important: To download the new Android 4.0 system components from the Android SDK Manager, you must first update the SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not, @@ -367,13 +398,13 @@ ignore attribute. (
- SDK Tools, Revision 15 (October 2011)
+
+
SDK Tools, Revision 15 (October 2011)
+
Important: To download the new Android 4.0 system components from the Android SDK Manager, you must first update the SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not, @@ -415,13 +446,13 @@ ignore attribute. (
- SDK Tools, Revision 14 (October 2011)
+
+
SDK Tools, Revision 14 (October 2011)
+
Important: To download the new Android 4.0 system components from the Android SDK Manager, you must first update the SDK tools to revision 14 and restart the Android SDK Manager. If you do not, @@ -470,12 +501,13 @@ site.
-SDK Tools, Revision 13 (September 2011)
-
+
SDK Tools, Revision 13 (September 2011)
+
-SDK Tools, Revision 12 (July 2011)
-
+
SDK Tools, Revision 12 (July 2011)
+
-SDK Tools, Revision 11 (May 2011)
-
+
SDK Tools, Revision 11 (May 2011)
+
-SDK Tools, Revision 10 (February 2011)
-
+
SDK Tools, Revision 10 (February 2011)
+
-SDK Tools, Revision 9 (January 2011)
-
+
SDK Tools, Revision 9 (January 2011)
+
-SDK Tools, Revision 8 (December 2010)
-
+
SDK Tools, Revision 8 (December 2010)
+
-SDK Tools, Revision 7 (September 2010)
-
+
SDK Tools, Revision 7 (September 2010)
+
-SDK Tools, Revision 6 (May 2010)
-
+
SDK Tools, Revision 6 (May 2010)
+
-SDK Tools, Revision 5 (March 2010)
-
+
SDK Tools, Revision 5 (March 2010)
+
-SDK Tools, Revision 4 (December 2009)
-
+
SDK Tools, Revision 4 (December 2009)
+
SDK Tools r4 is compatible with ADT 0.9.5 and later, but not @@ -866,12 +910,13 @@ skin name specified.
-SDK Tools, Revision 3 (October 2009)
-
+
SDK Tools, Revision 3 (October 2009)
+
SDK Tools r3 is compatible with ADT 0.9.4 and later, but not @@ -928,3 +973,4 @@ href="/tools/help/layoutopt.html">layoutopt.
Now you have a complete, functioning accessibility service. Try configuring how it interacts with the user, by adding Android's text-to-speech + href="http://android-developers.blogspot.com/2009/09/introduction-to-text-to-speech-in.html">text-to-speech engine, or using a {@link android.os.Vibrator} to provide haptic feedback!
diff --git a/docs/html/training/basics/activity-lifecycle/starting.jd b/docs/html/training/basics/activity-lifecycle/starting.jd index 1a4bc2da5b4..dd17304d810 100644 --- a/docs/html/training/basics/activity-lifecycle/starting.jd +++ b/docs/html/training/basics/activity-lifecycle/starting.jd @@ -112,7 +112,7 @@ methods.As you'll learn in the following lessons, there are several situtations in which an activity +
As you'll learn in the following lessons, there are several situations in which an activity transitions between different states that are illustrated in figure 1. However, only three of these states can be static. That is, the activity can exist in one of only three states for an extended period of time:
diff --git a/docs/html/training/basics/firstapp/building-ui.jd b/docs/html/training/basics/firstapp/building-ui.jd index f0ec79e8772..bc6c47c1916 100644 --- a/docs/html/training/basics/firstapp/building-ui.jd +++ b/docs/html/training/basics/firstapp/building-ui.jd @@ -18,7 +18,7 @@ next.link=starting-activity.htmlThe graphical user interface for an Android app is built using a hierarchy of {@link android.view.View} and {@link android.view.ViewGroup} objects. {@link android.view.View} objects are -usually UI widgets such as a button or text field and {@link android.view.ViewGroup} objects are +usually UI widgets such as buttons or +text fields and {@link +android.view.ViewGroup} objects are invisible view containers that define how the child views are laid out, such as in a grid or a vertical list.
Android provides an XML vocabulary that corresponds to the subclasses of {@link -android.view.View} and {@link android.view.ViewGroup} so you can define your UI in XML with a -hierarchy of view elements.
+android.view.View} and {@link android.view.ViewGroup} so you can define your UI in XML using +a hierarchy of UI elements.Separating the UI layout into XML files is important for several reasons, -but it's especially important on Android because it allows you to define alternative layouts for +
Declaring your UI layout in XML rather than runtime code is useful for several reasons, +but it's especially important so you can create different layouts for different screen sizes. For example, you can create two versions of a layout and tell the system to use one on "small" screens and the other on "large" screens. For more information, see the class about Supporting Different -Hardware.
+Devices.
+
Figure 1. Illustration of how {@link -android.view.ViewGroup} objects form branches in the layout and contain {@link +android.view.ViewGroup} objects form branches in the layout and contain other {@link android.view.View} objects.
-In this lesson, you'll create a layout in XML that includes a text input field and a +
In this lesson, you'll create a layout in XML that includes a text field and a button. In the following lesson, you'll respond when the button is pressed by sending the content of the text field to another activity.
-Open the main.xml file from the res/layout/
-directory (every new Android project includes this file by default).
Open the activity_main.xml file from the res/layout/
+directory.
Note: In Eclipse, when you open a layout file, you’re first shown -the ADT Layout Editor. This is an editor that helps you build layouts using WYSIWYG tools. For this -lesson, you’re going to work directly with the XML, so click the main.xml tab at +the Graphical Layout editor. This is an editor that helps you build layouts using WYSIWYG tools. For this +lesson, you’re going to work directly with the XML, so click the activity_main.xml tab at the bottom of the screen to open the XML editor.
-By default, the main.xml file includes a layout with a {@link
-android.widget.LinearLayout} root view group and a {@link android.widget.TextView} child view.
-You’re going to re-use the {@link android.widget.LinearLayout} in this lesson, but change its
-contents and layout orientation.
The BlankActivity template you used to start this project creates the
+activity_main.xml file with a {@link
+android.widget.RelativeLayout} root view and a {@link android.widget.TextView} child view.
First, delete the {@link android.widget.TextView} element and change the value +
First, delete the {@link android.widget.TextView <TextView>} element and change the {@link
+ android.widget.RelativeLayout <RelativeLayout>} element to {@link
+ android.widget.LinearLayout <LinearLayout>}. Then add the
{@code
-android:orientation} to be "horizontal". The result looks like this:
"horizontal".
+The result looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
@@ -116,26 +120,18 @@ android:layout_height}, are required for all views in order to specify their
Because the {@link android.widget.LinearLayout} is the root view in the layout, it should fill
the entire screen area that's
available to the app by setting the width and height to
-"fill_parent".
Note: Beginning with Android 2.2 (API level 8),
-"fill_parent" has been renamed "match_parent" to better reflect the
-behavior. The reason is that if you set a view to "fill_parent" it does not expand to
-fill the remaining space after sibling views are considered, but instead expands to
-match the size of the parent view no matter what—it will overlap any sibling
-views.
"match_parent". This value declares that the view should expand its width
+or height to match the width or height of the parent view.
For more information about layout properties, see the XML Layout guide.
+href="{@docRoot}guide/topics/ui/declaring-layout.html">Layout guide.To create a user-editable text field, add an {@link android.widget.EditText -<EditText>} element inside the {@link android.widget.LinearLayout <LinearLayout>}. The {@link -android.widget.EditText} class is a subclass of {@link android.view.View} that displays an editable -text field.
+<EditText>} element inside the {@link android.widget.LinearLayout <LinearLayout>}.Like every {@link android.view.View} object, you must define certain XML attributes to specify the {@link android.widget.EditText} object's properties. Here’s how you should declare it @@ -164,6 +160,8 @@ href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code android which allows you to reference that view from other code.
The SDK tools generate the {@code R.java} each time you compile your app. You should never modify this file by hand.
+For more information, read the guide to Providing Resources.
@@ -175,17 +173,18 @@ modify this file by hand. from your app code, such as to read and manipulate the object (you'll see this in the next lesson). -The at-symbol (@) is required when you want to refer to a resource object from
-XML, followed by the resource type ({@code id} in this case), then the resource name ({@code
-edit_message}). (Other resources can use the same name as long as they are not the same
-resource type—for example, the string resource uses the same name.)
The plus-symbol (+) is needed only when you're defining a resource ID for the
-first time. It tells the SDK tools that the resource ID needs to be created. Thus, when the app is
-compiled, the SDK tools use the ID value, edit_message, to create a new identifier in
-your project's {@code gen/R.java} file that is now associated with the {@link
-android.widget.EditText} element. Once the resource ID is created, other references to the ID do not
-need the plus symbol. This is the only attribute that may need the plus-symbol. See the sidebox for
+
The at sign (@) is required when you're referring to any resource object from
+XML. It is followed by the resource type ({@code id} in this case), a slash, then the resource name
+({@code edit_message}).
The plus sign (+) before the resource type is needed only when you're defining a
+resource ID for the first time. When you compile the app,
+the SDK tools use the ID name to create a new resource ID in
+your project's {@code gen/R.java} file that refers to the {@link
+android.widget.EditText} element. Once the resource ID is declared once this way,
+other references to the ID do not
+need the plus sign. Using the plus sign is necessary only when specifying a new resource ID and not
+needed for concrete resources such as strings or layouts. See the sidebox for
more information about resource objects.
"wrap_content" value
specifies that the view should be only as big as needed to fit the contents of the view. If you
-were to instead use "fill_parent", then the {@link android.widget.EditText}
-element would fill the screen, because it'd match the size of the parent {@link
+were to instead use "match_parent", then the {@link android.widget.EditText}
+element would fill the screen, because it would match the size of the parent {@link
android.widget.LinearLayout}. For more information, see the XML Layouts guide.Note: This string resource has the same name as the element ID: +{@code edit_message}. However, references +to resources are always scoped by the resource type (such as {@code id} or {@code string}), so using +the same name does not cause collisions.
+When you need to add text in the user interface, you should always specify each string of text in -a resource file. String resources allow you to maintain a single location for all string -values, which makes it easier to find and update text. Externalizing the strings also allows you to +
When you need to add text in the user interface, you should always specify each string as +a resource. String resources allow you to manage all UI text in a single location, +which makes it easier to find and update text. Externalizing the strings also allows you to localize your app to different languages by providing alternative definitions for each -string.
+string resource.By default, your Android project includes a string resource file at
-res/values/strings.xml. Open this file, delete the existing "hello"
-string, and add one for the
-"edit_message" string used by the {@link android.widget.EditText <EditText>}
-element.
res/values/strings.xml. Open this file and delete the {@code <string>} element
+named "hello_world". Then add a new one named
+"edit_message" and set the value to "Enter a message."
-While you’re in this file, also add a string for the button you’ll soon add, called +
While you’re in this file, also add a "Send" string for the button you’ll soon add, called
"button_send".
The result for strings.xml looks like this:
For more information about using string resources to localize your app for several languages, +
For more information about using string resources to localize your app for other languages, see the Supporting Various Devices +href="{@docRoot}training/basics/supporting-devices/index.html">Supporting Different Devices class.
@@ -280,23 +284,26 @@ android.widget.Button} widgets have their widths set to"wrap_content".
This works fine for the button, but not as well for the text field, because the user might type -something longer and there's extra space left on the screen. So, it'd be nice to fill that width -using the text field. -{@link android.widget.LinearLayout} enables such a design with the weight property, which +something longer. So, it would be nice to fill the unused screen width +with the text field. You can do this inside a +{@link android.widget.LinearLayout} with the weight property, which you can specify using the {@code android:layout_weight} attribute.
-The weight value allows you to specify the amount of remaining space each view should consume, -relative to the amount consumed by sibling views, just like the ingredients in a drink recipe: "2 +
The weight value is a number that specifies the amount of remaining space each view should +consume, +relative to the amount consumed by sibling views. This works kind of like the +amount of ingredients in a drink recipe: "2 parts vodka, 1 part coffee liqueur" means two-thirds of the drink is vodka. For example, if you give -one view a weight of 2 and another one a weight of 1, the sum is 3, so the first view gets 2/3 of -the remaining space and the second view gets the rest. If you give a third view a weight of 1, -then the first view now gets 1/2 the remaining space, while the remaining two each get 1/4.
+one view a weight of 2 and another one a weight of 1, the sum is 3, so the first view fills 2/3 of +the remaining space and the second view fills the rest. If you add a third view and give it a weight +of 1, then the first view (with weight of 2) now gets 1/2 the remaining space, while the remaining +two each get 1/4.The default weight for all views is 0, so if you specify any weight value -greater than 0 to only one view, then that view fills whatever space remains after each view is -given the space it requires. So, to fill the remaining space with the {@link +greater than 0 to only one view, then that view fills whatever space remains after all views are +given the space they require. So, to fill the remaining space in your layout with the {@link android.widget.EditText} element, give it a weight of 1 and leave the button with no weight.
@@ -331,8 +338,9 @@ android.widget.LinearLayout}.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:orientation="horizontal">
<EditText android:id="@+id/edit_message"
android:layout_weight="1"
@@ -351,7 +359,8 @@ that the SDK tools generated when you created the project, so you can now run th
results:
from the toolbar.diff --git a/docs/html/training/basics/firstapp/creating-project.jd b/docs/html/training/basics/firstapp/creating-project.jd index 4fbfe3490a8..2ea8b2f8414 100644 --- a/docs/html/training/basics/firstapp/creating-project.jd +++ b/docs/html/training/basics/firstapp/creating-project.jd @@ -34,66 +34,77 @@ SDK
An Android project contains all the files that comprise the source code for your Android app. The Android SDK tools make it easy to start a new Android project with a set of -default project directories and files.
+default project directories and files.This lesson shows how to create a new project either using Eclipse (with the ADT plugin) or using the SDK tools from a command line.
Note: You should already have the Android SDK installed, and if -you're using Eclipse, you should have installed the ADT plugin as well. If you have not installed -these, see Installing the Android SDK and return here -when you've completed the installation.
+you're using Eclipse, you should also have the ADT +plugin installed (version 20.0.0 or higher). If you don't have these, follow the guide to Installing the Android SDK before you start this +lesson.
+ in the toolbar. (If you don’t see this button,
+then you have not installed the ADT plugin—see Installing the Eclipse Plugin.)
+
-Figure 1. The new project wizard in Eclipse.
+Figure 1. The New Android App Project wizard in Eclipse.
We recommend that you select the latest version possible. You can still build your app to -support older versions, but setting the build target to the latest version allows you to -easily optimize your app for a great user experience on the latest Android-powered devices.
-If you don't see any built targets listed, you need to install some using the Android SDK -Manager tool. See step 4 in the -installing guide.
-Click Next.
Because this version is lower than the build target selected for the app, a warning -appears, but that's alright. You simply need to be sure that you don't use any APIs that require an -API level greater than the minimum SDK -version without first using some code to verify the device's system version (you'll see this in some -other classes).
-Leave this set to the default value for this project.
-Click Finish.
+Click Next.
+You can customize an icon in several ways and the tool generates an icon for all + screen densities. Before you publish your app, you should be sure your icon meets + the specifications defined in the Iconography + design guide.
+Click Next.
+For this project, select BlankActivity and click Next.
Your Android project is now set up with some default files and you’re ready to begin @@ -104,7 +115,7 @@ building the app. Continue to the next lesson.
Create a Project with Command Line ToolsIf you're not using the Eclipse IDE with the ADT plugin, you can instead create your project -using the SDK tools in a command line:
+using the SDK tools from a command line:tools/ path.If you don't see any targets listed, you need to install some using the Android SDK -Manager tool. See step 4 in the -installing guide.
+Manager tool. See Adding Platforms + and Packages.android create project --target <target-id> --name MyFirstApp \ ---path <path-to-workspace>/MyFirstApp --activity MyFirstActivity \ ---package com.example.myapp +--path <path-to-workspace>/MyFirstApp --activity MainActivity \ +--package com.example.myfirstapp
Replace <target-id> with an id from the list of targets (from the previous step)
and replace
diff --git a/docs/html/training/basics/firstapp/index.jd b/docs/html/training/basics/firstapp/index.jd
index 43b289bfb05..4c1a0dc3535 100644
--- a/docs/html/training/basics/firstapp/index.jd
+++ b/docs/html/training/basics/firstapp/index.jd
@@ -14,8 +14,9 @@ next.link=creating-project.html
Before you start this class, be sure that you have your development environment set up. You need +
Before you start this class, be sure you have your development environment set up. You need to:
If you haven't already done this setup, read Installing -the SDK. Once you've finished the setup, you're ready to begin this class.
+If you haven't already done these tasks, start by downloading the + Android SDK and following the install steps. + Once you've finished the setup, you're ready to begin this class.
-This class uses a tutorial format that incrementally builds a small Android app in order to teach +
This class uses a tutorial format that incrementally builds a small Android app that teaches you some fundamental concepts about Android development, so it's important that you follow each step.
- -If you followed the previous lesson to create an Android project, it includes a default set of "Hello World" source files that allow you to -run the app right away.
+immediately run the app.How you run your app depends on two things: whether you have a real Android-powered device and whether you’re using Eclipse. This lesson shows you how to install and run your app on a @@ -49,14 +49,16 @@ project:
AndroidManifest.xmlsrc/res/drawable-hdpi/When you build and run the default Android project, the default {@link android.app.Activity}
-class in the src/ directory starts and loads a layout file from the
-layout/ directory, which includes a "Hello World" message. Not real exciting, but it's
-important that you understand how to build and run your app before adding real functionality to
-the app.
When you build and run the default Android app, the default {@link android.app.Activity} +class starts and loads a layout file +that says "Hello World." The result is nothing exciting, but it's +important that you understand how to run your app before you start developing.
Whether you’re using Eclipse or the command line, you need to:
+If you have a real Android-powered device, here's how you can install and run your app:
To run the app from Eclipse, open one of your project's files and click
-Run from the toolbar. Eclipse installs the app on your connected device and starts
+Run
+from the toolbar. Eclipse installs the app on your connected device and starts
it.
To start adding stuff to the app, continue to the next
+ That's how you build and run your Android app on a device!
+ To start developing, continue to the next
lesson. Whether you’re using Eclipse or the command line, you need to first create an Android Virtual
-Device (AVD). An AVD is a
-device configuration for the Android emulator that allows you to model
-different device configurations. Whether you’re using Eclipse or the command line, to run your app on the emulator you need to
+first create an Android Virtual Device (AVD). An
+AVD is a device configuration for the Android emulator that allows you to model different
+devices. To run the app from Eclipse, open one of your project's files and click
-Run from the toolbar. Eclipse installs the app on your AVD and starts it. Or to run your app from the command line: To start adding stuff to the app, continue to the next
+ That's how you build and run your Android app on the emulator!
+ To start developing, continue to the next
lesson. After completing the previous lesson, you have an app that
shows an activity (a single screen) with a text field and a button. In this lesson, you’ll add some
-code to The {@code
-android:onClick} attribute’s value, Add the corresponding method inside the Open the Tip: In Eclipse, press Ctrl + Shift + O to import missing classes
(Cmd + Shift + O on Mac). Note that, in order for the system to match this method to the method name given to In order for the system to match this method to the method name given to {@code android:onClick},
the signature must be exactly as shown. Specifically, the method must: An {@link android.content.Intent} is an object that provides runtime binding between separate
components (such as two activities). The {@link android.content.Intent} represents an
-app’s "intent to do something." You can use an {@link android.content.Intent} for a wide
+app’s "intent to do something." You can use intents for a wide
variety of tasks, but most often they’re used to start another activity. Inside the {@code sendMessage()} method, create an {@link android.content.Intent} to start
-an activity called {@code DisplayMessageActvity}:Run on the Emulator
-
@@ -131,13 +133,15 @@ devices.
-
<sdk>/tools/ and execute:
-./android avd
from the toolbar.<sdk>/tools/ and execute:
+android avd
+from the toolbar. Eclipse installs the app on your AVD and starts it.
MyFirstActivity that
-starts a new activity when the user selects the Send button.MainActivity that
+starts a new activity when the user clicks the Send button.
Respond to the Send Button
@@ -64,13 +64,13 @@ attribute to the {@link android.widget.Button <Button>} element:
sendMessage, is the name of a method in your
-activity that you want to call when the user selects the button."sendMessage", is the name of a method in your
+activity that the system calls when the user clicks the button.
-MyFirstActivity class:MainActivity class and add the corresponding method:
-/** Called when the user selects the Send button */
+/** Called when the user clicks the Send button */
public void sendMessage(View view) {
// Do something in response to button
}
@@ -79,7 +79,7 @@ public void sendMessage(View view) {
Intent intent = new Intent(this, DisplayMessageActivity.class);
@@ -127,7 +127,7 @@ specifies the exact app component to which the intent should be given. However,
can also be implicit, in which case the {@link android.content.Intent} does not specify
the desired component, but allows any app installed on the device to respond to the intent
as long as it satisfies the meta-data specifications for the action that's specified in various
-{@link android.content.Intent} parameters. For more informations, see the class about Interacting with Other Apps.
An intent not only allows you to start another activity, but can carry a bundle of data to the +
An intent not only allows you to start another activity, but it can carry a bundle of data to the activity as well. So, use {@link android.app.Activity#findViewById findViewById()} to get the -{@link android.widget.EditText} element and add its message to the intent:
+{@link android.widget.EditText} element and add its text value to the intent:Intent intent = new Intent(this, DisplayMessageActivity.class); @@ -148,37 +148,36 @@ intent.putExtra(EXTRA_MESSAGE, message);
An {@link android.content.Intent} can carry a collection of various data types as key-value -pairs called extras. The {@link android.content.Intent#putExtra putExtra()} method takes a -string as the key and the value in the second parameter.
+pairs called extras. The {@link android.content.Intent#putExtra putExtra()} method takes the +key name in the first parameter and the value in the second parameter. -In order for the next activity to query the extra data, you should define your keys using a +
In order for the next activity to query the extra data, you should define your key using a public constant. So add the {@code EXTRA_MESSAGE} definition to the top of the {@code -MyFirstActivity} class:
+MainActivity} class:
-public class MyFirstActivity extends Activity {
- public final static String EXTRA_MESSAGE = "com.example.myapp.MESSAGE";
+public class MainActivity extends Activity {
+ public final static String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE";
...
}
-It's generally a good practice to define keys for extras with your app's package name as a prefix -to ensure it's unique, in case your app interacts with other apps.
+It's generally a good practice to define keys for intent extras using your app's package name +as a prefix. This ensures they are unique, in case your app interacts with other apps.
To start an activity, you simply need to call {@link android.app.Activity#startActivity -startActivity()} and pass it your {@link android.content.Intent}.
- -The system receives this call and starts an instance of the {@link android.app.Activity} +startActivity()} and pass it your {@link android.content.Intent}. The system receives this call +and starts an instance of the {@link android.app.Activity} specified by the {@link android.content.Intent}.
-With this method included, the complete {@code sendMessage()} method that's invoked by the Send +
With this new code, the complete {@code sendMessage()} method that's invoked by the Send button now looks like this:
-/** Called when the user selects the Send button */
+/** Called when the user clicks the Send button */
public void sendMessage(View view) {
Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.edit_message);
@@ -195,20 +194,48 @@ work.
Create the Second Activity
-In your project, create a new class file under the src/<package-name>/
-directory called DisplayMessageActivity.java.
+
+
+Figure 1. The new activity wizard in Eclipse.
+
-Tip: In Eclipse, right-click the package name under the
-src/ directory and select New > Class.
-Enter "DisplayMessageActivity" for the name and {@code android.app.Activity} for the superclass.
+To create a new activity using Eclipse:
-Inside the class, add the {@link android.app.Activity#onCreate onCreate()} callback method:
+
+ - Click New
in the toolbar.
+ - In the window that appears, open the Android folder
+ and select Android Activity. Click Next.
+ - Select BlankActivity and click Next.
+ - Fill in the activity details:
+
+ - Project: MyFirstApp
+ - Activity Name: DisplayMessageActivity
+ - Layout Name: activity_display_message
+ - Navigation Type: None
+ - Hierarchial Parent: com.example.myfirstapp.MainActivity
+ - Title: My Message
+
+ Click Finish.
+
+
+
+If you're using a different IDE or the command line tools, create a new file named
+{@code DisplayMessageActivity.java} in the project's src/ directory, next to
+the original {@code MainActivity.java} file.
+
+Open the {@code DisplayMessageActivity.java} file. If you used Eclipse to create it, the class
+already includes an implementation of the required {@link android.app.Activity#onCreate onCreate()}
+method. There's also an implemtation of the {@link android.app.Activity#onCreateOptionsMenu
+onCreateOptionsMenu()} method, but
+you won't need it for this app so you can remove it. The class should look like this:
public class DisplayMessageActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_display_message);
}
}
@@ -216,7 +243,7 @@ public class DisplayMessageActivity extends Activity {
All subclasses of {@link android.app.Activity} must implement the {@link
android.app.Activity#onCreate onCreate()} method. The system calls this when creating a new
instance of the activity. It is where you must define the activity layout and where you should
-initialize essential activity components.
+perform initial setup for the activity components.
@@ -226,38 +253,55 @@ initialize essential activity components.
{@code <activity>} element.
-Because {@code DisplayMessageActivity} is invoked using an explicit intent, it does not require
-any intent filters (such as those you can see in the manifest for MyFirstActivity). So
-the declaration for DisplayMessageActivity can be simply one line of code inside the {@code <application>}
-element:
+When you use the Eclipse tools to create the activity, it creates a default entry. It should
+look like this:
<application ... >
- <activity android:name="com.example.myapp.DisplayMessageActivity" />
...
+ <activity
+ android:name=".DisplayMessageActivity"
+ android:label="@string/title_activity_display_message" >
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.example.myfirstapp.MainActivity" />
+ </activity>
</application>
+The {@code
+ <meta-data>} element declares the name of this activity's parent activity
+ within the app's logical hierarchy. The Android Support Library uses this information
+ to implement default navigation behaviors, such as Up navigation.
+
+Note: During installation, you should have downloaded
+the latest Support Library. Eclipse automatically includes this library in your app project (you
+can see the library's JAR file listed under Android Dependencies). If you're not using
+Eclipse, you may need to manually add the library to your project—follow this guide for setting up the Support Library.
+
The app is now runnable because the {@link android.content.Intent} in the
first activity now resolves to the {@code DisplayMessageActivity} class. If you run the app now,
-pressing the Send button starts the
-second activity, but it doesn't show anything yet.
+clicking the Send button starts the second activity, but it's still using the default
+"Hello world" layout.
Receive the Intent
Every {@link android.app.Activity} is invoked by an {@link android.content.Intent}, regardless of
how the user navigated there. You can get the {@link android.content.Intent} that started your
-activity by calling {@link android.app.Activity#getIntent()} and the retrieve data contained
+activity by calling {@link android.app.Activity#getIntent()} and retrieve the data contained
within it.
In the {@code DisplayMessageActivity} class’s {@link android.app.Activity#onCreate onCreate()}
-method, get the intent and extract the message delivered by {@code MyFirstActivity}:
+method, get the intent and extract the message delivered by {@code MainActivity}:
Intent intent = getIntent();
-String message = intent.getStringExtra(MyFirstActivity.EXTRA_MESSAGE);
+String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
@@ -279,22 +323,23 @@ public void onCreate(Bundle savedInstanceState) {
// Get the message from the intent
Intent intent = getIntent();
- String message = intent.getStringExtra(MyFirstActivity.EXTRA_MESSAGE);
+ String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
// Create the text view
TextView textView = new TextView(this);
textView.setTextSize(40);
textView.setText(message);
+ // Set the text view as the activity layout
setContentView(textView);
}
-You can now run the app, type a message in the text field, press Send, and view the message on -the second activity.
+You can now run the app. When it opens, type a message in the text field, click Send, + and the message appears on the second activity.
-Figure 1. Both activities in the final app, running +
Figure 2. Both activities in the final app, running on Android 4.0.
That's it, you've built your first Android app!
diff --git a/docs/html/training/basics/fragments/communicating.jd b/docs/html/training/basics/fragments/communicating.jd index 3ac987372ec..eb9b3689fc8 100644 --- a/docs/html/training/basics/fragments/communicating.jd +++ b/docs/html/training/basics/fragments/communicating.jd @@ -108,7 +108,7 @@ public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... - public void onArticleSelected(Uri articleUri) { + public void onArticleSelected(int position) { // The user selected the headline of an article from the HeadlinesFragment // Do something here to display that article } diff --git a/docs/html/training/basics/network-ops/connecting.jd b/docs/html/training/basics/network-ops/connecting.jd index f70cf58989f..ac8d993d3e3 100644 --- a/docs/html/training/basics/network-ops/connecting.jd +++ b/docs/html/training/basics/network-ops/connecting.jd @@ -66,7 +66,7 @@ and {@link android.net.NetworkInfo#isConnected isConnected()}. Remember, the device may be out of range of a network, or the user may have disabled both Wi-Fi and mobile data access. For more discussion of this topic, see the lesson Managing Network +href="{@docRoot}training/basics/network-ops/managing.html">Managing Network Usage.diff --git a/docs/html/training/connect-devices-wirelessly/index.jd b/docs/html/training/connect-devices-wirelessly/index.jd new file mode 100644 index 00000000000..37cf633a453 --- /dev/null +++ b/docs/html/training/connect-devices-wirelessly/index.jd @@ -0,0 +1,62 @@ +page.title=Connecting Devices Wirelessly + +trainingnavtop=true +startpage=true +next.title=Using Network Service Discovery +next.link=nsd.html + +@jd:body + + +++ + ++ ++Dependencies and prerequisites
++
+ +- Android 4.1 or higher
+You should also read
++
+ + +- Wi-Fi Direct
+Besides enabling communication with the cloud, Android's wireless APIs also +enable communication with other devices on the same local network, and even +devices which are not on a network, but are physically nearby. The addition of +Network Service Discovery (NSD) takes this further by allowing an application to +seek out a nearby device running services with which it can communicate. +Integrating this functionality into your application helps you provide a wide range +of features, such as playing games with users in the same room, pulling +images from a networked NSD-enabled webcam, or remotely logging into +other machines on the same network.
+This class describes the key APIs for finding and +connecting to other devices from your application. Specifically, it +describes the NSD API for discovering available services and the Wi-Fi +Direct™ API for doing peer-to-peer wireless connections. This class also +shows you how to use NSD and Wi-Fi Direct in +combination to detect the services offered by a device and connect to the +device when neither device is connected to a network. +
+Lessons
+ +
The first lesson in this class, Using Network Service + Discovery, showed you +how to discover services that are connected to a local network. However, using +Wi-Fi Direct&trad; Service Discovery allows you to discover the services of nearby devices directly, +without being connected to a network. You can also advertise the services +running on your device. These capabilities help you communicate between apps, +even when no local network or hotspot is available.
+While this set of APIs is similar in purpose to the Network Service Discovery +APIs outlined in a previous lesson, implementing them in code is very different. +This lesson shows you how to discover services available from other devices, +using Wi-Fi Direct™. The lesson assumes that you're already familiar with the +Wi-Fi Direct API.
+ + +In order to use Wi-Fi Direct, add the {@link +android.Manifest.permission#CHANGE_WIFI_STATE}, {@link +android.Manifest.permission#ACCESS_WIFI_STATE}, +and {@link android.Manifest.permission#INTERNET} +permissions to your manifest. Even though Wi-Fi Direct doesn't require an +Internet connection, it uses standard Java sockets, and using these in Android +requires the requested permissions.
+ ++<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.nsdchat" + ... + + <uses-permission + android:required="true" + android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.CHANGE_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.INTERNET"/> + ... ++ +
If you're providing a local service, you need to register it for +service discovery. Once your local service is registered, the framework +automatically responds to service discovery requests from peers.
+ +To create a local service:
+ +
+ private void startRegistration() {
+ // Create a string map containing information about your service.
+ Map record = new HashMap();
+ record.put("listenport", String.valueOf(SERVER_PORT));
+ record.put("buddyname", "John Doe" + (int) (Math.random() * 1000));
+ record.put("available", "visible");
+
+ // Service information. Pass it an instance name, service type
+ // _protocol._transportlayer , and the map containing
+ // information other devices will want once they connect to this one.
+ WifiP2pDnsSdServiceInfo serviceInfo =
+ WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record);
+
+ // Add the local service, sending the service info, network channel,
+ // and listener that will be used to indicate success or failure of
+ // the request.
+ mManager.addLocalService(channel, serviceInfo, new ActionListener() {
+ @Override
+ public void onSuccess() {
+ // Command successful! Code isn't necessarily needed here,
+ // Unless you want to update the UI or add logging statements.
+ }
+
+ @Override
+ public void onFailure(int arg0) {
+ // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
+ }
+ });
+ }
+
+
+Android uses callback methods to notify your application of available services, so +the first thing to do is set those up. Create a {@link +android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener} to listen for +incoming records. This record can optionally be broadcast by other +devices. When one comes in, copy the device address and any other +relevant information you want into a data structure external to the current +method, so you can access it later. The following example assumes that the +record contains a "buddyname" field, populated with the user's identity.
+ +
+final HashMap<String, String> buddies = new HashMap<String, String>();
+...
+private void discoverService() {
+ DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
+ @Override
+ /* Callback includes:
+ * fullDomain: full domain name: e.g "printer._ipp._tcp.local."
+ * record: TXT record dta as a map of key/value pairs.
+ * device: The device running the advertised service.
+ */
+
+ public void onDnsSdTxtRecordAvailable(
+ String fullDomain, Map record, WifiP2pDevice device) {
+ Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
+ buddies.put(device.deviceAddress, record.get("buddyname"));
+ }
+ };
+ ...
+}
+
+
+To get the service information, create a {@link +android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener}. This +receives the actual description and connection information. The previous code +snippet implemented a {@link java.util.Map} object to pair a device address with the buddy +name. The service response listener uses this to link the DNS record with the +corresponding service information. Once both +listeners are implemented, add them to the {@link +android.net.wifi.p2p.WifiP2pManager} using the {@link +android.net.wifi.p2p.WifiP2pManager#setDnsSdResponseListeners(WifiP2pManager.Channel, +WifiP2pManager.DnsSdServiceResponseListener, +WifiP2pManager.DnsSdTxtRecordListener) setDnsSdResponseListeners()} method.
+ +
+private void discoverService() {
+...
+
+ DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
+ @Override
+ public void onDnsSdServiceAvailable(String instanceName, String registrationType,
+ WifiP2pDevice resourceType) {
+
+ // Update the device name with the human-friendly version from
+ // the DnsTxtRecord, assuming one arrived.
+ resourceType.deviceName = buddies
+ .containsKey(resourceType.deviceAddress) ? buddies
+ .get(resourceType.deviceAddress) : resourceType.deviceName;
+
+ // Add to the custom adapter defined specifically for showing
+ // wifi devices.
+ WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
+ .findFragmentById(R.id.frag_peerlist);
+ WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
+ .getListAdapter());
+
+ adapter.add(resourceType);
+ adapter.notifyDataSetChanged();
+ Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
+ }
+ };
+
+ mManager.setDnsSdResponseListeners(channel, servListener, txtListener);
+ ...
+}
+
+
+Now create a service request and call {@link +android.net.wifi.p2p.WifiP2pManager#addServiceRequest(WifiP2pManager.Channel, +WifiP2pServiceRequest, WifiP2pManager.ActionListener) addServiceRequest()}. +This method also takes a listener to report success or failure.
+ +
+ serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
+ mManager.addServiceRequest(channel,
+ serviceRequest,
+ new ActionListener() {
+ @Override
+ public void onSuccess() {
+ // Success!
+ }
+
+ @Override
+ public void onFailure(int code) {
+ // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
+ }
+ });
+
+
+Finally, make the call to {@link +android.net.wifi.p2p.WifiP2pManager#discoverServices(WifiP2pManager.Channel, +WifiP2pManager.ActionListener) discoverServices()}.
+ +
+ mManager.discoverServices(channel, new ActionListener() {
+
+ @Override
+ public void onSuccess() {
+ // Success!
+ }
+
+ @Override
+ public void onFailure(int code) {
+ // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
+ if (code == WifiP2pManager.P2P_UNSUPPORTED) {
+ Log.d(TAG, "P2P isn't supported on this device.");
+ else if(...)
+ ...
+ }
+ });
+
+
+If all goes well, hooray, you're done! If you encounter problems, remember +that the asynchronous calls you've made take an +{@link android.net.wifi.p2p.WifiP2pManager.ActionListener} as an argument, and +this provides you with callbacks indicating success or failure. To diagnose +problems, put debugging code in {@link +android.net.wifi.p2p.WifiP2pManager.ActionListener#onFailure(int) onFailure()}. The error code +provided by the method hints at the problem. Here are the possible error values +and what they mean
+nsdchat.zip
+Adding Network Service Discovery (NSD) to your app allows your users to +identify other devices on the local network that support the services your app +requests. This is useful for a variety of peer-to-peer applications such as file +sharing or multi-player gaming. Android's NSD APIs simplify the effort required +for you to implement such features.
+ +This lesson shows you how to build an application that can broadcast its +name and connection information to the local network and scan for information +from other applications doing the same. Finally, this lesson shows you how +to connect to the same application running on another device.
+ +Note: This step is optional. If +you don't care about broadcasting your app's services over the local network, +you can skip forward to the +next section, Discover Services on the Network.
+ +To register your service on the local network, first create a {@link +android.net.nsd.NsdServiceInfo} object. This object provides the information +that other devices on the network use when they're deciding whether to connect to your +service.
+ +
+public void registerService(int port) {
+ // Create the NsdServiceInfo object, and populate it.
+ NsdServiceInfo serviceInfo = new NsdServiceInfo();
+
+ // The name is subject to change based on conflicts
+ // with other services advertised on the same network.
+ serviceInfo.setServiceName("NsdChat");
+ serviceInfo.setServiceType("_http._tcp.");
+ serviceInfo.setPort(port);
+ ....
+}
+
+
+This code snippet sets the service name to "NsdChat". +The name is visible to any device on the network that is using NSD to look for +local services. Keep in mind that the name must be unique for any service on the +network, and Android automatically handles conflict resolution. If +two devices on the network both have the NsdChat application installed, one of +them changes the service name automatically, to something like "NsdChat +(1)".
+ +The second parameter sets the service type, specifies which protocol and transport +layer the application uses. The syntax is +"_<protocol>._<transportlayer>". In the +code snippet, the service uses HTTP protocol running over TCP. An application +offering a printer service (for instance, a network printer) would set the +service type to "_ipp._tcp".
+ +Note: The International Assigned Numbers +Authority (IANA) manages a centralized, +authoritative list of service types used by service discovery protocols such as NSD and Bonjour. +You can download the list from the +IANA list of service names and port numbers. +If you intend to use a new service type, you should reserve it by filling out +the IANA Ports and Service + registration form.
+ +When setting the port for your service, avoid hardcoding it as this +conflicts with other applications. For instance, assuming +that your application always uses port 1337 puts it in potential conflict with +other installed applications that use the same port. Instead, use the device's +next available port. Because this information is provided to other apps by a +service broadcast, there's no need for the port your application uses to be +known by other applications at compile-time. Instead, the applications can get +this information from your service broadcast, right before connecting to your +service.
+ +If you're working with sockets, here's how you can initialize a socket to any +available port simply by setting it to 0.
+ +
+public void initializeServerSocket() {
+ // Initialize a server socket on the next available port.
+ mServerSocket = new ServerSocket(0);
+
+ // Store the chosen port.
+ mLocalPort = mServerSocket.getLocalPort();
+ ...
+}
+
+
+Now that you've defined the {@link android.net.nsd.NsdServiceInfo +NsdServiceInfo} object, you need to implement the {@link +android.net.nsd.NsdManager.RegistrationListener RegistrationListener} interface. This +interface contains callbacks used by Android to alert your application of the +success or failure of service registration and unregistration. +
+
+public void initializeRegistrationListener() {
+ mRegistrationListener = new NsdManager.RegistrationListener() {
+
+ @Override
+ public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
+ // Save the service name. Android may have changed it in order to
+ // resolve a conflict, so update the name you initially requested
+ // with the name Android actually used.
+ mServiceName = NsdServiceInfo.getServiceName();
+ }
+
+ @Override
+ public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ // Registration failed! Put debugging code here to determine why.
+ }
+
+ @Override
+ public void onServiceUnregistered(NsdServiceInfo arg0) {
+ // Service has been unregistered. This only happens when you call
+ // NsdManager.unregisterService() and pass in this listener.
+ }
+
+ @Override
+ public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ // Unregistration failed. Put debugging code here to determine why.
+ }
+ };
+}
+
+
+Now you have all the pieces to register your service. Call the method +{@link android.net.nsd.NsdManager#registerService registerService()}. +
+ +Note that this method is asynchronous, so any code that needs to run +after the service has been registered must go in the {@link +android.net.nsd.NsdManager.RegistrationListener#onServiceRegistered(NsdServiceInfo) +onServiceRegistered()} method.
+ +
+public void registerService(int port) {
+ NsdServiceInfo serviceInfo = new NsdServiceInfo();
+ serviceInfo.setServiceName("NsdChat");
+ serviceInfo.setServiceType("_http._tcp.");
+ serviceInfo.setPort(port);
+
+ mNsdManager = Context.getSystemService(Context.NSD_SERVICE);
+
+ mNsdManager.registerService(
+ serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
+}
+
+
+The network is teeming with life, from the beastly network printers to the +docile network webcams, to the brutal, fiery battles of nearby tic-tac-toe +players. The key to letting your application see this vibrant ecosystem of +functionality is service discovery. Your application needs to listen to service +broadcasts on the network to see what services are available, and filter out +anything the application can't work with.
+ +Service discovery, like service registration, has two steps: + setting up a discovery listener with the relevant callbacks, and making a single asynchronous +API call to {@link android.net.nsd.NsdManager#discoverServices(String +, int , NsdManager.DiscoveryListener) discoverServices()}.
+ +First, instantiate an anonymous class that implements {@link +android.net.nsd.NsdManager.DiscoveryListener}. The following snippet shows a +simple example:
+ +
+public void initializeDiscoveryListener() {
+
+ // Instantiate a new DiscoveryListener
+ mDiscoveryListener = new NsdManager.DiscoveryListener() {
+
+ // Called as soon as service discovery begins.
+ @Override
+ public void onDiscoveryStarted(String regType) {
+ Log.d(TAG, "Service discovery started");
+ }
+
+ @Override
+ public void onServiceFound(NsdServiceInfo service) {
+ // A service was found! Do something with it.
+ Log.d(TAG, "Service discovery success" + service);
+ if (!service.getServiceType().equals(SERVICE_TYPE)) {
+ // Service type is the string containing the protocol and
+ // transport layer for this service.
+ Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
+ } else if (service.getServiceName().equals(mServiceName)) {
+ // The name of the service tells the user what they'd be
+ // connecting to. It could be "Bob's Chat App".
+ Log.d(TAG, "Same machine: " + mServiceName);
+ } else if (service.getServiceName().contains("NsdChat")){
+ mNsdManager.resolveService(service, mResolveListener);
+ }
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo service) {
+ // When the network service is no longer available.
+ // Internal bookkeeping code goes here.
+ Log.e(TAG, "service lost" + service);
+ }
+
+ @Override
+ public void onDiscoveryStopped(String serviceType) {
+ Log.i(TAG, "Discovery stopped: " + serviceType);
+ }
+
+ @Override
+ public void onStartDiscoveryFailed(String serviceType, int errorCode) {
+ Log.e(TAG, "Discovery failed: Error code:" + errorCode);
+ mNsdManager.stopServiceDiscovery(this);
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+ Log.e(TAG, "Discovery failed: Error code:" + errorCode);
+ mNsdManager.stopServiceDiscovery(this);
+ }
+ };
+}
+
+
+The NSD API uses the methods in this interface to inform your application when discovery +is started, when it fails, and when services are found and lost (lost means "is +no longer available"). Notice that this snippet does several checks +when a service is found.
+Checking the service name isn't always necessary, and is only relevant if you +want to connect to a specific application. For instance, the application might +only want to connect to instances of itself running on other devices. However, if the +application wants to connect to a network printer, it's enough to see that the service type +is "_ipp._tcp".
+ +After setting up the listener, call {@link android.net.nsd.NsdManager#discoverServices(String, int, +NsdManager.DiscoveryListener) discoverServices()}, passing in the service type +your application should look for, the discovery protocol to use, and the +listener you just created.
+ ++ mNsdManager.discoverServices( + SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); ++ + +
When your application finds a service on the network to connect to, it +must first determine the connection information for that service, using the +{@link android.net.nsd.NsdManager#resolveService resolveService()} method. +Implement a {@link android.net.nsd.NsdManager.ResolveListener} to pass into this +method, and use it to get a {@link android.net.nsd.NsdServiceInfo} containing +the connection information.
+ +
+public void initializeResolveListener() {
+ mResolveListener = new NsdManager.ResolveListener() {
+
+ @Override
+ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ // Called when the resolve fails. Use the error code to debug.
+ Log.e(TAG, "Resolve failed" + errorCode);
+ }
+
+ @Override
+ public void onServiceResolved(NsdServiceInfo serviceInfo) {
+ Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
+
+ if (serviceInfo.getServiceName().equals(mServiceName)) {
+ Log.d(TAG, "Same IP.");
+ return;
+ }
+ mService = serviceInfo;
+ int port = mService.getPort();
+ InetAddress host = mService.getHost();
+ }
+ };
+}
+
+
+Once the service is resolved, your application receives detailed +service information including an IP address and port number. This is everything +you need to create your own network connection to the service.
+ + +It's important to enable and disable NSD +functionality as appropriate during the application's +lifecycle. Unregistering your application when it closes down helps prevent +other applications from thinking it's still active and attempting to connect to +it. Also, service discovery is an expensive operation, and should be stopped +when the parent Activity is paused, and re-enabled when the Activity is +resumed. Override the lifecycle methods of your main Activity and insert code +to start and stop service broadcast and discovery as appropriate.
+ +
+//In your application's Activity
+
+ @Override
+ protected void onPause() {
+ if (mNsdHelper != null) {
+ mNsdHelper.tearDown();
+ }
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mNsdHelper != null) {
+ mNsdHelper.registerService(mConnection.getLocalPort());
+ mNsdHelper.discoverServices();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ mNsdHelper.tearDown();
+ mConnection.tearDown();
+ super.onDestroy();
+ }
+
+ // NsdHelper's tearDown method
+ public void tearDown() {
+ mNsdManager.unregisterService(mRegistrationListener);
+ mNsdManager.stopServiceDiscovery(mDiscoveryListener);
+ }
+
+
diff --git a/docs/html/training/connect-devices-wirelessly/wifi-direct.jd b/docs/html/training/connect-devices-wirelessly/wifi-direct.jd
new file mode 100644
index 00000000000..99bb243879a
--- /dev/null
+++ b/docs/html/training/connect-devices-wirelessly/wifi-direct.jd
@@ -0,0 +1,372 @@
+page.title=Connecting with Wi-Fi Direct
+parent.title=Connecting Devices Wirelessly
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Using Network Service Discovery
+previous.link=nsd.html
+next.title=Service Discovery with Wi-Fi Direct
+next.link=nsd-wifi-direct.html
+
+@jd:body
+
+The Wi-Fi Direct™ APIs allow applications to connect to nearby devices without +needing to connect to a network or hotspot. This allows your application to quickly +find and interact with nearby devices, at a range beyond the capabilities of Bluetooth. +
++This lesson shows you how to find and connect to nearby devices using Wi-Fi Direct. +
+In order to use Wi-Fi Direct, add the {@link +android.Manifest.permission#CHANGE_WIFI_STATE}, {@link +android.Manifest.permission#ACCESS_WIFI_STATE}, +and {@link android.Manifest.permission#INTERNET} +permissions to your manifest. Wi-Fi Direct doesn't require an internet connection, +but it does use standard Java sockets, which require the {@link +android.Manifest.permission#INTERNET} permission. +So you need the following permissions to use Wi-Fi Direct.
+ ++<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.nsdchat" + ... + + <uses-permission + android:required="true" + android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.CHANGE_WIFI_STATE"/> + <uses-permission + android:required="true" + android:name="android.permission.INTERNET"/> + ... ++ +
To use Wi-Fi Direct, you need to listen for broadcast intents that tell your +application when certain events have occurred. In your application, instantiate +an {@link +android.content.IntentFilter} and set it to listen for the following:
+
+private final IntentFilter intentFilter = new IntentFilter();
+...
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // Indicates a change in the Wi-Fi Peer-to-Peer status.
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+
+ // Indicates a change in the list of available peers.
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+
+ // Indicates the state of Wi-Fi P2P connectivity has changed.
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+
+ // Indicates this device's details have changed.
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
+
+ ...
+}
+
+
+ At the end of the {@link android.app.Activity#onCreate onCreate()} method, get an instance of the {@link +android.net.wifi.p2p.WifiP2pManager}, and call its {@link +android.net.wifi.p2p.WifiP2pManager#initialize(Context, Looper, WifiP2pManager.ChannelListener) initialize()} +method. This method returns a {@link +android.net.wifi.p2p.WifiP2pManager.Channel} object, which you'll use later to +connect your app to the Wi-Fi Direct Framework.
+ +
+@Override
+
+Channel mChannel;
+
+public void onCreate(Bundle savedInstanceState) {
+ ....
+ mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
+ mChannel = mManager.initialize(this, getMainLooper(), null);
+}
+
+Now create a new {@link +android.content.BroadcastReceiver} class that you'll use to listen for changes +to the System's Wi-Fi P2P state. In the {@link +android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} +method, add a condition to handle each P2P state change listed above.
+ +
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
+ // Determine if Wifi Direct mode is enabled or not, alert
+ // the Activity.
+ int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
+ if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
+ activity.setIsWifiP2pEnabled(true);
+ } else {
+ activity.setIsWifiP2pEnabled(false);
+ }
+ } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
+
+ // The peer list has changed! We should probably do something about
+ // that.
+
+ } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
+
+ // Connection state changed! We should probably do something about
+ // that.
+
+ } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
+ DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
+ .findFragmentById(R.id.frag_list);
+ fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
+ WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
+
+ }
+ }
+
+
+Finally, add code to register the intent filter and broadcast receiver when +your main activity is active, and unregister them when the activity is paused. +The best place to do this is the {@link android.app.Activity#onResume()} and +{@link android.app.Activity#onPause()} methods. + +
+ /** register the BroadcastReceiver with the intent values to be matched */
+ @Override
+ public void onResume() {
+ super.onResume();
+ receiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
+ registerReceiver(receiver, intentFilter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(receiver);
+ }
+
+
+
+To start searching for nearby devices with Wi-Fi Direct, call {@link +android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel, +WifiP2pManager.ActionListener) discoverPeers()}. This method takes the +following arguments:
+
+mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
+
+ @Override
+ public void onSuccess() {
+ // Code for when the discovery initiation is successful goes here.
+ // No services have actually been discovered yet, so this method
+ // can often be left blank. Code for peer discovery goes in the
+ // onReceive method, detailed below.
+ }
+
+ @Override
+ public void onFailure(int reasonCode) {
+ // Code for when the discovery initiation fails goes here.
+ // Alert the user that something went wrong.
+ }
+});
+
+
+Keep in mind that this only initiates peer discovery. The +{@link android.net.wifi.p2p.WifiP2pManager#discoverPeers(WifiP2pManager.Channel, +WifiP2pManager.ActionListener) discoverPeers()} method starts the discovery process and then +immediately returns. The system notifies you if the peer discovery process is +successfully initiated by calling methods in the provided action listener. +Also, discovery will remain active until a connection is initiated or a P2P group is +formed.
+ +Now write the code that fetches and processes the list of peers. First +implement the {@link android.net.wifi.p2p.WifiP2pManager.PeerListListener} +interface, which provides information about the peers that Wi-Fi Direct has +detected. The following code snippet illustrates this.
+ ++ private List+ +peers = new ArrayList (); + ... + + private PeerListListener peerListListener = new PeerListListener() { + @Override + public void onPeersAvailable(WifiP2pDeviceList peerList) { + + // Out with the old, in with the new. + peers.clear(); + peers.addAll(peerList.getDeviceList()); + + // If an AdapterView is backed by this data, notify it + // of the change. For instance, if you have a ListView of available + // peers, trigger an update. + ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged(); + if (peers.size() == 0) { + Log.d(WiFiDirectActivity.TAG, "No devices found"); + return; + } + } + } +
Now modify your broadcast receiver's {@link +android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} +method to call {@link android.net.wifi.p2p.WifiP2pManager#requestPeers +requestPeers()} when an intent with the action {@link +android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} is received. You +need to pass this listener into the receiver somehow. One way is to send it +as an argument to the broadcast receiver's constructor. +
+ +
+public void onReceive(Context context, Intent intent) {
+ ...
+ else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
+
+ // Request available peers from the wifi p2p manager. This is an
+ // asynchronous call and the calling activity is notified with a
+ // callback on PeerListListener.onPeersAvailable()
+ if (mManager != null) {
+ mManager.requestPeers(mChannel, peerListener);
+ }
+ Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
+ }...
+}
+
+
+Now, an intent with the action {@link +android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_PEERS_CHANGED_ACTION} intent will +trigger a request for an updated peer list.
+ +In order to connect to a peer, create a new {@link +android.net.wifi.p2p.WifiP2pConfig} object, and copy data into it from the +{@link android.net.wifi.p2p.WifiP2pDevice} representing the device you want to +connect to. Then call the {@link +android.net.wifi.p2p.WifiP2pManager#connect(WifiP2pManager.Channel, +WifiP2pConfig, WifiP2pManager.ActionListener) connect()} +method.
+ +
+ @Override
+ public void connect() {
+ // Picking the first device found on the network.
+ WifiP2pDevice device = peers.get(0);
+
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = device.deviceAddress;
+ config.wps.setup = WpsInfo.PBC;
+
+ mManager.connect(mChannel, config, new ActionListener() {
+
+ @Override
+ public void onSuccess() {
+ // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+
+The {@link android.net.wifi.p2p.WifiP2pManager.ActionListener} implemented in +this snippet only notifies you when the initiation succeeds or fails. +To listen for changes in connection state, implement the {@link +android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener} interface. Its {@link +android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener#onConnectionInfoAvailable(WifiP2pInfo) +onConnectionInfoAvailable()} +callback will notify you when the state of the connection changes. In cases +where multiple devices are going to be connected to a single device (like a game with +3 or more players, or a chat app), one device will be designated the "group +owner".
+ +
+ @Override
+ public void onConnectionInfoAvailable(final WifiP2pInfo info) {
+
+ // InetAddress from WifiP2pInfo struct.
+ InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());
+
+ // After the group negotiation, we can determine the group owner.
+ if (info.groupFormed && info.isGroupOwner) {
+ // Do whatever tasks are specific to the group owner.
+ // One common case is creating a server thread and accepting
+ // incoming connections.
+ } else if (info.groupFormed) {
+ // The other device acts as the client. In this case,
+ // you'll want to create a client thread that connects to the group
+ // owner.
+ }
+ }
+
+
+Now go back to the {@link +android.content.BroadcastReceiver#onReceive(Context, Intent) onReceive()} method of the broadcast receiver, and modify the section +that listens for a {@link +android.net.wifi.p2p.WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} intent. +When this intent is received, call {@link +android.net.wifi.p2p.WifiP2pManager#requestConnectionInfo(WifiP2pManager.Channel, +WifiP2pManager.ConnectionInfoListener) requestConnectionInfo()}. This is an +asynchronous call, so results will be received by the connection info listener +you provide as a parameter. + +
+ ...
+ } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
+
+ if (mManager == null) {
+ return;
+ }
+
+ NetworkInfo networkInfo = (NetworkInfo) intent
+ .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
+
+ if (networkInfo.isConnected()) {
+
+ // We are connected with the other device, request connection
+ // info to find group owner IP
+
+ mManager.requestConnectionInfo(mChannel, connectionListener);
+ }
+ ...
+
diff --git a/docs/html/training/custom-views/create-view.jd b/docs/html/training/custom-views/create-view.jd
index b0bc8b47824..674bcc942af 100644
--- a/docs/html/training/custom-views/create-view.jd
+++ b/docs/html/training/custom-views/create-view.jd
@@ -61,7 +61,7 @@ well-designed class, though, a custom view should:
existing view
subclasses, such as {@link android.widget.Button}.
-To allow the Android Developer Tools
+ To allow the Android Developer Tools
to interact with your view, at a minimum you must provide a constructor that takes a
{@link android.content.Context} and an {@link android.util.AttributeSet} object as parameters.
This constructor allows the layout editor to create and edit an instance of your view. For more information on creating accessible views, see
-
+
Making Applications Accessible in the Android Developers Guide.
This lesson will examine how your refresh frequency can be varied to best mitigate the effect of background updates on the underlying wireless radio state machine. Every time your app polls your server to check if an update is required, you activate the wireless radio, drawing power unnecessarily, for up to 20 seconds on a typical 3G connection. Android Cloud to Device Messaging (C2DM) is a lightweight mechanism used to transmit data from a server to a particular app instance. Using C2DM, your server can notify your app running on a particular device that there is new data available for it. Google Cloud Messaging for Android (GCM) is a lightweight mechanism used to transmit data from a server to a particular app instance. Using GCM, your server can notify your app running on a particular device that there is new data available for it. Compared to polling, where your app must regularly ping the server to query for new data, this event-driven model allows your app to create a new connection only when it knows there is data to download. The result is a reduction in unnecessary connections, and a reduced latency for updated data within your application. C2DM is implemented using a persistent TCP/IP connection. While it's possible to implement your own push service, it's best practice to use C2DM. This minimizes the number of persistent connections and allows the platform to optimize bandwidth and minimize the associated impact on battery life. GCM is implemented using a persistent TCP/IP connection. While it's possible to implement your own push service, it's best practice to use GCM. This minimizes the number of persistent connections and allows the platform to optimize bandwidth and minimize the associated impact on battery life. Alternatively, for transfers that are failure tolerant (such as regular updates), you can simply ignore failed connection and transfer attempts. Alternatively, for transfers that are failure tolerant (such as regular updates), you can simply ignore failed connection and transfer attempts. Horizontal paging, or swipe views, allow users to swipe horizontally on the current screen to navigate to adjacent screens. This pattern can be implemented using the {@link android.support.v4.view.ViewPager} widget, currently available as part of the Android Support Package. For navigating between sibling screens representing a fixed number of sections, it's best to provide the {@link android.support.v4.view.ViewPager} with a {@link android.support.v4.app.FragmentPagerAdapter}. For horizontal paging across collections of objects, it's best to use a {@link android.support.v4.app.FragmentStatePagerAdapter}, which destroys fragments as the user navigates to other pages, minimizing memory usage. Horizontal paging, or swipe views, allow users to swipe horizontally on the current screen to navigate to adjacent screens. This pattern can be implemented using the {@link android.support.v4.view.ViewPager} widget, currently available as part of the Android Support Package. For navigating between sibling screens representing a fixed number of sections, it's best to provide the {@link android.support.v4.view.ViewPager} with a {@link android.support.v4.app.FragmentPagerAdapter}. For horizontal paging across collections of objects, it's best to use a {@link android.support.v4.app.FragmentStatePagerAdapter}, which destroys fragments as the user navigates to other pages, minimizing memory usage. Below is an example of using a {@link android.support.v4.view.ViewPager} to swipe across a collection of objects.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 81e80282464..df4e67de91e 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -27,6 +27,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemProperties;
import android.util.Log;
/**
@@ -310,6 +311,15 @@ private void audioParamCheck(int audioSource, int sampleRateInHz,
case AudioFormat.ENCODING_PCM_8BIT:
mAudioFormat = audioFormat;
break;
+ case AudioFormat.ENCODING_AMRNB:
+ case AudioFormat.ENCODING_AMRWB:
+ case AudioFormat.ENCODING_EVRC:
+ case AudioFormat.ENCODING_EVRCB:
+ case AudioFormat.ENCODING_EVRCWB:
+ if (SystemProperties.QCOM_HARDWARE) {
+ mAudioFormat = audioFormat;
+ break;
+ }
default:
mAudioFormat = AudioFormat.ENCODING_INVALID;
throw (new IllegalArgumentException("Unsupported sample encoding."
@@ -476,7 +486,12 @@ static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int au
}
// PCM_8BIT is not supported at the moment
- if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) {
+ if (audioFormat != AudioFormat.ENCODING_PCM_16BIT
+ && audioFormat != AudioFormat.ENCODING_AMRNB
+ && audioFormat != AudioFormat.ENCODING_AMRWB
+ && audioFormat != AudioFormat.ENCODING_EVRC
+ && audioFormat != AudioFormat.ENCODING_EVRCB
+ && audioFormat != AudioFormat.ENCODING_EVRCWB) {
loge("getMinBufferSize(): Invalid audio format.");
return AudioRecord.ERROR_BAD_VALUE;
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2e153ddc0ea..e7040b02102 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -67,7 +67,10 @@
import android.util.Log;
import android.view.KeyEvent;
import android.view.VolumePanel;
+import android.provider.Settings.SettingNotFoundException;
+import android.content.res.Resources;
+import com.android.internal.app.ThemeUtils;
import com.android.internal.telephony.ITelephony;
import java.io.FileDescriptor;
@@ -113,6 +116,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
/** The UI */
private VolumePanel mVolumePanel;
+ private Context mUiContext;
+ private Handler mHandler;
// sendMsg() flags
/** If the msg is already queued, replace it with this one. */
@@ -166,6 +171,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
/** @see VolumeStreamState */
private VolumeStreamState[] mStreamStates;
private SettingsObserver mSettingsObserver;
+ //nodelay in a2dp
+ private boolean noDelayInATwoDP = Resources.getSystem().getBoolean(com.android.internal.R.bool.config_noDelayInATwoDP);
private int mMode;
// protects mRingerMode
@@ -228,7 +235,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* NOTE: do not create loops in aliases!
* Some streams alias to different streams according to device category (phone or tablet) or
* use case (in call s off call...).See updateStreamVolumeAlias() for more details
- * mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
+ * STREAM_VOLUME_ALIAS contains the default aliases for a voice capable device (phone) and
* STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
private final int[] STREAM_VOLUME_ALIAS = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
@@ -270,6 +277,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
"STREAM_TTS"
};
+ private boolean mLinkNotificationWithVolume;
+
+ // Cap used for safe headset volume restore. The value directly applies
+ // to AudioSystem.STREAM_MUSIC volume and is rescaled for other streams.
+ private static final int HEADSET_VOLUME_RESTORE_CAP = 10;
+
private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
public void onError(int error) {
switch (error) {
@@ -402,7 +415,7 @@ public void onError(int error) {
private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
- // Request to override default use of A2DP for media.
+ // Request to override default use of A2DP for media
private boolean mBluetoothA2dpEnabled;
private final Object mBluetoothA2dpEnabledLock = new Object();
@@ -424,6 +437,7 @@ public void onError(int error) {
public AudioService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+ mHandler = new Handler();
mVoiceCapable = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_voice_capable);
@@ -441,7 +455,6 @@ public AudioService(Context context) {
SOUND_EFFECT_VOLUME_DB = context.getResources().getInteger(
com.android.internal.R.integer.config_soundEffectVolumeDb);
- mVolumePanel = new VolumePanel(context, this);
mMode = AudioSystem.MODE_NORMAL;
mForcedUseForComm = AudioSystem.FORCE_NONE;
createAudioSystemThread();
@@ -462,6 +475,8 @@ public AudioService(Context context) {
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+ if (noDelayInATwoDP)
+ intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
@@ -469,6 +484,7 @@ public AudioService(Context context) {
intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
// Register a configuration change listener only if requested by system properties
// to monitor orientation changes (off by default)
@@ -487,6 +503,13 @@ public AudioService(Context context) {
pkgFilter.addDataScheme("package");
context.registerReceiver(mReceiver, pkgFilter);
+ ThemeUtils.registerThemeChangeReceiver(context, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mUiContext = null;
+ }
+ });
+
// Register for phone state monitoring
TelephonyManager tmgr = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -579,6 +602,13 @@ private void updateStreamVolumeAlias(boolean updateVolumes) {
dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
}
mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
+
+ if (mLinkNotificationWithVolume) {
+ mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
+ } else {
+ mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
+ }
+
if (updateVolumes) {
mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
false /*lastAudible*/);
@@ -639,6 +669,9 @@ private void readPersistedSettings() {
Settings.System.putInt(cr,
Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
+ mLinkNotificationWithVolume = Settings.System.getBoolean(cr,
+ Settings.System.VOLUME_LINK_NOTIFICATION, true);
+
mMuteAffectedStreams = System.getInt(cr,
System.MUTE_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
@@ -935,8 +968,7 @@ private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags
streamType = AudioSystem.STREAM_NOTIFICATION;
}
- mVolumePanel.postVolumeChanged(streamType, flags);
-
+ showVolumeChangeUi(streamType, flags);
oldIndex = (oldIndex + 5) / 10;
index = (index + 5) / 10;
Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
@@ -1701,6 +1733,9 @@ public boolean isBluetoothScoOn() {
/** @see AudioManager#setBluetoothA2dpOn() */
public void setBluetoothA2dpOn(boolean on) {
+ if (!checkAudioSettingsPermission("setBluetoothA2dpOn()") && noDelayInATwoDP) {
+ return;
+ }
setBluetoothA2dpOnInt(on);
}
@@ -2001,17 +2036,21 @@ public void onServiceConnected(int profile, BluetoothProfile proxy) {
deviceList = a2dp.getConnectedDevices();
if (deviceList.size() > 0) {
btDevice = deviceList.get(0);
- synchronized (mConnectedDevices) {
- int state = a2dp.getConnectionState(btDevice);
- int delay = checkSendBecomingNoisyIntent(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
- queueMsgUnderWakeLock(mAudioHandler,
- MSG_SET_A2DP_CONNECTION_STATE,
- state,
- 0,
- btDevice,
- delay);
+ if (!noDelayInATwoDP){
+ synchronized (mConnectedDevices) {
+ int state = a2dp.getConnectionState(btDevice);
+ int delay = checkSendBecomingNoisyIntent(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
+ queueMsgUnderWakeLock(mAudioHandler,
+ MSG_SET_A2DP_CONNECTION_STATE,
+ state,
+ 0,
+ btDevice,
+ delay);
+ }
+ } else {
+ onSetA2dpConnectionState(btDevice, a2dp.getConnectionState(btDevice));
}
}
break;
@@ -2330,12 +2369,12 @@ private int getDeviceForStream(int stream) {
int device = AudioSystem.getDevicesForStream(stream);
if ((device & (device - 1)) != 0) {
// Multiple device selection is either:
- // - speaker + one other device: give priority to speaker in this case.
+ // - speaker + one other device: give priority to the non-speaker device in this case.
// - one A2DP device + another device: happens with duplicated output. In this case
// retain the device on the A2DP output as the other must not correspond to an active
// selection if not the speaker.
if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
- device = AudioSystem.DEVICE_OUT_SPEAKER;
+ device ^= AudioSystem.DEVICE_OUT_SPEAKER;
} else {
device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
}
@@ -2358,15 +2397,19 @@ public void setWiredDeviceConnectionState(int device, int state, String name) {
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
{
int delay;
- synchronized (mConnectedDevices) {
- delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
- queueMsgUnderWakeLock(mAudioHandler,
- MSG_SET_A2DP_CONNECTION_STATE,
- state,
- 0,
- device,
- delay);
+ if(!noDelayInATwoDP) {
+ synchronized (mConnectedDevices) {
+ delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
+ queueMsgUnderWakeLock(mAudioHandler,
+ MSG_SET_A2DP_CONNECTION_STATE,
+ state,
+ 0,
+ device,
+ delay);
+ }
+ } else {
+ delay = 0;
}
return delay;
}
@@ -2425,10 +2468,8 @@ public synchronized void readSettings() {
// retrieve current volume for device
String name = getSettingNameForDevice(false /* lastAudible */, device);
- // if no volume stored for current stream and device, use default volume if default
- // device, continue otherwise
- int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
- AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
+ // if no volume stored for current stream and device, use default volume
+ int defaultIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
if (index == -1) {
continue;
@@ -3121,6 +3162,8 @@ private class SettingsObserver extends ContentObserver {
super(new Handler());
mContentResolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
+ mContentResolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.VOLUME_LINK_NOTIFICATION), false, this);
}
@Override
@@ -3148,6 +3191,14 @@ public void onChange(boolean selfChange) {
mRingerModeAffectedStreams = ringerModeAffectedStreams;
setRingerModeInt(getRingerMode(), false);
}
+
+ mLinkNotificationWithVolume = Settings.System.getBoolean(mContentResolver,
+ Settings.System.VOLUME_LINK_NOTIFICATION, true);
+ if (mLinkNotificationWithVolume) {
+ mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
+ } else {
+ mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
+ }
}
}
}
@@ -3172,6 +3223,8 @@ private void sendBecomingNoisyIntent() {
// must be called synchronized on mConnectedDevices
private void makeA2dpDeviceUnavailableNow(String address) {
+ if (noDelayInATwoDP)
+ sendBecomingNoisyIntent();
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
address);
@@ -3410,6 +3463,12 @@ public void onReceive(Context context, Intent intent) {
config = AudioSystem.FORCE_NONE;
}
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
+ } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED) && noDelayInATwoDP) {
+ state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+ BluetoothProfile.STATE_DISCONNECTED);
+ BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ onSetA2dpConnectionState(btDevice, state);
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
@@ -3450,6 +3509,54 @@ public void onReceive(Context context, Intent intent) {
}
}
}
+ } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
+ state = intent.getIntExtra("state", 0);
+ if (state == 1) {
+ // Headset plugged in
+ // Avoid connection glitches
+ if (noDelayInATwoDP) {
+ setBluetoothA2dpOnInt(false);
+ }
+
+ // Volume restore capping
+ final boolean capVolumeRestore = Settings.System.getInt(mContentResolver,
+ Settings.System.SAFE_HEADSET_VOLUME_RESTORE, 1) == 1;
+
+ for (int stream = 0; stream < AudioSystem.getNumStreamTypes(); stream++) {
+ if (stream == mStreamVolumeAlias[stream]) {
+ VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[stream]];
+ device = getDeviceForStream(stream);
+ // apply stored value for device
+ streamState.applyDeviceVolume(device);
+
+ // now reduce volume if required
+ if (capVolumeRestore) {
+ final int volume = getStreamVolume(stream);
+ final int restoreCap = rescaleIndex(HEADSET_VOLUME_RESTORE_CAP,
+ AudioSystem.STREAM_MUSIC, stream);
+ if (volume > restoreCap) {
+ setStreamVolume(stream, restoreCap, 0);
+ }
+ }
+ }
+ }
+ } else {
+ // Headset disconnected
+ // Avoid disconnection glitches
+ if (noDelayInATwoDP) {
+ setBluetoothA2dpOnInt(true);
+ }
+
+ // Restore volumes
+ for (int stream = 0; stream < AudioSystem.getNumStreamTypes(); stream++) {
+ if (stream == mStreamVolumeAlias[stream]) {
+ VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[stream]];
+ device = getDeviceForStream(stream);
+ // apply stored value for device
+ streamState.applyDeviceVolume(device);
+ }
+ }
+ }
} else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
state = intent.getIntExtra("state", 0);
@@ -3550,6 +3657,25 @@ public void onReceive(Context context, Intent intent) {
}
}
+ private void showVolumeChangeUi(final int streamType, final int flags) {
+ if (mUiContext != null && mVolumePanel != null) {
+ mVolumePanel.postVolumeChanged(streamType, flags);
+ } else {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mUiContext == null) {
+ mUiContext = ThemeUtils.createUiContext(mContext);
+ }
+
+ final Context context = mUiContext != null ? mUiContext : mContext;
+ mVolumePanel = new VolumePanel(context, AudioService.this);
+ mVolumePanel.postVolumeChanged(streamType, flags);
+ }
+ });
+ }
+ }
+
//==========================================================================================
// AudioFocus
//==========================================================================================
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 96f01dbfb4c..a4686db9cae 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +24,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemProperties;
import android.media.AudioManager;
import android.util.Log;
@@ -423,6 +425,15 @@ private void audioParamCheck(int streamType, int sampleRateInHz,
case AudioFormat.ENCODING_PCM_8BIT:
mAudioFormat = audioFormat;
break;
+ case AudioFormat.ENCODING_AMRNB:
+ case AudioFormat.ENCODING_AMRWB:
+ case AudioFormat.ENCODING_EVRC:
+ case AudioFormat.ENCODING_EVRCB:
+ case AudioFormat.ENCODING_EVRCWB:
+ if (SystemProperties.QCOM_HARDWARE) {
+ mAudioFormat = audioFormat;
+ break;
+ }
default:
mAudioFormat = AudioFormat.ENCODING_INVALID;
throw(new IllegalArgumentException("Unsupported sample encoding."
@@ -679,7 +690,12 @@ static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int au
}
if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
- && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
+ && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)
+ && (audioFormat != AudioFormat.ENCODING_AMRNB)
+ && (audioFormat != AudioFormat.ENCODING_AMRWB)
+ && (audioFormat != AudioFormat.ENCODING_EVRC)
+ && (audioFormat != AudioFormat.ENCODING_EVRCB)
+ && (audioFormat != AudioFormat.ENCODING_EVRCWB)) {
loge("getMinBufferSize(): Invalid audio format.");
return AudioTrack.ERROR_BAD_VALUE;
}
diff --git a/media/java/android/media/IAudioFocusDispatcher.aidl b/media/java/android/media/IAudioFocusDispatcher.aidl
old mode 100755
new mode 100644
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
index 7a520feb6af..7e2afec2567 100644
--- a/media/java/android/media/MediaActionSound.java
+++ b/media/java/android/media/MediaActionSound.java
@@ -18,6 +18,7 @@
import android.media.AudioManager;
import android.media.SoundPool;
+import android.os.SystemProperties;
import android.util.Log;
/**
@@ -87,6 +88,8 @@ public class MediaActionSound {
public static final int STOP_VIDEO_RECORDING = 3;
private static final int SOUND_NOT_LOADED = -1;
+
+ private static final String PROP_CAMERA_SOUND = "persist.sys.camera-sound";
/**
* Construct a new MediaActionSound instance. Only a single instance is
@@ -156,15 +159,17 @@ public synchronized void load(int soundName) {
* @see #STOP_VIDEO_RECORDING
*/
public synchronized void play(int soundName) {
- if (soundName < 0 || soundName >= SOUND_FILES.length) {
- throw new RuntimeException("Unknown sound requested: " + soundName);
- }
- if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
- mSoundIdToPlay =
- mSoundPool.load(SOUND_FILES[soundName], 1);
- mSoundIds[soundName] = mSoundIdToPlay;
- } else {
- mSoundPool.play(mSoundIds[soundName], 1.0f, 1.0f, 0, 0, 1.0f);
+ if (SystemProperties.getBoolean(PROP_CAMERA_SOUND, true)) {
+ if (soundName < 0 || soundName >= SOUND_FILES.length) {
+ throw new RuntimeException("Unknown sound requested: " + soundName);
+ }
+ if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
+ mSoundIdToPlay =
+ mSoundPool.load(SOUND_FILES[soundName], 1);
+ mSoundIds[soundName] = mSoundIdToPlay;
+ } else {
+ mSoundPool.play(mSoundIds[soundName], 1.0f, 1.0f, 0, 0, 1.0f);
+ }
}
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 560c549df67..99db0663b03 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -33,7 +33,6 @@
* codec.start();
* ByteBuffer[] inputBuffers = codec.getInputBuffers();
* ByteBuffer[] outputBuffers = codec.getOutputBuffers();
- * MediaFormat format = codec.getOutputFormat();
* for (;;) {
* int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
* if (inputBufferIndex >= 0) {
@@ -51,7 +50,7 @@
* outputBuffers = codec.getOutputBuffers();
* } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
* // Subsequent data will conform to new format.
- * format = codec.getOutputFormat();
+ * MediaFormat format = codec.getOutputFormat();
* ...
* }
* }
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 9f10cb03744..687d3a52810 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -295,7 +295,7 @@ public MediaFormat getTrackFormat(int index) {
* Returns true iff we are caching data and the cache has reached the
* end of the data stream (for now, a future seek may of course restart
* the fetching of data).
- * This API only returns a meaningful result if {link #getCachedDuration}
+ * This API only returns a meaningful result if {@link #getCachedDuration}
* indicates the presence of a cache, i.e. does NOT return -1.
*/
public native boolean hasCacheReachedEndOfStream();
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index c9bec181427..fb67f7aa94b 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -181,6 +181,7 @@ private static boolean isWMVEnabled() {
}
addFileType("OGG", FILE_TYPE_OGG, "audio/ogg", MtpConstants.FORMAT_OGG);
addFileType("OGG", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
+ addFileType("OGA", FILE_TYPE_OGG, "audio/ogg", MtpConstants.FORMAT_OGG);
addFileType("OGA", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
addFileType("AAC", FILE_TYPE_AAC, "audio/aac", MtpConstants.FORMAT_AAC);
addFileType("AAC", FILE_TYPE_AAC, "audio/aac-adts", MtpConstants.FORMAT_AAC);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 586f11e8343..afeb9d3da42 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -706,7 +706,7 @@ public void setSurface(Surface surface) {
* surface rendering area. When the surface has the same aspect ratio
* as the content, the aspect ratio of the content is maintained;
* otherwise, the aspect ratio of the content is not maintained when video
- * is being rendered. Unlike {@ #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING},
+ * is being rendered. Unlike {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING},
* there is no content cropping with this video scaling mode.
*/
public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
@@ -2024,6 +2024,7 @@ public void handleMessage(Message msg) {
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel)msg.obj;
TimedText text = new TimedText(parcel);
+ parcel.recycle();
mOnTimedTextListener.onTimedText(mMediaPlayer, text);
}
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 9af201dba60..b38d635a953 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -235,6 +236,9 @@ private OutputFormat() {}
/** @hide H.264/AAC data encapsulated in MPEG2/TS */
public static final int OUTPUT_FORMAT_MPEG2TS = 8;
+
+ /** QCP file format */
+ public static final int QCP = 9;
};
/**
@@ -257,6 +261,12 @@ private AudioEncoder() {}
public static final int HE_AAC = 4;
/** Enhanced Low Delay AAC (AAC-ELD) audio codec */
public static final int AAC_ELD = 5;
+ /** EVRC audio codec */
+ public static final int EVRC = 6;
+ /** QCELP audio codec */
+ public static final int QCELP =7;
+ /** Linear PCM audio codec */
+ public static final int LPCM =8;
}
/**
@@ -351,12 +361,11 @@ public void setProfile(CamcorderProfile profile) {
*/
public void setCaptureRate(double fps) {
// Make sure that time lapse is enabled when this method is called.
- setParameter(String.format("time-lapse-enable=1"));
+ setParameter("time-lapse-enable=1");
double timeBetweenFrameCapture = 1 / fps;
int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
- setParameter(String.format("time-between-time-lapse-frame-capture=%d",
- timeBetweenFrameCaptureMs));
+ setParameter("time-between-time-lapse-frame-capture=" + timeBetweenFrameCaptureMs);
}
/**
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 357bf4ebfd5..a25607966ee 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -126,6 +126,15 @@ void updateRoutes(AudioRoutesInfo newRoutes) {
sStatic.mDefaultAudio.mNameResId = name;
dispatchRouteChanged(sStatic.mDefaultAudio);
}
+
+ boolean a2dpEnabled;
+ try {
+ a2dpEnabled = mAudioService.isBluetoothA2dpOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying Bluetooth A2DP state", e);
+ a2dpEnabled = false;
+ }
+
if (!TextUtils.equals(newRoutes.mBluetoothName, mCurRoutesInfo.mBluetoothName)) {
mCurRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
if (mCurRoutesInfo.mBluetoothName != null) {
@@ -135,13 +144,6 @@ void updateRoutes(AudioRoutesInfo newRoutes) {
info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
sStatic.mBluetoothA2dpRoute = info;
addRoute(sStatic.mBluetoothA2dpRoute);
- try {
- if (mAudioService.isBluetoothA2dpOn()) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error selecting Bluetooth A2DP route", e);
- }
} else {
sStatic.mBluetoothA2dpRoute.mName = mCurRoutesInfo.mBluetoothName;
dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
@@ -151,6 +153,16 @@ void updateRoutes(AudioRoutesInfo newRoutes) {
sStatic.mBluetoothA2dpRoute = null;
}
}
+
+ if (mBluetoothA2dpRoute != null) {
+ if (mCurRoutesInfo.mMainType != AudioRoutesInfo.MAIN_SPEAKER &&
+ mSelectedRoute == mBluetoothA2dpRoute) {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudio);
+ } else if (mCurRoutesInfo.mMainType == AudioRoutesInfo.MAIN_SPEAKER &&
+ mSelectedRoute == mDefaultAudio && a2dpEnabled) {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
+ }
+ }
}
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 6f8b809f82f..a37d8b35ddb 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -283,7 +283,7 @@ public class MediaScanner
"Terror",
"Indie",
"Britpop",
- "Negerpunk",
+ null,
"Polsk Punk",
"Beat",
"Christian Gangsta",
@@ -683,7 +683,7 @@ public String getGenreName(String genreTagValue) {
try {
short genreIndex = Short.parseShort(number.toString());
if (genreIndex >= 0) {
- if (genreIndex < ID3_GENRES.length) {
+ if (genreIndex < ID3_GENRES.length && ID3_GENRES[genreIndex] != null) {
return ID3_GENRES[genreIndex];
} else if (genreIndex == 0xFF) {
return null;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 23f7b5566a7..c85ccecafbf 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -109,6 +110,19 @@ public String getTitle(Context context) {
return mTitle = getTitle(context, mUri, true);
}
+ private static String stringForQuery(Cursor cursor) {
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getString(0);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return null;
+ }
+
private static String getTitle(Context context, Uri uri, boolean followSettingsUri) {
Cursor cursor = null;
ContentResolver res = context.getContentResolver();
@@ -127,6 +141,14 @@ private static String getTitle(Context context, Uri uri, boolean followSettingsU
.getString(com.android.internal.R.string.ringtone_default_with_actual,
actualTitle);
}
+ } else if (RingtoneManager.THEME_AUTHORITY.equals(authority)) {
+ Uri themes = Uri.parse("content://com.tmobile.thememanager.themes/themes");
+ title = stringForQuery(res.query(themes, new String[] { "ringtone_name" },
+ "ringtone_uri = ?", new String[] { uri.toString() }, null));
+ if (title == null) {
+ title = stringForQuery(res.query(themes, new String[] { "notif_ringtone_name" },
+ "notif_ringtone_uri = ?", new String[] { uri.toString() }, null));
+ }
} else {
try {
if (DrmStore.AUTHORITY.equals(authority)) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 5e18bfad0c1..3ce2f19889b 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +24,6 @@
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
-import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
@@ -55,29 +55,29 @@ public class RingtoneManager {
// Make sure these are in sync with attrs.xml:
//
@@ -131,7 +131,7 @@ public class RingtoneManager {
*/
public static final String EXTRA_RINGTONE_EXISTING_URI =
"android.intent.extra.ringtone.EXISTING_URI";
-
+
/**
* Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
* ringtone to play when the user attempts to preview the "Default"
@@ -144,7 +144,7 @@ public class RingtoneManager {
*/
public static final String EXTRA_RINGTONE_DEFAULT_URI =
"android.intent.extra.ringtone.DEFAULT_URI";
-
+
/**
* Given to the ringtone picker as an int. Specifies which ringtone type(s) should be
* shown in the picker. One or more of {@link #TYPE_RINGTONE},
@@ -175,26 +175,31 @@ public class RingtoneManager {
public static final String EXTRA_RINGTONE_PICKED_URI =
"android.intent.extra.ringtone.PICKED_URI";
+ /**
+ * @hide
+ */
+ public static final String THEME_AUTHORITY = "com.tmobile.thememanager.packageresources";
+
// Make sure the column ordering and then ..._COLUMN_INDEX are in sync
private static final String[] INTERNAL_COLUMNS = new String[] {
MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
- "\"" + MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "\"",
+ "\"" + MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "/\" || " + MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE_KEY
};
private static final String[] DRM_COLUMNS = new String[] {
DrmStore.Audio._ID, DrmStore.Audio.TITLE,
- "\"" + DrmStore.Audio.CONTENT_URI + "\"",
+ "\"" + DrmStore.Audio.CONTENT_URI + "/\" || " + DrmStore.Audio._ID,
DrmStore.Audio.TITLE + " AS " + MediaStore.Audio.Media.TITLE_KEY
};
private static final String[] MEDIA_COLUMNS = new String[] {
MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
- "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"",
+ "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/\" || " + MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE_KEY
};
-
+
/**
* The column index (in the cursor returned by {@link #getCursor()} for the
* row ID.
@@ -215,11 +220,11 @@ public class RingtoneManager {
private Activity mActivity;
private Context mContext;
-
+
private Cursor mCursor;
private int mType = TYPE_RINGTONE;
-
+
/**
* If a column (item from this list) exists in the Cursor, its value must
* be true (value of 1) for the row to be returned.
@@ -230,7 +235,7 @@ public class RingtoneManager {
private Ringtone mPreviousRingtone;
private boolean mIncludeDrm;
-
+
/**
* Constructs a RingtoneManager. This constructor is recommended as its
* constructed instance manages cursor(s).
@@ -322,7 +327,7 @@ public void stopPreviousRingtone() {
mPreviousRingtone.stop();
}
}
-
+
/**
* Returns whether DRM ringtones will be included.
*
@@ -365,8 +370,12 @@ public Cursor getCursor() {
final Cursor internalCursor = getInternalRingtones();
final Cursor drmCursor = mIncludeDrm ? getDrmRingtones() : null;
final Cursor mediaCursor = getMediaRingtones();
-
- return mCursor = new SortCursor(new Cursor[] { internalCursor, drmCursor, mediaCursor },
+
+ final Cursor themeRegularCursor = getThemeRegularRingtones();
+ final Cursor themeNotifCursor = getThemeNotificationRingtones();
+
+ return mCursor = new SortCursor(new Cursor[] { internalCursor, drmCursor, mediaCursor,
+ themeRegularCursor, themeNotifCursor },
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
}
@@ -401,12 +410,11 @@ public Uri getRingtoneUri(int position) {
return getUriFromCursor(mCursor);
}
-
+
private static Uri getUriFromCursor(Cursor cursor) {
- return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor
- .getLong(ID_COLUMN_INDEX));
+ return Uri.parse(cursor.getString(URI_COLUMN_INDEX));
}
-
+
/**
* Gets the position of a {@link Uri} within this {@link RingtoneManager}.
*
@@ -424,23 +432,12 @@ public int getRingtonePosition(Uri ringtoneUri) {
return -1;
}
- // Only create Uri objects when the actual URI changes
- Uri currentUri = null;
- String previousUriString = null;
for (int i = 0; i < cursorCount; i++) {
- String uriString = cursor.getString(URI_COLUMN_INDEX);
- if (currentUri == null || !uriString.equals(previousUriString)) {
- currentUri = Uri.parse(uriString);
- }
-
- if (ringtoneUri.equals(ContentUris.withAppendedId(currentUri, cursor
- .getLong(ID_COLUMN_INDEX)))) {
+ if (ringtoneUri.equals(getUriFromCursor(cursor))) {
return i;
}
cursor.move(1);
-
- previousUriString = uriString;
}
return -1;
@@ -466,6 +463,14 @@ public static Uri getValidRingtoneUri(Context context) {
uri = getValidRingtoneUriFromCursorAndClose(context, rm.getDrmRingtones());
}
+ if (uri == null) {
+ uri = getValidRingtoneUriFromCursorAndClose(context, rm.getThemeRegularRingtones());
+ }
+
+ if (uri == null) {
+ uri = getValidRingtoneUriFromCursorAndClose(context, rm.getThemeNotificationRingtones());
+ }
+
return uri;
}
@@ -510,7 +515,39 @@ private Cursor getMediaRingtones() {
MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
: null;
}
-
+
+ private String getThemeWhereClause(String uriColumn) {
+ /* Filter out themes with no ringtone and the default theme (which has no package). */
+ String clause = uriColumn + " IS NOT NULL AND LENGTH(theme_package) > 0";
+ if (mIncludeDrm) {
+ return clause;
+ } else {
+ return clause + " AND " + uriColumn + " NOT LIKE '%/assets/%locked%'";
+ }
+ }
+
+ private Cursor getThemeRegularRingtones() {
+ if ((mType & TYPE_RINGTONE) != 0) {
+ return query(Uri.parse("content://com.tmobile.thememanager.themes/themes"),
+ new String[] { "_id", "ringtone_name AS " + MEDIA_COLUMNS[1], "ringtone_uri",
+ "ringtone_name_key AS " + MEDIA_COLUMNS[3] },
+ getThemeWhereClause("ringtone_uri"), null, MEDIA_COLUMNS[3]);
+ } else {
+ return null;
+ }
+ }
+
+ private Cursor getThemeNotificationRingtones() {
+ if ((mType & TYPE_NOTIFICATION) != 0) {
+ return query(Uri.parse("content://com.tmobile.thememanager.themes/themes"),
+ new String[] { "_id", "notif_ringtone_name AS " + MEDIA_COLUMNS[1], "notif_ringtone_uri",
+ "notif_ringtone_name_key AS " + MEDIA_COLUMNS[3] },
+ getThemeWhereClause("notif_ringtone_uri"), null, MEDIA_COLUMNS[3]);
+ } else {
+ return null;
+ }
+ }
+
private void setFilterColumnsList(int type) {
List
@@ -619,7 +656,7 @@ private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int
return null;
}
-
+
/**
* Gets the current default sound's {@link Uri}. This will give the actual
* sound {@link Uri}, instead of using this, most clients can use
@@ -638,7 +675,7 @@ public static Uri getActualDefaultRingtoneUri(Context context, int type) {
final String uriString = Settings.System.getString(context.getContentResolver(), setting);
return uriString != null ? Uri.parse(uriString) : null;
}
-
+
/**
* Sets the {@link Uri} of the default sound for a given sound type.
*
@@ -655,7 +692,7 @@ public static void setActualDefaultRingtoneUri(Context context, int type, Uri ri
Settings.System.putString(context.getContentResolver(), setting,
ringtoneUri != null ? ringtoneUri.toString() : null);
}
-
+
private static String getSettingForType(int type) {
if ((type & TYPE_RINGTONE) != 0) {
return Settings.System.RINGTONE;
@@ -667,7 +704,7 @@ private static String getSettingForType(int type) {
return null;
}
}
-
+
/**
* Returns whether the given {@link Uri} is one of the default ringtones.
*
@@ -677,7 +714,7 @@ private static String getSettingForType(int type) {
public static boolean isDefault(Uri ringtoneUri) {
return getDefaultType(ringtoneUri) != -1;
}
-
+
/**
* Returns the type of a default {@link Uri}.
*
@@ -700,7 +737,7 @@ public static int getDefaultType(Uri defaultRingtoneUri) {
return -1;
}
}
-
+
/**
* Returns the {@link Uri} for the default ringtone of a particular type.
* Rather than returning the actual ringtone's sound {@link Uri}, this will
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 8eb93324747..38e8fa3d3fa 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -161,6 +161,8 @@ public static Bitmap createVideoThumbnail(String filePath, int kind) {
try {
retriever.setDataSource(filePath);
bitmap = retriever.getFrameAtTime(-1);
+ } catch (OutOfMemoryError e) {
+ Log.e(TAG, "Got OOM error", e);
} catch (IllegalArgumentException ex) {
// Assume this is a corrupt video file
} catch (RuntimeException ex) {
diff --git a/media/java/android/media/VibrationPattern.java b/media/java/android/media/VibrationPattern.java
new file mode 100644
index 00000000000..abcc76ab587
--- /dev/null
+++ b/media/java/android/media/VibrationPattern.java
@@ -0,0 +1,141 @@
+
+package android.media;
+
+import java.util.ArrayList;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+
+public class VibrationPattern {
+ public static final String URI = "content://com.aokp.romcontrol.Vibrations/vibrations";
+ public static final String FALLBACK_NAME = "FALLBACK";
+ public static final String FALLBACK_PATTERN = "500,1000,1000,1000,1000";
+
+ private static final String TAG = "VibrationPattern";
+ private Context mContext = null;
+ private String mName;
+ private Uri mUri;
+ private long[] mPattern;
+ private Vibrator mVibrator = null;
+
+ public VibrationPattern(String name, ArrayList
<resources>;
- <declare-styleable name="PieChart">
+ <declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
@@ -276,6 +276,6 @@ public void setShowText(boolean showText) {
You should also read
-
diff --git a/docs/html/training/improving-layouts/smooth-scrolling.jd b/docs/html/training/improving-layouts/smooth-scrolling.jd
index 0afa929ccad..2a1ffba6fa5 100644
--- a/docs/html/training/improving-layouts/smooth-scrolling.jd
+++ b/docs/html/training/improving-layouts/smooth-scrolling.jd
@@ -22,8 +22,8 @@ previous.link=loading-ondemand.html
This lesson teaches you to
-
You should also read
@@ -34,17 +34,17 @@ next.link=redundant_redundant.html
Use Cloud to Device Messaging as an Alternative to Polling
+Use Google Cloud Messaging as an Alternative to Polling
Optimize Polling with Inexact Repeating Alarms and Exponential Backoffs
@@ -99,4 +99,4 @@ executeUpdateOrPrefetch();
}
}
-Implement Horizontal Paging (Swipe Views)
-You should also read
-
You should also read
diff --git a/docs/html/training/notepad/index.jd b/docs/html/training/notepad/index.jd
index 87a57d10edc..64ba14466a3 100644
--- a/docs/html/training/notepad/index.jd
+++ b/docs/html/training/notepad/index.jd
@@ -1,6 +1,5 @@
page.title=Notepad Tutorial
parent.title=Tutorials
-parent.link=../../browser.html?tag=tutorial
@jd:body
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 77a68371a88..b9589912de2 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -673,6 +673,27 @@
+
+
+
diff --git a/drm/java/android/drm/DrmConvertedStatus.java b/drm/java/android/drm/DrmConvertedStatus.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmErrorEvent.java b/drm/java/android/drm/DrmErrorEvent.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmEvent.java b/drm/java/android/drm/DrmEvent.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmInfo.java b/drm/java/android/drm/DrmInfo.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmInfoEvent.java b/drm/java/android/drm/DrmInfoEvent.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmInfoRequest.java b/drm/java/android/drm/DrmInfoRequest.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmInfoStatus.java b/drm/java/android/drm/DrmInfoStatus.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmRights.java b/drm/java/android/drm/DrmRights.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmStore.java b/drm/java/android/drm/DrmStore.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmSupportInfo.java b/drm/java/android/drm/DrmSupportInfo.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/DrmUtils.java b/drm/java/android/drm/DrmUtils.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/ProcessedData.java b/drm/java/android/drm/ProcessedData.java
old mode 100755
new mode 100644
diff --git a/drm/java/android/drm/package.html b/drm/java/android/drm/package.html
old mode 100755
new mode 100644
diff --git a/graphics/java/android/graphics/Bitmap.aidl b/graphics/java/android/graphics/Bitmap.aidl
old mode 100755
new mode 100644
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 7c7cd01f74b..272187bbb4a 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -82,6 +82,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
private final AnimationState mAnimationState;
private int mCurFrame = -1;
private boolean mMutated;
+ private OnAnimationFinishedListener mOnAnimationFinishedListener;
public AnimationDrawable() {
this(null, null);
@@ -211,6 +212,9 @@ private void nextFrame(boolean unschedule) {
int next = mCurFrame+1;
final int N = mAnimationState.getChildCount();
if (next >= N) {
+ if (mOnAnimationFinishedListener != null) {
+ mOnAnimationFinishedListener.onAnimationFinished();
+ }
next = 0;
}
setFrame(next, unschedule, !mAnimationState.mOneShot || next < (N - 1));
@@ -361,5 +365,16 @@ private AnimationDrawable(AnimationState state, Resources res) {
setFrame(0, true, false);
}
}
+
+ // @hide
+ public interface OnAnimationFinishedListener
+ {
+ public void onAnimationFinished();
+ }
+
+ // @hide
+ public void setOnAnimationFinishedListener(OnAnimationFinishedListener l) {
+ mOnAnimationFinishedListener = l;
+ }
}
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index d153c315556..2717de9b83c 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +23,7 @@
#include " + getResources().getString(R.string.page_title) + "
");
+ htmlString.append("" + getResources().getString(R.string.updated) + " " +
+ formatter.format(rightNow.getTime()) + "");
+
+ try {
+ stream = downloadUrl(urlString);
+ entries = stackOverflowXmlParser.parse(stream);
+ // Makes sure that the InputStream is closed after the app is
+ // finished using it.
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+
+ // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
+ // Each Entry object represents a single post in the XML feed.
+ // This section processes the entries list to combine each entry with HTML markup.
+ // Each entry is displayed in the UI as a link that optionally includes
+ // a text summary.
+ for (Entry entry : entries) {
+ htmlString.append("");
+ // If the user set the preference to include summary text,
+ // adds it to the display.
+ if (pref) {
+ htmlString.append(entry.summary);
+ }
+ }
+ return htmlString.toString();
+ }
+
+ // Given a string representation of a URL, sets up a connection and gets
+ // an input stream.
+ private InputStream downloadUrl(String urlString) throws IOException {
+ URL url = new URL(urlString);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(10000 /* milliseconds */);
+ conn.setConnectTimeout(15000 /* milliseconds */);
+ conn.setRequestMethod("GET");
+ conn.setDoInput(true);
+ // Starts the query
+ conn.connect();
+ InputStream stream = conn.getInputStream();
+ return stream;
+ }
+
+ /**
+ *
+ * This BroadcastReceiver intercepts the android.net.ConnectivityManager.CONNECTIVITY_ACTION,
+ * which indicates a connection change. It checks whether the type is TYPE_WIFI.
+ * If it is, it checks whether Wi-Fi is connected and sets the wifiConnected flag in the
+ * main activity accordingly.
+ *
+ */
+ public class NetworkReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ConnectivityManager connMgr =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
+
+ // Checks the user prefs and the network connection. Based on the result, decides
+ // whether
+ // to refresh the display or keep the current display.
+ // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
+ if (WIFI.equals(sPref) && networkInfo != null
+ && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ // If device has its Wi-Fi connection, sets refreshDisplay
+ // to true. This causes the display to be refreshed when the user
+ // returns to the app.
+ refreshDisplay = true;
+ Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
+
+ // If the setting is ANY network and there is a network connection
+ // (which by process of elimination would be mobile), sets refreshDisplay to true.
+ } else if (ANY.equals(sPref) && networkInfo != null) {
+ refreshDisplay = true;
+
+ // Otherwise, the app can't download content--either because there is no network
+ // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
+ // is no Wi-Fi connection.
+ // Sets refreshDisplay to false.
+ } else {
+ refreshDisplay = false;
+ Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+}
diff --git a/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java b/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java
new file mode 100644
index 00000000000..73b72d26f7d
--- /dev/null
+++ b/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.example.android.networkusage;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import com.example.android.networkusage.R;
+
+/**
+ * This preference activity has in its manifest declaration an intent filter for
+ * the ACTION_MANAGE_NETWORK_USAGE action. This activity provides a settings UI
+ * for users to specify network settings to control data usage.
+ */
+public class SettingsActivity extends PreferenceActivity
+ implements
+ OnSharedPreferenceChangeListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Loads the XML preferences file.
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Registers a callback to be invoked whenever a user changes a preference.
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // Unregisters the listener set in onResume().
+ // It's best practice to unregister listeners when your app isn't using them to cut down on
+ // unnecessary system overhead. You do this in onPause().
+ getPreferenceScreen()
+ .getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ // Fires when the user changes a preference.
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ // Sets refreshDisplay to true so that when the user returns to the main
+ // activity, the display refreshes to reflect the new settings.
+ NetworkActivity.refreshDisplay = true;
+ }
+}
diff --git a/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java b/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java
new file mode 100644
index 00000000000..6a010986f97
--- /dev/null
+++ b/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.example.android.networkusage;
+
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class parses XML feeds from stackoverflow.com.
+ * Given an InputStream representation of a feed, it returns a List of entries,
+ * where each list element represents a single entry (post) in the XML feed.
+ */
+public class StackOverflowXmlParser {
+ private static final String ns = null;
+
+ // We don't use namespaces
+
+ public List