summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java25
-rw-r--r--core/java/android/accounts/AccountManager.java161
-rw-r--r--core/java/android/accounts/IAccountManager.aidl9
-rw-r--r--core/java/android/animation/TimeAnimator.java25
-rw-r--r--core/java/android/animation/ValueAnimator.java42
-rw-r--r--core/java/android/app/Activity.java10
-rw-r--r--core/java/android/app/ActivityManagerInternal.java4
-rw-r--r--core/java/android/app/ActivityManagerNative.java38
-rw-r--r--core/java/android/app/ActivityThread.java9
-rw-r--r--core/java/android/app/ContextImpl.java37
-rw-r--r--core/java/android/app/IActivityManager.java7
-rw-r--r--core/java/android/app/KeyguardManager.java26
-rw-r--r--core/java/android/app/LoadedApk.java91
-rw-r--r--core/java/android/app/Notification.java14
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java153
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl7
-rw-r--r--core/java/android/app/backup/RecentsBackupHelper.java114
-rw-r--r--core/java/android/app/trust/ITrustManager.aidl3
-rw-r--r--core/java/android/app/trust/TrustManager.java13
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeAdvertiser.java10
-rw-r--r--core/java/android/content/ContentProvider.java81
-rw-r--r--core/java/android/content/ContentProviderClient.java2
-rw-r--r--core/java/android/content/ContentProviderNative.java6
-rw-r--r--core/java/android/content/Context.java27
-rw-r--r--core/java/android/content/ContextWrapper.java13
-rw-r--r--core/java/android/content/IContentProvider.java3
-rw-r--r--core/java/android/content/Intent.java28
-rw-r--r--core/java/android/content/pm/PackageInfo.java31
-rw-r--r--core/java/android/content/pm/PackageInfoLite.java16
-rw-r--r--core/java/android/content/pm/PackageManager.java20
-rw-r--r--core/java/android/content/pm/PackageParser.java50
-rw-r--r--core/java/android/content/pm/ParceledListSlice.java47
-rw-r--r--core/java/android/content/res/AssetManager.java8
-rw-r--r--core/java/android/content/res/Resources.java2
-rw-r--r--core/java/android/content/res/StringBlock.java10
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java16
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java18
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java335
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java68
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java14
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java14
-rw-r--r--core/java/android/hardware/camera2/legacy/ParameterUtils.java4
-rw-r--r--core/java/android/hardware/hdmi/HdmiPortInfo.java13
-rw-r--r--core/java/android/inputmethodservice/KeyboardView.java6
-rw-r--r--core/java/android/net/ConnectivityManager.java50
-rw-r--r--core/java/android/net/EthernetManager.java97
-rw-r--r--core/java/android/net/IConnectivityManager.aidl8
-rw-r--r--core/java/android/net/IEthernetManager.aidl4
-rw-r--r--core/java/android/net/IEthernetServiceListener.aidl (renamed from core/java/android/webkit/MustOverrideException.java)16
-rw-r--r--core/java/android/net/NetworkIdentity.java24
-rw-r--r--core/java/android/net/NetworkState.java11
-rw-r--r--core/java/android/net/NetworkStats.java16
-rw-r--r--core/java/android/net/NetworkStatsHistory.java48
-rw-r--r--core/java/android/net/NetworkTemplate.java28
-rw-r--r--core/java/android/net/VpnService.java82
-rw-r--r--core/java/android/net/WebAddress.java15
-rw-r--r--core/java/android/net/http/CharArrayBuffers.java89
-rw-r--r--core/java/android/net/http/Headers.java59
-rw-r--r--core/java/android/net/http/X509TrustManagerExtensions.java3
-rw-r--r--core/java/android/nfc/BeamShareData.java9
-rw-r--r--core/java/android/nfc/INfcAdapter.aidl2
-rw-r--r--core/java/android/nfc/NfcActivityManager.java41
-rw-r--r--core/java/android/os/AsyncTask.java24
-rw-r--r--core/java/android/os/BatteryStats.java71
-rw-r--r--core/java/android/os/Build.java29
-rw-r--r--core/java/android/os/Handler.java6
-rw-r--r--core/java/android/os/Message.java40
-rw-r--r--core/java/android/os/PowerManagerInternal.java52
-rw-r--r--core/java/android/os/UserHandle.java4
-rw-r--r--core/java/android/os/UserManager.java4
-rw-r--r--core/java/android/provider/CallLog.java1
-rw-r--r--core/java/android/provider/DocumentsProvider.java6
-rw-r--r--core/java/android/provider/Settings.java15
-rw-r--r--core/java/android/service/carriermessaging/CarrierMessagingService.aidl19
-rw-r--r--core/java/android/service/carriermessaging/CarrierMessagingService.java393
-rw-r--r--core/java/android/service/carriermessaging/CarrierMessagingServiceManager.java97
-rw-r--r--core/java/android/service/carriermessaging/ICarrierMessagingCallback.aidl33
-rw-r--r--core/java/android/service/carriermessaging/ICarrierMessagingService.aidl103
-rw-r--r--core/java/android/service/carriermessaging/MessagePdu.aidl19
-rw-r--r--core/java/android/service/carriermessaging/MessagePdu.java97
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java9
-rw-r--r--core/java/android/service/trust/ITrustAgentService.aidl2
-rw-r--r--core/java/android/service/trust/TrustAgentService.java32
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java70
-rw-r--r--core/java/android/text/StaticLayout.java4
-rwxr-xr-xcore/java/android/text/format/DateFormat.java103
-rw-r--r--core/java/android/transition/ChangeTransform.java9
-rw-r--r--core/java/android/transition/SidePropagation.java22
-rw-r--r--core/java/android/transition/Slide.java40
-rw-r--r--core/java/android/transition/Visibility.java1
-rw-r--r--core/java/android/util/PathParser.java25
-rw-r--r--core/java/android/view/GLES20Canvas.java5
-rw-r--r--core/java/android/view/HardwareCanvas.java2
-rw-r--r--core/java/android/view/InputEvent.java7
-rw-r--r--core/java/android/view/KeyEvent.java10
-rw-r--r--core/java/android/view/MotionEvent.java6
-rw-r--r--core/java/android/view/RenderNodeAnimator.java21
-rw-r--r--core/java/android/view/Surface.java11
-rw-r--r--core/java/android/view/ThreadedRenderer.java3
-rw-r--r--core/java/android/view/View.java330
-rw-r--r--core/java/android/view/ViewAnimationUtils.java7
-rw-r--r--core/java/android/view/ViewGroup.java17
-rw-r--r--core/java/android/view/ViewRootImpl.java21
-rw-r--r--core/java/android/view/ViewTreeObserver.java49
-rw-r--r--core/java/android/view/Window.java26
-rw-r--r--core/java/android/view/WindowManagerGlobal.java29
-rw-r--r--core/java/android/view/WindowManagerImpl.java36
-rw-r--r--core/java/android/webkit/CookieManager.java101
-rw-r--r--core/java/android/webkit/DebugFlags.java37
-rw-r--r--core/java/android/webkit/FindActionModeCallback.java2
-rw-r--r--core/java/android/webkit/GeolocationPermissions.java3
-rw-r--r--core/java/android/webkit/HttpAuthHandler.java2
-rw-r--r--core/java/android/webkit/JsDialogHelper.java4
-rw-r--r--core/java/android/webkit/JsPromptResult.java4
-rw-r--r--core/java/android/webkit/JsResult.java5
-rw-r--r--core/java/android/webkit/SslErrorHandler.java2
-rw-r--r--core/java/android/webkit/URLUtil.java9
-rw-r--r--core/java/android/webkit/WebBackForwardList.java31
-rw-r--r--core/java/android/webkit/WebChromeClient.java2
-rw-r--r--core/java/android/webkit/WebHistoryItem.java40
-rw-r--r--core/java/android/webkit/WebIconDatabase.java39
-rw-r--r--core/java/android/webkit/WebSettings.java400
-rw-r--r--core/java/android/webkit/WebStorage.java15
-rw-r--r--core/java/android/webkit/WebView.java118
-rw-r--r--core/java/android/webkit/WebViewDatabase.java33
-rw-r--r--core/java/android/webkit/WebViewDelegate.java4
-rw-r--r--core/java/android/webkit/WebViewFactory.java14
-rw-r--r--core/java/android/webkit/WebViewFactoryProvider.java2
-rw-r--r--core/java/android/webkit/WebViewProvider.java2
-rw-r--r--core/java/android/widget/AbsSeekBar.java67
-rw-r--r--core/java/android/widget/ActionMenuPresenter.java16
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java6
-rw-r--r--core/java/android/widget/CalendarView.java1616
-rw-r--r--core/java/android/widget/CalendarViewLegacyDelegate.java1527
-rw-r--r--core/java/android/widget/CalendarViewMaterialDelegate.java260
-rw-r--r--core/java/android/widget/DatePicker.java28
-rw-r--r--core/java/android/widget/DatePickerCalendarDelegate.java13
-rw-r--r--core/java/android/widget/DateTimeView.java21
-rw-r--r--core/java/android/widget/DayPickerView.java85
-rw-r--r--core/java/android/widget/FastScroller.java57
-rw-r--r--core/java/android/widget/GridLayout.java81
-rw-r--r--core/java/android/widget/PopupWindow.java6
-rw-r--r--core/java/android/widget/ProgressBar.java157
-rw-r--r--core/java/android/widget/RadialTimePickerView.java5
-rw-r--r--core/java/android/widget/RatingBar.java4
-rw-r--r--core/java/android/widget/SearchView.java213
-rw-r--r--core/java/android/widget/SimpleMonthAdapter.java31
-rw-r--r--core/java/android/widget/TextClock.java34
-rw-r--r--core/java/android/widget/TextView.java25
-rw-r--r--core/java/com/android/internal/app/ProcessStats.java6
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java24
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java8
-rw-r--r--core/java/com/android/internal/net/VpnConfig.java4
-rw-r--r--core/java/com/android/internal/os/HandlerCaller.java4
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java128
-rw-r--r--core/java/com/android/internal/policy/IKeyguardService.aidl51
-rw-r--r--core/java/com/android/internal/policy/IKeyguardServiceConstants.java41
-rw-r--r--core/java/com/android/internal/policy/IKeyguardStateCallback.aidl (renamed from core/java/android/net/http/Timer.java)33
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java144
-rw-r--r--core/java/com/android/internal/widget/SwipeDismissLayout.java39
-rw-r--r--core/java/com/android/server/backup/SystemBackupAgent.java4
162 files changed, 6066 insertions, 3684 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a9eaf29..3f1845a 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,6 +16,7 @@
package android.accessibilityservice;
+import android.annotation.NonNull;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -27,6 +28,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -618,6 +620,23 @@ public abstract class AccessibilityService extends Service {
}
}
+ @Override
+ public Object getSystemService(@ServiceName @NonNull String name) {
+ if (getBaseContext() == null) {
+ throw new IllegalStateException(
+ "System services not available to Activities before onCreate()");
+ }
+
+ // Guarantee that we always return the same window manager instance.
+ if (WINDOW_SERVICE.equals(name)) {
+ if (mWindowManager == null) {
+ mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
+ }
+ return mWindowManager;
+ }
+ return super.getSystemService(name);
+ }
+
/**
* Implement to return the implementation of the internal accessibility
* service interface.
@@ -645,8 +664,10 @@ public abstract class AccessibilityService extends Service {
mConnectionId = connectionId;
mWindowToken = windowToken;
- // Let the window manager know about our shiny new token.
- WindowManagerGlobal.getInstance().setDefaultToken(mWindowToken);
+ // The client may have already obtained the window manager, so
+ // update the default token on whatever manager we gave them.
+ final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
+ wm.setDefaultToken(windowToken);
}
@Override
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 09b484b..6957435 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -17,37 +17,37 @@
package android.accounts;
import android.app.Activity;
-import android.content.Intent;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentFilter;
-import android.content.BroadcastReceiver;
import android.content.res.Resources;
import android.database.SQLException;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.os.RemoteException;
import android.os.Parcelable;
-import android.os.Build;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Log;
import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.google.android.collect.Maps;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.android.internal.R;
-import com.google.android.collect.Maps;
+import java.util.concurrent.TimeoutException;
/**
* This class provides access to a centralized registry of the user's
@@ -747,13 +747,17 @@ public class AccountManager {
* null for the main thread
* @return An {@link AccountManagerFuture} which resolves to a Boolean,
* true if the account has been successfully removed
+ * @deprecated use
+ * {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)}
+ * instead
*/
+ @Deprecated
public AccountManagerFuture<Boolean> removeAccount(final Account account,
AccountManagerCallback<Boolean> callback, Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null");
return new Future2Task<Boolean>(handler, callback) {
public void doWork() throws RemoteException {
- mService.removeAccount(mResponse, account);
+ mService.removeAccount(mResponse, account, false);
}
public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
@@ -765,9 +769,60 @@ public class AccountManager {
}
/**
+ * Removes an account from the AccountManager. Does nothing if the account
+ * does not exist. Does not delete the account from the server.
+ * The authenticator may have its own policies preventing account
+ * deletion, in which case the account will not be deleted.
+ *
+ * <p>This method may be called from any thread, but the returned
+ * {@link AccountManagerFuture} must not be used on the main thread.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+ *
+ * @param account The {@link Account} to remove
+ * @param activity The {@link Activity} context to use for launching a new
+ * authenticator-defined sub-Activity to prompt the user to delete an
+ * account; used only to call startActivity(); if null, the prompt
+ * will not be launched directly, but the {@link Intent} may be
+ * returned to the caller instead
+ * @param callback Callback to invoke when the request completes,
+ * null for no callback
+ * @param handler {@link Handler} identifying the callback thread,
+ * null for the main thread
+ * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+ * {@link #KEY_BOOLEAN_RESULT} if activity was specified and an account
+ * was removed or if active. If no activity was specified, the returned
+ * Bundle contains only {@link #KEY_INTENT} with the {@link Intent}
+ * needed to launch the actual account removal process, if authenticator
+ * needs the activity launch. If an error occurred,
+ * {@link AccountManagerFuture#getResult()} throws:
+ * <ul>
+ * <li> {@link AuthenticatorException} if no authenticator was registered for
+ * this account type or the authenticator failed to respond
+ * <li> {@link OperationCanceledException} if the operation was canceled for
+ * any reason, including the user canceling the creation process or
+ * adding accounts (of this type) has been disabled by policy
+ * </ul>
+ */
+ public AccountManagerFuture<Bundle> removeAccount(final Account account,
+ final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.removeAccount(mResponse, account, activity != null);
+ }
+ }.start();
+ }
+
+ /**
* @see #removeAccount(Account, AccountManagerCallback, Handler)
* @hide
+ * @deprecated use
+ * {@link #removeAccountAsUser(Account, Activity, AccountManagerCallback, Handler)}
+ * instead
*/
+ @Deprecated
public AccountManagerFuture<Boolean> removeAccountAsUser(final Account account,
AccountManagerCallback<Boolean> callback, Handler handler,
final UserHandle userHandle) {
@@ -775,7 +830,7 @@ public class AccountManager {
if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
return new Future2Task<Boolean>(handler, callback) {
public void doWork() throws RemoteException {
- mService.removeAccountAsUser(mResponse, account, userHandle.getIdentifier());
+ mService.removeAccountAsUser(mResponse, account, false, userHandle.getIdentifier());
}
public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
@@ -787,6 +842,52 @@ public class AccountManager {
}
/**
+ * @see #removeAccount(Account, Activity, AccountManagerCallback, Handler)
+ * @hide
+ */
+ public AccountManagerFuture<Bundle> removeAccountAsUser(final Account account,
+ final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler,
+ final UserHandle userHandle) {
+ if (account == null)
+ throw new IllegalArgumentException("account is null");
+ if (userHandle == null)
+ throw new IllegalArgumentException("userHandle is null");
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.removeAccountAsUser(mResponse, account, activity != null,
+ userHandle.getIdentifier());
+ }
+ }.start();
+ }
+
+ /**
+ * Removes an account directly. Normally used by authenticators, not
+ * directly by applications. Does not delete the account from the server.
+ * The authenticator may have its own policies preventing account deletion,
+ * in which case the account will not be deleted.
+ * <p>
+ * It is safe to call this method from the main thread.
+ * <p>
+ * This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and to have the
+ * same UID or signature as the account's authenticator.
+ *
+ * @param account The {@link Account} to delete.
+ * @return True if the account was successfully deleted, false if the
+ * account did not exist, the account is null, or another error
+ * occurs.
+ */
+ public boolean removeAccountExplicitly(Account account) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ try {
+ return mService.removeAccountExplicitly(account);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Removes an auth token from the AccountManager's cache. Does nothing if
* the auth token is not currently in the cache. Applications must call this
* method when the auth token is found to have expired or otherwise become
@@ -1342,6 +1443,40 @@ public class AccountManager {
}
/**
+ * Copies an account from the primary user to another user.
+ * @param account the account to copy
+ * @param user the target user
+ * @param callback Callback to invoke when the request completes,
+ * null for no callback
+ * @param handler {@link Handler} identifying the callback thread,
+ * null for the main thread
+ * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated wether it
+ * succeeded.
+ * @hide
+ */
+ public AccountManagerFuture<Boolean> copyAccountToUser(
+ final Account account, final UserHandle user,
+ AccountManagerCallback<Boolean> callback, Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (user == null) throw new IllegalArgumentException("user is null");
+
+ return new Future2Task<Boolean>(handler, callback) {
+ @Override
+ public void doWork() throws RemoteException {
+ mService.copyAccountToUser(
+ mResponse, account, UserHandle.USER_OWNER, user.getIdentifier());
+ }
+ @Override
+ public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
+ if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
+ throw new AuthenticatorException("no result in response");
+ }
+ return bundle.getBoolean(KEY_BOOLEAN_RESULT);
+ }
+ }.start();
+ }
+
+ /**
* @hide
* Removes the shared account.
* @param account the account to remove
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index a133788..aa41161 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -37,8 +37,13 @@ interface IAccountManager {
void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features);
void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
- void removeAccount(in IAccountManagerResponse response, in Account account);
- void removeAccountAsUser(in IAccountManagerResponse response, in Account account, int userId);
+ void removeAccount(in IAccountManagerResponse response, in Account account,
+ boolean expectActivityLaunch);
+ void removeAccountAsUser(in IAccountManagerResponse response, in Account account,
+ boolean expectActivityLaunch, int userId);
+ boolean removeAccountExplicitly(in Account account);
+ void copyAccountToUser(in IAccountManagerResponse response, in Account account,
+ int userFrom, int userTo);
void invalidateAuthToken(String accountType, String authToken);
String peekAuthToken(in Account account, String authTokenType);
void setAuthToken(in Account account, String authTokenType, String authToken);
diff --git a/core/java/android/animation/TimeAnimator.java b/core/java/android/animation/TimeAnimator.java
index f9aa00e..1738ade 100644
--- a/core/java/android/animation/TimeAnimator.java
+++ b/core/java/android/animation/TimeAnimator.java
@@ -1,5 +1,23 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.animation;
+import android.view.animation.AnimationUtils;
+
/**
* This class provides a simple callback mechanism to listeners that is synchronized with all
* other animators in the system. There is no duration, interpolation, or object value-setting
@@ -29,6 +47,13 @@ public class TimeAnimator extends ValueAnimator {
return false;
}
+ @Override
+ public void setCurrentPlayTime(long playTime) {
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ mStartTime = Math.max(mStartTime, currentTime - playTime);
+ animationFrame(currentTime);
+ }
+
/**
* Sets a listener that is sent update events throughout the life of
* an animation.
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 07f79b8..d65b490 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -79,7 +79,7 @@ public class ValueAnimator extends Animator {
* Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
* to a value.
*/
- long mSeekTime = -1;
+ float mSeekFraction = -1;
/**
* Set on the next frame after pause() is called, used to calculate a new startTime
@@ -537,14 +537,31 @@ public class ValueAnimator extends Animator {
* @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
*/
public void setCurrentPlayTime(long playTime) {
+ float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration :
+ playTime == 0 ? 0 : 1;
+ setCurrentFraction(fraction);
+ }
+
+ /**
+ * Sets the position of the animation to the specified fraction. This fraction should
+ * be between 0 and the total fraction of the animation, including any repetition. That is,
+ * a fraction of 0 will position the animation at the beginning, a value of 1 at the end,
+ * and a value of 2 at the beginning of a reversing animator that repeats once. If
+ * the animation has not yet been started, then it will not advance forward after it is
+ * set to this fraction; it will simply set the fraction to this value and perform any
+ * appropriate actions based on that fraction. If the animation is already running, then
+ * setCurrentFraction() will set the current fraction to this value and continue
+ * playing from that point.
+ *
+ * @param fraction The fraction to which the animation is advanced or rewound.
+ */
+ public void setCurrentFraction(float fraction) {
initAnimation();
- long currentTime = AnimationUtils.currentAnimationTimeMillis();
if (mPlayingState != RUNNING) {
- mSeekTime = playTime;
+ mSeekFraction = fraction;
mPlayingState = SEEKED;
}
- mStartTime = currentTime - playTime;
- doAnimationFrame(currentTime);
+ animateValue(fraction);
}
/**
@@ -948,6 +965,7 @@ public class ValueAnimator extends Animator {
}
mPlayingBackwards = playBackwards;
mCurrentIteration = 0;
+ int prevPlayingState = mPlayingState;
mPlayingState = STOPPED;
mStarted = true;
mStartedDelay = false;
@@ -957,7 +975,9 @@ public class ValueAnimator extends Animator {
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
- setCurrentPlayTime(0);
+ if (prevPlayingState != SEEKED) {
+ setCurrentPlayTime(0);
+ }
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
@@ -1221,12 +1241,12 @@ public class ValueAnimator extends Animator {
final boolean doAnimationFrame(long frameTime) {
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
- if (mSeekTime < 0) {
+ if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
- mStartTime = frameTime - mSeekTime;
- // Now that we're playing, reset the seek time
- mSeekTime = -1;
+ long seekTime = (long) (mDuration * mSeekFraction);
+ mStartTime = frameTime - seekTime;
+ mSeekFraction = -1;
}
}
if (mPaused) {
@@ -1292,7 +1312,7 @@ public class ValueAnimator extends Animator {
if (mUpdateListeners != null) {
anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>(mUpdateListeners);
}
- anim.mSeekTime = -1;
+ anim.mSeekFraction = -1;
anim.mPlayingBackwards = false;
anim.mCurrentIteration = 0;
anim.mInitialized = false;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 148527f..fab88a2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5620,6 +5620,16 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * @hide
+ */
+ public void dispatchEnterAnimationComplete() {
+ onEnterAnimationComplete();
+ if (getWindow() != null && getWindow().getDecorView() != null) {
+ getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete();
+ }
+ }
+
+ /**
* Adjust the current immersive mode setting.
*
* Note that changing this value will have no effect on the activity's
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 2a17fa6..d56dc1e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -23,8 +23,8 @@ package android.app;
*/
public abstract class ActivityManagerInternal {
// Called by the power manager.
- public abstract void goingToSleep();
- public abstract void wakingUp();
+ public abstract void onWakefulnessChanged(int wakefulness);
+
public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
String processName, String abiOverride, int uid, Runnable crashHandler);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c3028b7..6ec48e5 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1186,6 +1186,18 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case CHECK_PERMISSION_WITH_TOKEN_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String perm = data.readString();
+ int pid = data.readInt();
+ int uid = data.readInt();
+ IBinder token = data.readStrongBinder();
+ int res = checkPermissionWithToken(perm, pid, uid, token);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
case CHECK_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
Uri uri = Uri.CREATOR.createFromParcel(data);
@@ -1193,7 +1205,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
int uid = data.readInt();
int mode = data.readInt();
int userId = data.readInt();
- int res = checkUriPermission(uri, pid, uid, mode, userId);
+ IBinder callerToken = data.readStrongBinder();
+ int res = checkUriPermission(uri, pid, uid, mode, userId, callerToken);
reply.writeNoException();
reply.writeInt(res);
return true;
@@ -3742,7 +3755,7 @@ class ActivityManagerProxy implements IActivityManager
mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
reply.readException();
IIntentSender res = IIntentSender.Stub.asInterface(
- reply.readStrongBinder());
+ reply.readStrongBinder());
data.recycle();
reply.recycle();
return res;
@@ -3851,6 +3864,22 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return res;
}
+ public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(permission);
+ data.writeInt(pid);
+ data.writeInt(uid);
+ data.writeStrongBinder(callerToken);
+ mRemote.transact(CHECK_PERMISSION_WITH_TOKEN_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, final int userId) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -3866,8 +3895,8 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return res;
}
- public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
- throws RemoteException {
+ public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId,
+ IBinder callerToken) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3876,6 +3905,7 @@ class ActivityManagerProxy implements IActivityManager
data.writeInt(uid);
data.writeInt(mode);
data.writeInt(userId);
+ data.writeStrongBinder(callerToken);
mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a268b1c..b64e724 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -84,6 +84,8 @@ import android.util.Slog;
import android.util.SuperNotCalledException;
import android.view.Display;
import android.view.HardwareRenderer;
+import android.view.IWindowManager;
+import android.view.IWindowSessionCallback;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewManager;
@@ -2366,6 +2368,9 @@ public final class ActivityThread {
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
+ // Initialize before creating the activity
+ WindowManagerGlobal.initialize();
+
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
@@ -2531,7 +2536,7 @@ public final class ActivityThread {
private void handleEnterAnimationComplete(IBinder token) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
- r.activity.onEnterAnimationComplete();
+ r.activity.dispatchEnterAnimationComplete();
}
}
@@ -5223,8 +5228,6 @@ public final class ActivityThread {
sMainThreadHandler = thread.getHandler();
}
- AsyncTask.init();
-
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 78cc810..12fee2d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -20,9 +20,9 @@ import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManager;
import android.appwidget.AppWidgetManager;
import android.os.Build;
-
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
+
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.policy.PolicyManager;
import com.android.internal.util.Preconditions;
@@ -127,6 +127,7 @@ import android.print.PrintManager;
import android.service.fingerprint.IFingerprintService;
import android.service.fingerprint.FingerprintManager;
import android.telecom.TelecomManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
import android.util.AndroidRuntimeException;
@@ -563,6 +564,11 @@ class ContextImpl extends Context {
return new TelephonyManager(ctx.getOuterContext());
}});
+ registerService(TELEPHONY_SUBSCRIPTION_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ return new SubscriptionManager(ctx.getOuterContext());
+ }});
+
registerService(TELECOM_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new TelecomManager(ctx.getOuterContext());
@@ -1872,6 +1878,21 @@ class ContextImpl extends Context {
}
}
+ /** @hide */
+ @Override
+ public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ try {
+ return ActivityManagerNative.getDefault().checkPermissionWithToken(
+ permission, pid, uid, callerToken);
+ } catch (RemoteException e) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+
@Override
public int checkCallingPermission(String permission) {
if (permission == null) {
@@ -1960,7 +1981,19 @@ class ContextImpl extends Context {
try {
return ActivityManagerNative.getDefault().checkUriPermission(
ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
- resolveUserId(uri));
+ resolveUserId(uri), null);
+ } catch (RemoteException e) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+
+ /** @hide */
+ @Override
+ public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+ try {
+ return ActivityManagerNative.getDefault().checkUriPermission(
+ ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
+ resolveUserId(uri), callerToken);
} catch (RemoteException e) {
return PackageManager.PERMISSION_DENIED;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6433f3f..5362303 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -219,9 +219,11 @@ public interface IActivityManager extends IInterface {
public int checkPermission(String permission, int pid, int uid)
throws RemoteException;
-
- public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
+ public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken)
throws RemoteException;
+
+ public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId,
+ IBinder callerToken) throws RemoteException;
public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
int mode, int userId) throws RemoteException;
public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode, int userId)
@@ -785,4 +787,5 @@ public interface IActivityManager extends IInterface {
int GET_TASK_DESCRIPTION_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+238;
int LAUNCH_ASSIST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+239;
int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240;
+ int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241;
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 5038df9..ddd21e6 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -225,28 +225,28 @@ public class KeyguardManager {
}
/**
- * Return whether unlocking the device is currently not requiring a password
- * because of a trust agent.
+ * Returns whether the device is currently locked and requires a PIN, pattern or
+ * password to unlock.
*
- * @return true if the keyguard can currently be unlocked without entering credentials
- * because the device is in a trusted environment.
+ * @return true if unlocking the device currently requires a PIN, pattern or
+ * password.
*/
- public boolean isKeyguardInTrustedState() {
- return isKeyguardInTrustedState(UserHandle.getCallingUserId());
+ public boolean isDeviceLocked() {
+ return isDeviceLocked(UserHandle.getCallingUserId());
}
/**
- * Return whether unlocking the device is currently not requiring a password
- * because of a trust agent.
+ * Returns whether the device is currently locked and requires a PIN, pattern or
+ * password to unlock.
*
- * @param userId the user for which the trusted state should be reported.
- * @return true if the keyguard can currently be unlocked without entering credentials
- * because the device is in a trusted environment.
+ * @param userId the user for which the locked state should be reported.
+ * @return true if unlocking the device currently requires a PIN, pattern or
+ * password.
* @hide
*/
- public boolean isKeyguardInTrustedState(int userId) {
+ public boolean isDeviceLocked(int userId) {
try {
- return mTrustManager.isTrusted(userId);
+ return mTrustManager.isDeviceLocked(userId);
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index aa98e97..ece2a33 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -632,55 +632,60 @@ public final class LoadedApk {
public void removeContextRegistrations(Context context,
String who, String what) {
final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
- ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
- mReceivers.remove(context);
- if (rmap != null) {
- for (int i=0; i<rmap.size(); i++) {
- LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
- IntentReceiverLeaked leak = new IntentReceiverLeaked(
- what + " " + who + " has leaked IntentReceiver "
- + rd.getIntentReceiver() + " that was " +
- "originally registered here. Are you missing a " +
- "call to unregisterReceiver()?");
- leak.setStackTrace(rd.getLocation().getStackTrace());
- Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
- if (reportRegistrationLeaks) {
- StrictMode.onIntentReceiverLeaked(leak);
- }
- try {
- ActivityManagerNative.getDefault().unregisterReceiver(
- rd.getIIntentReceiver());
- } catch (RemoteException e) {
- // system crashed, nothing we can do
+ synchronized (mReceivers) {
+ ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
+ mReceivers.remove(context);
+ if (rmap != null) {
+ for (int i = 0; i < rmap.size(); i++) {
+ LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
+ IntentReceiverLeaked leak = new IntentReceiverLeaked(
+ what + " " + who + " has leaked IntentReceiver "
+ + rd.getIntentReceiver() + " that was " +
+ "originally registered here. Are you missing a " +
+ "call to unregisterReceiver()?");
+ leak.setStackTrace(rd.getLocation().getStackTrace());
+ Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
+ if (reportRegistrationLeaks) {
+ StrictMode.onIntentReceiverLeaked(leak);
+ }
+ try {
+ ActivityManagerNative.getDefault().unregisterReceiver(
+ rd.getIIntentReceiver());
+ } catch (RemoteException e) {
+ // system crashed, nothing we can do
+ }
}
}
+ mUnregisteredReceivers.remove(context);
}
- mUnregisteredReceivers.remove(context);
- //Slog.i(TAG, "Receiver registrations: " + mReceivers);
- ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
- mServices.remove(context);
- if (smap != null) {
- for (int i=0; i<smap.size(); i++) {
- LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
- ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
- what + " " + who + " has leaked ServiceConnection "
- + sd.getServiceConnection() + " that was originally bound here");
- leak.setStackTrace(sd.getLocation().getStackTrace());
- Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
- if (reportRegistrationLeaks) {
- StrictMode.onServiceConnectionLeaked(leak);
- }
- try {
- ActivityManagerNative.getDefault().unbindService(
- sd.getIServiceConnection());
- } catch (RemoteException e) {
- // system crashed, nothing we can do
+
+ synchronized (mServices) {
+ //Slog.i(TAG, "Receiver registrations: " + mReceivers);
+ ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
+ mServices.remove(context);
+ if (smap != null) {
+ for (int i = 0; i < smap.size(); i++) {
+ LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
+ ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
+ what + " " + who + " has leaked ServiceConnection "
+ + sd.getServiceConnection() + " that was originally bound here");
+ leak.setStackTrace(sd.getLocation().getStackTrace());
+ Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
+ if (reportRegistrationLeaks) {
+ StrictMode.onServiceConnectionLeaked(leak);
+ }
+ try {
+ ActivityManagerNative.getDefault().unbindService(
+ sd.getIServiceConnection());
+ } catch (RemoteException e) {
+ // system crashed, nothing we can do
+ }
+ sd.doForget();
}
- sd.doForget();
}
+ mUnboundServices.remove(context);
+ //Slog.i(TAG, "Service registrations: " + mServices);
}
- mUnboundServices.remove(context);
- //Slog.i(TAG, "Service registrations: " + mServices);
}
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index dfe5cf5..9e8a793 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4115,9 +4115,9 @@ public class Notification implements Parcelable
* <pre class="prettyprint">
* Notification noti = new Notification.Builder()
* .setSmallIcon(R.drawable.ic_stat_player)
- * .setContentTitle(&quot;Track title&quot;) // these three lines are optional
- * .setContentText(&quot;Artist - Album&quot;) // if you use
- * .setLargeIcon(albumArtBitmap)) // setMediaSession(token)
+ * .setContentTitle(&quot;Track title&quot;)
+ * .setContentText(&quot;Artist - Album&quot;)
+ * .setLargeIcon(albumArtBitmap))
* .setStyle(<b>new Notification.MediaStyle()</b>
* .setMediaSession(mySession))
* .build();
@@ -4986,7 +4986,9 @@ public class Notification implements Parcelable
}
/**
- * Set a hint that this notification's background should not be clipped if possible.
+ * Set a hint that this notification's background should not be clipped if possible,
+ * and should instead be resized to fully display on the screen, retaining the aspect
+ * ratio of the image. This can be useful for images like barcodes or qr codes.
* @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
* @return this object for method chaining
*/
@@ -4997,7 +4999,9 @@ public class Notification implements Parcelable
}
/**
- * Get a hint that this notification's background should not be clipped if possible.
+ * Get a hint that this notification's background should not be clipped if possible,
+ * and should instead be resized to fully display on the screen, retaining the aspect
+ * ratio of the image. This can be useful for images like barcodes or qr codes.
* @return {@code true} if it's ok if the background is clipped on the screen, false
* otherwise. The default value is {@code false} if this was never set.
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 33cac75..50d6b11 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -52,11 +52,15 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
+import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -142,13 +146,24 @@ public class DevicePolicyManager {
* {@link #ACTION_PROVISION_MANAGED_PROFILE} this package has to match the package name of the
* application that started provisioning. The package will be set as profile owner in that case.
*
- * <p>This package is set as device owner when device owner provisioning is started by an Nfc
- * message containing an Nfc record with MIME type {@link #MIME_TYPE_PROVISIONING_NFC}.
+ * <p>This package is set as device owner when device owner provisioning is started by an NFC
+ * message containing an NFC record with MIME type {@link #MIME_TYPE_PROVISIONING_NFC}.
*/
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
/**
+ * An {@link android.accounts.Account} extra holding the account to migrate during managed
+ * profile provisioning. If the account supplied is present in the primary user, it will be
+ * copied, along with its credentials to the managed profile and removed from the primary user.
+ *
+ * Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}.
+ */
+
+ public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE
+ = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
+
+ /**
* A String extra that, holds the email address of the account which a managed profile is
* created for. Used with {@link #ACTION_PROVISION_MANAGED_PROFILE} and
* {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE}.
@@ -165,11 +180,21 @@ public class DevicePolicyManager {
= "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
/**
+ * A Boolean extra that can be used by the mobile device management application to skip the
+ * disabling of system apps during provisioning when set to <code>true</code>.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED =
+ "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
+
+ /**
* A String extra holding the time zone {@link android.app.AlarmManager} that the device
* will be set to.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_TIME_ZONE
= "android.app.extra.PROVISIONING_TIME_ZONE";
@@ -178,8 +203,8 @@ public class DevicePolicyManager {
* A Long extra holding the wall clock time (in milliseconds) to be set on the device's
* {@link android.app.AlarmManager}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_LOCAL_TIME
= "android.app.extra.PROVISIONING_LOCAL_TIME";
@@ -188,8 +213,8 @@ public class DevicePolicyManager {
* A String extra holding the {@link java.util.Locale} that the device will be set to.
* Format: xx_yy, where xx is the language code, and yy the country code.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_LOCALE
= "android.app.extra.PROVISIONING_LOCALE";
@@ -198,8 +223,8 @@ public class DevicePolicyManager {
* A String extra holding the ssid of the wifi network that should be used during nfc device
* owner provisioning for downloading the mobile device management application.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_SSID
= "android.app.extra.PROVISIONING_WIFI_SSID";
@@ -208,8 +233,8 @@ public class DevicePolicyManager {
* A boolean extra indicating whether the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}
* is hidden or not.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_HIDDEN
= "android.app.extra.PROVISIONING_WIFI_HIDDEN";
@@ -218,8 +243,8 @@ public class DevicePolicyManager {
* A String extra indicating the security type of the wifi network in
* {@link #EXTRA_PROVISIONING_WIFI_SSID}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE
= "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
@@ -228,8 +253,8 @@ public class DevicePolicyManager {
* A String extra holding the password of the wifi network in
* {@link #EXTRA_PROVISIONING_WIFI_SSID}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_PASSWORD
= "android.app.extra.PROVISIONING_WIFI_PASSWORD";
@@ -238,8 +263,8 @@ public class DevicePolicyManager {
* A String extra holding the proxy host for the wifi network in
* {@link #EXTRA_PROVISIONING_WIFI_SSID}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_PROXY_HOST
= "android.app.extra.PROVISIONING_WIFI_PROXY_HOST";
@@ -248,8 +273,8 @@ public class DevicePolicyManager {
* An int extra holding the proxy port for the wifi network in
* {@link #EXTRA_PROVISIONING_WIFI_SSID}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_PROXY_PORT
= "android.app.extra.PROVISIONING_WIFI_PROXY_PORT";
@@ -258,8 +283,8 @@ public class DevicePolicyManager {
* A String extra holding the proxy bypass for the wifi network in
* {@link #EXTRA_PROVISIONING_WIFI_SSID}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_PROXY_BYPASS
= "android.app.extra.PROVISIONING_WIFI_PROXY_BYPASS";
@@ -268,8 +293,8 @@ public class DevicePolicyManager {
* A String extra holding the proxy auto-config (PAC) URL for the wifi network in
* {@link #EXTRA_PROVISIONING_WIFI_SSID}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_WIFI_PAC_URL
= "android.app.extra.PROVISIONING_WIFI_PAC_URL";
@@ -278,8 +303,8 @@ public class DevicePolicyManager {
* A String extra holding a url that specifies the download location of the device admin
* package. When not provided it is assumed that the device admin package is already installed.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
@@ -288,8 +313,8 @@ public class DevicePolicyManager {
* A String extra holding a http cookie header which should be used in the http request to the
* url specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
@@ -300,8 +325,8 @@ public class DevicePolicyManager {
* the file at the download location an error will be shown to the user and the user will be
* asked to factory reset the device.
*
- * <p>Use in an Nfc record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
- * provisioning via an Nfc bump.
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
*/
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
@@ -317,9 +342,9 @@ public class DevicePolicyManager {
* <p> A typical use case would be a device that is owned by a company, but used by either an
* employee or client.
*
- * <p> The Nfc message should be send to an unprovisioned device.
+ * <p> The NFC message should be send to an unprovisioned device.
*
- * <p>The Nfc record must contain a serialized {@link java.util.Properties} object which
+ * <p>The NFC record must contain a serialized {@link java.util.Properties} object which
* contains the following properties:
* <ul>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}</li>
@@ -1324,6 +1349,24 @@ public class DevicePolicyManager {
}
/**
+ * Returns the profile with the smallest maximum failed passwords for wipe,
+ * for the given user. So for primary user, it might return the primary or
+ * a managed profile. For a secondary user, it would be the same as the
+ * user passed in.
+ * @hide Used only by Keyguard
+ */
+ public int getProfileWithMinimumFailedPasswordsForWipe(int userHandle) {
+ if (mService != null) {
+ try {
+ return mService.getProfileWithMinimumFailedPasswordsForWipe(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
+ /**
* Flag for {@link #resetPassword}: don't allow other admins to change
* the password again until the user has entered it.
*/
@@ -1342,13 +1385,16 @@ public class DevicePolicyManager {
* characters when the requested quality is only numeric), in which case
* the currently active quality will be increased to match.
*
+ * <p>Calling with a null or empty password will clear any existing PIN,
+ * pattern or password if the current password constraints allow it.
+ *
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call
* this method; if it has not, a security exception will be thrown.
*
* <p>Calling this from a managed profile will throw a security exception.
*
- * @param password The new password for the user.
+ * @param password The new password for the user. Null or empty clears the password.
* @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}.
* @return Returns true if the password was applied, or false if it is
* not acceptable for the current constraints.
@@ -1431,22 +1477,30 @@ public class DevicePolicyManager {
/**
* Flag for {@link #wipeData(int)}: also erase the device's external
- * storage.
+ * storage (such as SD cards).
*/
public static final int WIPE_EXTERNAL_STORAGE = 0x0001;
/**
+ * Flag for {@link #wipeData(int)}: also erase the factory reset protection
+ * data.
+ *
+ * This flag may only be set by device owner admins; if it is set by other
+ * admins a {@link SecurityException} will be thrown.
+ */
+ public static final int WIPE_RESET_PROTECTION_DATA = 0x0002;
+
+ /**
* Ask the user data be wiped. This will cause the device to reboot,
- * erasing all user data while next booting up. External storage such
- * as SD cards will be also erased if the flag {@link #WIPE_EXTERNAL_STORAGE}
- * is set.
+ * erasing all user data while next booting up.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to be able to call
* this method; if it has not, a security exception will be thrown.
*
- * @param flags Bit mask of additional options: currently 0 and
- * {@link #WIPE_EXTERNAL_STORAGE} are supported.
+ * @param flags Bit mask of additional options: currently supported flags
+ * are {@link #WIPE_EXTERNAL_STORAGE} and
+ * {@link #WIPE_RESET_PROTECTION_DATA}.
*/
public void wipeData(int flags) {
if (mService != null) {
@@ -1868,13 +1922,15 @@ public class DevicePolicyManager {
String alias) {
try {
final byte[] pemCert = Credentials.convertToPem(cert);
- return mService.installKeyPair(who, privKey.getEncoded(), pemCert, alias);
- } catch (CertificateException e) {
- Log.w(TAG, "Error encoding certificate", e);
- } catch (IOException e) {
- Log.w(TAG, "Error writing certificate", e);
+ final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
+ .getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded();
+ return mService.installKeyPair(who, pkcs8Key, pemCert, alias);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ Log.w(TAG, "Failed to obtain private key material", e);
+ } catch (CertificateException | IOException e) {
+ Log.w(TAG, "Could not pem-encode certificate", e);
}
return false;
}
@@ -3400,12 +3456,13 @@ public class DevicePolicyManager {
}
/**
- * Called by profile or device owners to check whether a user has been blocked from
- * uninstalling a package.
+ * Check whether the current user has been blocked by device policy from uninstalling a package.
+ * Requires the caller to be the profile owner if checking a specific admin's policy.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin The name of the admin component whose blocking policy will be checked, or null
+ * to check if any admin has blocked the uninstallation.
* @param packageName package to check.
- * @return true if the user shouldn't be able to uninstall the package.
+ * @return true if uninstallation is blocked.
*/
public boolean isUninstallBlocked(ComponentName admin, String packageName) {
if (mService != null) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 07aa800..d144ae8 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -66,6 +66,7 @@ interface IDevicePolicyManager {
boolean isActivePasswordSufficient(int userHandle);
int getCurrentFailedPasswordAttempts(int userHandle);
+ int getProfileWithMinimumFailedPasswordsForWipe(int userHandle);
void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num, int userHandle);
int getMaximumFailedPasswordsForWipe(in ComponentName admin, int userHandle);
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 0a2d4f5..41ad936 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -303,4 +303,11 @@ interface IBackupManager {
* {@code false} otherwise.
*/
void setBackupServiceActive(int whichUser, boolean makeActive);
+
+ /**
+ * Queries the activity status of backup service as set by {@link #setBackupServiceActive}.
+ * @param whichUser User handle of the defined user whose backup active state
+ * is being queried.
+ */
+ boolean isBackupServiceActive(int whichUser);
}
diff --git a/core/java/android/app/backup/RecentsBackupHelper.java b/core/java/android/app/backup/RecentsBackupHelper.java
new file mode 100644
index 0000000..fd69d20
--- /dev/null
+++ b/core/java/android/app/backup/RecentsBackupHelper.java
@@ -0,0 +1,114 @@
+package android.app.backup;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.util.Slog;
+
+import java.io.File;
+
+/**
+ * Helper for saving/restoring 'recent tasks' infrastructure.
+ * @hide
+ */
+public class RecentsBackupHelper implements BackupHelper {
+ private static final String TAG = "RecentsBackup";
+ private static final boolean DEBUG = false;
+
+ // This must match TaskPersister.TASKS_DIRNAME, but that class is not accessible from here
+ private static final String RECENTS_TASK_DIR = "recent_tasks";
+
+ // Must match TaskPersister.IMAGES_DIRNAME, as above
+ private static final String RECENTS_IMAGE_DIR = "recent_images";
+
+ // At restore time, tasks/thumbnails are placed in these directories alongside
+ // the "live" recents dirs named above.
+ private static final String RECENTS_TASK_RESTORE_DIR = "restored_" + RECENTS_TASK_DIR;
+ private static final String RECENTS_IMAGE_RESTORE_DIR = "restored_" + RECENTS_IMAGE_DIR;
+
+ // Prefixes for tagging the two kinds of recents backup records that we might generate
+ private static final String RECENTS_TASK_KEY = "task:";
+ private static final String RECENTS_IMAGE_KEY = "image:";
+
+ FileBackupHelperBase mTaskFileHelper;
+
+ final File mSystemDir;
+ final File mTasksDir;
+ final File mRestoredTasksDir;
+ final File mRestoredImagesDir;
+ final String[] mRecentFiles;
+ final String[] mRecentKeys;
+
+ /**
+ * @param context The agent context in which this helper instance will run
+ */
+ public RecentsBackupHelper(Context context) {
+ mTaskFileHelper = new FileBackupHelperBase(context);
+
+ mSystemDir = new File(Environment.getDataDirectory(), "system");
+ mTasksDir = new File(mSystemDir, RECENTS_TASK_DIR);
+ mRestoredTasksDir = new File(mSystemDir, RECENTS_TASK_RESTORE_DIR);
+ mRestoredImagesDir = new File(mSystemDir, RECENTS_IMAGE_RESTORE_DIR);
+
+ // Currently we back up only the recent-task descriptions, not the thumbnails
+ File[] recentFiles = mTasksDir.listFiles();
+ if (recentFiles != null) {
+ // We explicitly proceed even if this is a zero-size array
+ final int N = recentFiles.length;
+ mRecentKeys = new String[N];
+ mRecentFiles = new String[N];
+ if (DEBUG) {
+ Slog.i(TAG, "Identifying recents for backup: " + N);
+ }
+ for (int i = 0; i < N; i++) {
+ mRecentKeys[i] = new String(RECENTS_TASK_KEY + recentFiles[i].getName());
+ mRecentFiles[i] = recentFiles[i].getAbsolutePath();
+ if (DEBUG) {
+ Slog.i(TAG, " " + mRecentKeys[i]);
+ }
+ }
+ } else {
+ mRecentFiles = mRecentKeys = new String[0];
+ }
+ }
+
+ /**
+ * Task-file key: RECENTS_TASK_KEY + leaf filename
+ * Thumbnail-file key: RECENTS_IMAGE_KEY + leaf filename
+ */
+ @Override
+ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
+ FileBackupHelperBase.performBackup_checked(oldState, data, newState,
+ mRecentFiles, mRecentKeys);
+ }
+
+ @Override
+ public void restoreEntity(BackupDataInputStream data) {
+ final String key = data.getKey();
+ File output = null;
+ if (key.startsWith(RECENTS_TASK_KEY)) {
+ String name = key.substring(RECENTS_TASK_KEY.length());
+ output = new File(mRestoredTasksDir, name);
+ mRestoredTasksDir.mkdirs();
+ } else if (key.startsWith(RECENTS_IMAGE_KEY)) {
+ String name = key.substring(RECENTS_IMAGE_KEY.length());
+ output = new File(mRestoredImagesDir, name);
+ mRestoredImagesDir.mkdirs();
+ }
+
+ if (output != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "Restoring key='"
+ + key + "' to " + output.getAbsolutePath());
+ }
+ mTaskFileHelper.writeFile(output, data);
+ }
+ }
+
+ @Override
+ public void writeNewStateDescription(ParcelFileDescriptor newState) {
+ mTaskFileHelper.writeNewStateDescription(newState);
+ }
+
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 0193711..68ea0aa 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -29,5 +29,6 @@ interface ITrustManager {
void reportRequireCredentialEntry(int userId);
void registerTrustListener(in ITrustListener trustListener);
void unregisterTrustListener(in ITrustListener trustListener);
- boolean isTrusted(int userId);
+ void reportKeyguardShowingChanged();
+ boolean isDeviceLocked(int userId);
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 3d262b1..705a144 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -88,6 +88,19 @@ public class TrustManager {
}
/**
+ * Reports that the visibility of the keyguard has changed.
+ *
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ */
+ public void reportKeyguardShowingChanged() {
+ try {
+ mService.reportKeyguardShowingChanged();
+ } catch (RemoteException e) {
+ onError(e);
+ }
+ }
+
+ /**
* Registers a listener for trust events.
*
* Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index a019d5c..e76c23b 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -117,8 +117,9 @@ public final class BluetoothLeAdvertiser {
AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED);
return;
}
- if (totalBytes(advertiseData) > MAX_ADVERTISING_DATA_BYTES ||
- totalBytes(scanResponse) > MAX_ADVERTISING_DATA_BYTES) {
+ boolean isConnectable = settings.isConnectable();
+ if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES ||
+ totalBytes(scanResponse, isConnectable) > MAX_ADVERTISING_DATA_BYTES) {
postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
return;
}
@@ -171,9 +172,10 @@ public final class BluetoothLeAdvertiser {
}
// Compute the size of the advertise data.
- private int totalBytes(AdvertiseData data) {
+ private int totalBytes(AdvertiseData data, boolean isConnectable) {
if (data == null) return 0;
- int size = FLAGS_FIELD_BYTES; // flags field is always set.
+ // Flags field is omitted if the advertising is not connectable.
+ int size = isConnectable ? FLAGS_FIELD_BYTES : 0;
if (data.getServiceUuids() != null) {
int num16BitUuids = 0;
int num32BitUuids = 0;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index c8f9b7d..360f308 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -31,6 +31,7 @@ import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
@@ -201,7 +202,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
}
@@ -227,7 +228,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return rejectInsert(uri, initialValues);
}
final String original = setCallingPackage(callingPkg);
@@ -242,7 +243,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
}
final String original = setCallingPackage(callingPkg);
@@ -270,13 +271,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
operations.set(i, operation);
}
if (operation.isReadOperation()) {
- if (enforceReadPermission(callingPkg, uri)
+ if (enforceReadPermission(callingPkg, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
if (operation.isWriteOperation()) {
- if (enforceWritePermission(callingPkg, uri)
+ if (enforceWritePermission(callingPkg, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
@@ -301,7 +302,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
}
final String original = setCallingPackage(callingPkg);
@@ -317,7 +318,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
String[] selectionArgs) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
}
final String original = setCallingPackage(callingPkg);
@@ -330,11 +331,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public ParcelFileDescriptor openFile(
- String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
- throws FileNotFoundException {
+ String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
+ IBinder callerToken) throws FileNotFoundException {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, mode);
+ enforceFilePermission(callingPkg, uri, mode, callerToken);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.openFile(
@@ -350,7 +351,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
throws FileNotFoundException {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, mode);
+ enforceFilePermission(callingPkg, uri, mode, null);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.openAssetFile(
@@ -382,7 +383,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, "r");
+ enforceFilePermission(callingPkg, uri, "r", null);
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.openTypedAssetFile(
@@ -402,7 +403,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return null;
}
final String original = setCallingPackage(callingPkg);
@@ -418,7 +419,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return null;
}
final String original = setCallingPackage(callingPkg);
@@ -429,29 +430,33 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
}
- private void enforceFilePermission(String callingPkg, Uri uri, String mode)
- throws FileNotFoundException, SecurityException {
+ private void enforceFilePermission(String callingPkg, Uri uri, String mode,
+ IBinder callerToken) throws FileNotFoundException, SecurityException {
if (mode != null && mode.indexOf('w') != -1) {
- if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, uri, callerToken)
+ != AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
} else {
- if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, uri, callerToken)
+ != AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
}
}
- private int enforceReadPermission(String callingPkg, Uri uri) throws SecurityException {
- enforceReadPermissionInner(uri);
+ private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)
+ throws SecurityException {
+ enforceReadPermissionInner(uri, callerToken);
if (mReadOp != AppOpsManager.OP_NONE) {
return mAppOpsManager.noteOp(mReadOp, Binder.getCallingUid(), callingPkg);
}
return AppOpsManager.MODE_ALLOWED;
}
- private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException {
- enforceWritePermissionInner(uri);
+ private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)
+ throws SecurityException {
+ enforceWritePermissionInner(uri, callerToken);
if (mWriteOp != AppOpsManager.OP_NONE) {
return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg);
}
@@ -467,7 +472,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/** {@hide} */
- protected void enforceReadPermissionInner(Uri uri) throws SecurityException {
+ protected void enforceReadPermissionInner(Uri uri, IBinder callerToken)
+ throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -480,7 +486,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+ if (context.checkPermission(componentPerm, pid, uid, callerToken)
+ == PERMISSION_GRANTED) {
return;
} else {
missingPerm = componentPerm;
@@ -497,7 +504,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+ if (context.checkPermission(pathPerm, pid, uid, callerToken)
+ == PERMISSION_GRANTED) {
return;
} else {
// any denied <path-permission> means we lose
@@ -518,8 +526,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
final int callingUserId = UserHandle.getUserId(uid);
final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
? maybeAddUserId(uri, callingUserId) : uri;
- if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
- == PERMISSION_GRANTED) {
+ if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ callerToken) == PERMISSION_GRANTED) {
return;
}
@@ -532,7 +540,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/** {@hide} */
- protected void enforceWritePermissionInner(Uri uri) throws SecurityException {
+ protected void enforceWritePermissionInner(Uri uri, IBinder callerToken)
+ throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -545,7 +554,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+ if (context.checkPermission(componentPerm, pid, uid, callerToken)
+ == PERMISSION_GRANTED) {
return;
} else {
missingPerm = componentPerm;
@@ -562,7 +572,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+ if (context.checkPermission(pathPerm, pid, uid, callerToken)
+ == PERMISSION_GRANTED) {
return;
} else {
// any denied <path-permission> means we lose
@@ -580,8 +591,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
// last chance, check against any uri grants
- if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- == PERMISSION_GRANTED) {
+ if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ callerToken) == PERMISSION_GRANTED) {
return;
}
@@ -1659,12 +1670,6 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
- /*
- * We may be using AsyncTask from binder threads. Make it init here
- * so its static handler is on the main thread.
- */
- AsyncTask.init();
-
mNoPerms = testing;
/*
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index cefc27f..e15ac94 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -288,7 +288,7 @@ public class ContentProviderClient {
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
+ return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 39286d6..f2e7fc4 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -234,9 +234,10 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
+ IBinder callerToken = data.readStrongBinder();
ParcelFileDescriptor fd;
- fd = openFile(callingPkg, url, mode, signal);
+ fd = openFile(callingPkg, url, mode, signal, callerToken);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -575,7 +576,7 @@ final class ContentProviderProxy implements IContentProvider
@Override
public ParcelFileDescriptor openFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal)
+ String callingPkg, Uri url, String mode, ICancellationSignal signal, IBinder token)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -586,6 +587,7 @@ final class ContentProviderProxy implements IContentProvider
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
+ data.writeStrongBinder(token);
mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c9b7d0a..e6bb09f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -37,6 +37,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.StatFs;
import android.os.UserHandle;
@@ -2244,6 +2245,8 @@ public abstract class Context {
* @see android.media.MediaRouter
* @see #TELEPHONY_SERVICE
* @see android.telephony.TelephonyManager
+ * @see #TELEPHONY_SUBSCRIPTION_SERVICE
+ * @see android.telephony.SubscriptionManager
* @see #INPUT_METHOD_SERVICE
* @see android.view.inputmethod.InputMethodManager
* @see #UI_MODE_SERVICE
@@ -2588,6 +2591,16 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.telephony.SubscriptionManager} for handling management the
+ * telephony subscriptions of the device.
+ *
+ * @see #getSystemService
+ * @see android.telephony.SubscriptionManager
+ */
+ public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.telecom.TelecomManager} to manage telecom-related features
* of the device.
*
@@ -2864,11 +2877,10 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a {@link
- * android.app.UsageStatsManager} for interacting with the status bar.
+ * android.app.usage.UsageStatsManager} for querying device usage stats.
*
* @see #getSystemService
- * @see android.app.UsageStatsManager
- * @hide
+ * @see android.app.usage.UsageStatsManager
*/
public static final String USAGE_STATS_SERVICE = "usagestats";
@@ -2921,6 +2933,11 @@ public abstract class Context {
@PackageManager.PermissionResult
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
+ /** @hide */
+ @PackageManager.PermissionResult
+ public abstract int checkPermission(@NonNull String permission, int pid, int uid,
+ IBinder callerToken);
+
/**
* Determine whether the calling process of an IPC you are handling has been
* granted a particular permission. This is basically the same as calling
@@ -3108,6 +3125,10 @@ public abstract class Context {
public abstract int checkUriPermission(Uri uri, int pid, int uid,
@Intent.AccessUriMode int modeFlags);
+ /** @hide */
+ public abstract int checkUriPermission(Uri uri, int pid, int uid,
+ @Intent.AccessUriMode int modeFlags, IBinder callerToken);
+
/**
* Determine whether the calling process and user ID has been
* granted permission to access a specific URI. This is basically
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index ad7c350..cfae1cf 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -29,6 +29,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
import android.view.DisplayAdjustments;
@@ -566,6 +567,12 @@ public class ContextWrapper extends Context {
return mBase.checkPermission(permission, pid, uid);
}
+ /** @hide */
+ @Override
+ public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+ return mBase.checkPermission(permission, pid, uid, callerToken);
+ }
+
@Override
public int checkCallingPermission(String permission) {
return mBase.checkCallingPermission(permission);
@@ -608,6 +615,12 @@ public class ContextWrapper extends Context {
return mBase.checkUriPermission(uri, pid, uid, modeFlags);
}
+ /** @hide */
+ @Override
+ public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+ return mBase.checkUriPermission(uri, pid, uid, modeFlags, callerToken);
+ }
+
@Override
public int checkCallingUriPermission(Uri uri, int modeFlags) {
return mBase.checkCallingUriPermission(uri, modeFlags);
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index f92a404..f858406 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -47,7 +47,8 @@ public interface IContentProvider extends IInterface {
public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
public ParcelFileDescriptor openFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal)
+ String callingPkg, Uri url, String mode, ICancellationSignal signal,
+ IBinder callerToken)
throws RemoteException, FileNotFoundException;
public AssetFileDescriptor openAssetFile(
String callingPkg, Uri url, String mode, ICancellationSignal signal)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 57f6028..de7fbab 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3763,7 +3763,7 @@ public class Intent implements Parcelable, Cloneable {
* This flag is used to open a document into a new task rooted at the activity launched
* by this Intent. Through the use of this flag, or its equivalent attribute,
* {@link android.R.attr#documentLaunchMode} multiple instances of the same activity
- * containing different douments will appear in the recent tasks list.
+ * containing different documents will appear in the recent tasks list.
*
* <p>The use of the activity attribute form of this,
* {@link android.R.attr#documentLaunchMode}, is
@@ -4010,6 +4010,20 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int URI_ANDROID_APP_SCHEME = 1<<1;
+ /**
+ * Flag for use with {@link #toUri} and {@link #parseUri}: allow parsing
+ * of unsafe information. In particular, the flags {@link #FLAG_GRANT_READ_URI_PERMISSION},
+ * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, {@link #FLAG_GRANT_PERSISTABLE_URI_PERMISSION},
+ * and {@link #FLAG_GRANT_PREFIX_URI_PERMISSION} flags can not be set, so that the
+ * generated Intent can not cause unexpected data access to happen.
+ *
+ * <p>If you do not trust the source of the URI being parsed, you should still do further
+ * processing to protect yourself from it. In particular, when using it to start an
+ * activity you should usually add in {@link #CATEGORY_BROWSABLE} to limit the activities
+ * that can handle it.</p>
+ */
+ public static final int URI_ALLOW_UNSAFE = 1<<2;
+
// ---------------------------------------------------------------------
private String mAction;
@@ -4309,7 +4323,7 @@ public class Intent implements Parcelable, Cloneable {
// old format Intent URI
} else if (!uri.startsWith("#Intent;", i)) {
if (!androidApp) {
- return getIntentOld(uri);
+ return getIntentOld(uri, flags);
} else {
i = -1;
}
@@ -4359,6 +4373,9 @@ public class Intent implements Parcelable, Cloneable {
// launch flags
else if (uri.startsWith("launchFlags=", i)) {
intent.mFlags = Integer.decode(value).intValue();
+ if ((flags& URI_ALLOW_UNSAFE) == 0) {
+ intent.mFlags &= ~IMMUTABLE_FLAGS;
+ }
}
// package
@@ -4488,6 +4505,10 @@ public class Intent implements Parcelable, Cloneable {
}
public static Intent getIntentOld(String uri) throws URISyntaxException {
+ return getIntentOld(uri, 0);
+ }
+
+ private static Intent getIntentOld(String uri, int flags) throws URISyntaxException {
Intent intent;
int i = uri.lastIndexOf('#');
@@ -4536,6 +4557,9 @@ public class Intent implements Parcelable, Cloneable {
i += 12;
int j = uri.indexOf(')', i);
intent.mFlags = Integer.decode(uri.substring(i, j)).intValue();
+ if ((flags& URI_ALLOW_UNSAFE) == 0) {
+ intent.mFlags &= ~IMMUTABLE_FLAGS;
+ }
i = j + 1;
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index b66bd01..9223269 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -41,14 +41,30 @@ public class PackageInfo implements Parcelable {
* attribute.
*/
public int versionCode;
-
+
/**
* The version name of this package, as specified by the &lt;manifest&gt;
* tag's {@link android.R.styleable#AndroidManifest_versionName versionName}
* attribute.
*/
public String versionName;
-
+
+ /**
+ * The revision number of the base APK for this package, as specified by the
+ * &lt;manifest&gt; tag's
+ * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode}
+ * attribute.
+ */
+ public int baseRevisionCode;
+
+ /**
+ * The revision number of any split APKs for this package, as specified by
+ * the &lt;manifest&gt; tag's
+ * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode}
+ * attribute. Indexes are a 1:1 mapping against {@link #splitNames}.
+ */
+ public int[] splitRevisionCodes;
+
/**
* The shared user ID name of this package, as specified by the &lt;manifest&gt;
* tag's {@link android.R.styleable#AndroidManifest_sharedUserId sharedUserId}
@@ -257,21 +273,26 @@ public class PackageInfo implements Parcelable {
public PackageInfo() {
}
+ @Override
public String toString() {
return "PackageInfo{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + packageName + "}";
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
dest.writeString(versionName);
+ dest.writeInt(baseRevisionCode);
+ dest.writeIntArray(splitRevisionCodes);
dest.writeString(sharedUserId);
dest.writeInt(sharedUserLabel);
if (applicationInfo != null) {
@@ -305,10 +326,12 @@ public class PackageInfo implements Parcelable {
public static final Parcelable.Creator<PackageInfo> CREATOR
= new Parcelable.Creator<PackageInfo>() {
+ @Override
public PackageInfo createFromParcel(Parcel source) {
return new PackageInfo(source);
}
+ @Override
public PackageInfo[] newArray(int size) {
return new PackageInfo[size];
}
@@ -316,9 +339,11 @@ public class PackageInfo implements Parcelable {
private PackageInfo(Parcel source) {
packageName = source.readString();
- splitNames = source.readStringArray();
+ splitNames = source.createStringArray();
versionCode = source.readInt();
versionName = source.readString();
+ baseRevisionCode = source.readInt();
+ splitRevisionCodes = source.createIntArray();
sharedUserId = source.readString();
sharedUserLabel = source.readInt();
int hasApp = source.readInt();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index e336c5f..1efe082 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -19,6 +19,8 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.content.PackageHelper;
+
/**
* Basic information about a package as specified in its manifest.
* Utility class used in PackageManager methods
@@ -31,11 +33,19 @@ public class PackageInfoLite implements Parcelable {
*/
public String packageName;
+ /** Names of any split APKs, ordered by parsed splitName */
+ public String[] splitNames;
+
/**
* The android:versionCode of the package.
*/
public int versionCode;
+ /** Revision code of base APK */
+ public int baseRevisionCode;
+ /** Revision codes of any split APKs, ordered by parsed splitName */
+ public int[] splitRevisionCodes;
+
/**
* The android:multiArch flag from the package manifest. If set,
* we will extract all native libraries for the given app, not just those
@@ -70,7 +80,10 @@ public class PackageInfoLite implements Parcelable {
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
+ dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
+ dest.writeInt(baseRevisionCode);
+ dest.writeIntArray(splitRevisionCodes);
dest.writeInt(recommendedInstallLocation);
dest.writeInt(installLocation);
dest.writeInt(multiArch ? 1 : 0);
@@ -96,7 +109,10 @@ public class PackageInfoLite implements Parcelable {
private PackageInfoLite(Parcel source) {
packageName = source.readString();
+ splitNames = source.createStringArray();
versionCode = source.readInt();
+ baseRevisionCode = source.readInt();
+ splitRevisionCodes = source.createIntArray();
recommendedInstallLocation = source.readInt();
installLocation = source.readInt();
multiArch = (source.readInt() != 0);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e9f7c50..5c705e6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1078,6 +1078,26 @@ public abstract class PackageManager {
"android.hardware.camera.capability.raw";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+ * of the cameras on the device supports the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}
+ * capability level.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_CAPABILITY_BURST_CAPTURE =
+ "android.hardware.camera.capability.burst_capture";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+ * of the cameras on the device supports the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}
+ * capability level.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_CAPABILITY_READ_SENSOR_SETTINGS =
+ "android.hardware.camera.capability.read_sensor_settings";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* consumer IR devices.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8515520..82da7c5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -261,11 +261,16 @@ public class PackageParser {
/** Paths of any split APKs, ordered by parsed splitName */
public final String[] splitCodePaths;
+ /** Revision code of base APK */
+ public final int baseRevisionCode;
+ /** Revision codes of any split APKs, ordered by parsed splitName */
+ public final int[] splitRevisionCodes;
+
public final boolean coreApp;
public final boolean multiArch;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
- String[] splitCodePaths) {
+ String[] splitCodePaths, int[] splitRevisionCodes) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.installLocation = baseApk.installLocation;
@@ -274,6 +279,8 @@ public class PackageParser {
this.codePath = codePath;
this.baseCodePath = baseApk.codePath;
this.splitCodePaths = splitCodePaths;
+ this.baseRevisionCode = baseApk.revisionCode;
+ this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.multiArch = baseApk.multiArch;
}
@@ -296,6 +303,7 @@ public class PackageParser {
public final String packageName;
public final String splitName;
public final int versionCode;
+ public final int revisionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
@@ -303,12 +311,13 @@ public class PackageParser {
public final boolean multiArch;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
- int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
- boolean coreApp, boolean multiArch) {
+ int revisionCode, int installLocation, List<VerifierInfo> verifiers,
+ Signature[] signatures, boolean coreApp, boolean multiArch) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
this.versionCode = versionCode;
+ this.revisionCode = revisionCode;
this.installLocation = installLocation;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
this.signatures = signatures;
@@ -409,6 +418,8 @@ public class PackageParser {
pi.packageName = p.packageName;
pi.splitNames = p.splitNames;
pi.versionCode = p.mVersionCode;
+ pi.baseRevisionCode = p.baseRevisionCode;
+ pi.splitRevisionCodes = p.splitRevisionCodes;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
@@ -647,7 +658,7 @@ public class PackageParser {
throws PackageParserException {
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
- return new PackageLite(packagePath, baseApk, null, null);
+ return new PackageLite(packagePath, baseApk, null, null, null);
}
private static PackageLite parseClusterPackageLite(File packageDir, int flags)
@@ -704,20 +715,24 @@ public class PackageParser {
String[] splitNames = null;
String[] splitCodePaths = null;
+ int[] splitRevisionCodes = null;
if (size > 0) {
splitNames = new String[size];
splitCodePaths = new String[size];
+ splitRevisionCodes = new int[size];
splitNames = apks.keySet().toArray(splitNames);
Arrays.sort(splitNames, sSplitNameComparator);
for (int i = 0; i < size; i++) {
splitCodePaths[i] = apks.get(splitNames[i]).codePath;
+ splitRevisionCodes[i] = apks.get(splitNames[i]).revisionCode;
}
}
final String codePath = packageDir.getAbsolutePath();
- return new PackageLite(codePath, baseApk, splitNames, splitCodePaths);
+ return new PackageLite(codePath, baseApk, splitNames, splitCodePaths,
+ splitRevisionCodes);
}
/**
@@ -782,6 +797,7 @@ public class PackageParser {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
+ pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
for (int i = 0; i < num; i++) {
@@ -1249,25 +1265,21 @@ public class PackageParser {
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
+ int revisionCode = 0;
boolean coreApp = false;
boolean multiArch = false;
- int numFound = 0;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
- String attr = attrs.getAttributeName(i);
+ final String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
- numFound++;
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
- numFound++;
+ } else if (attr.equals("revisionCode")) {
+ revisionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("coreApp")) {
coreApp = attrs.getAttributeBooleanValue(i, false);
- numFound++;
- }
- if (numFound >= 3) {
- break;
}
}
@@ -1301,7 +1313,7 @@ public class PackageParser {
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
- installLocation, verifiers, signatures, coreApp, multiArch);
+ revisionCode, installLocation, verifiers, signatures, coreApp, multiArch);
}
/**
@@ -1359,6 +1371,8 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
+ pkg.baseRevisionCode = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
@@ -4168,6 +4182,7 @@ public class PackageParser {
public final static class Package {
public String packageName;
+
/** Names of any split APKs, ordered by parsed splitName */
public String[] splitNames;
@@ -4185,6 +4200,11 @@ public class PackageParser {
/** Paths of any split APKs, ordered by parsed splitName */
public String[] splitCodePaths;
+ /** Revision code of base APK */
+ public int baseRevisionCode;
+ /** Revision codes of any split APKs, ordered by parsed splitName */
+ public int[] splitRevisionCodes;
+
/** Flags of any split APKs; ordered by parsed splitName */
public int[] splitFlags;
@@ -4222,7 +4242,7 @@ public class PackageParser {
// The version code declared for this package.
public int mVersionCode;
-
+
// The version name declared for this package.
public String mVersionName;
diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java
index 8a43472..335a45e 100644
--- a/core/java/android/content/pm/ParceledListSlice.java
+++ b/core/java/android/content/pm/ParceledListSlice.java
@@ -30,6 +30,12 @@ import java.util.List;
* Transfer a large list of Parcelable objects across an IPC. Splits into
* multiple transactions if needed.
*
+ * Caveat: for efficiency and security, all elements must be the same concrete type.
+ * In order to avoid writing the class name of each object, we must ensure that
+ * each object is the same type, or else unparceling then reparceling the data may yield
+ * a different result if the class name encoded in the Parcelable is a Base type.
+ * See b/17671747.
+ *
* @hide
*/
public class ParceledListSlice<T extends Parcelable> implements Parcelable {
@@ -56,13 +62,25 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
if (N <= 0) {
return;
}
+
Parcelable.Creator<T> creator = p.readParcelableCreator(loader);
+ Class<?> listElementClass = null;
+
int i = 0;
while (i < N) {
if (p.readInt() == 0) {
break;
}
- mList.add(p.readCreator(creator, loader));
+
+ final T parcelable = p.readCreator(creator, loader);
+ if (listElementClass == null) {
+ listElementClass = parcelable.getClass();
+ } else {
+ verifySameType(listElementClass, parcelable.getClass());
+ }
+
+ mList.add(parcelable);
+
if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
i++;
}
@@ -82,7 +100,11 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
return;
}
while (i < N && reply.readInt() != 0) {
- mList.add(reply.readCreator(creator, loader));
+ final T parcelable = reply.readCreator(creator, loader);
+ verifySameType(listElementClass, parcelable.getClass());
+
+ mList.add(parcelable);
+
if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
i++;
}
@@ -91,6 +113,14 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
}
}
+ private static void verifySameType(final Class<?> expected, final Class<?> actual) {
+ if (!actual.equals(expected)) {
+ throw new IllegalArgumentException("Can't unparcel type "
+ + actual.getName() + " in list of type "
+ + expected.getName());
+ }
+ }
+
public List<T> getList() {
return mList;
}
@@ -116,11 +146,16 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
dest.writeInt(N);
if (DEBUG) Log.d(TAG, "Writing " + N + " items");
if (N > 0) {
+ final Class<?> listElementClass = mList.get(0).getClass();
dest.writeParcelableCreator(mList.get(0));
int i = 0;
while (i < N && dest.dataSize() < MAX_FIRST_IPC_SIZE) {
dest.writeInt(1);
- mList.get(i).writeToParcel(dest, callFlags);
+
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ parcelable.writeToParcel(dest, callFlags);
+
if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
i++;
}
@@ -137,7 +172,11 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
reply.writeInt(1);
- mList.get(i).writeToParcel(reply, callFlags);
+
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ parcelable.writeToParcel(reply, callFlags);
+
if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
i++;
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index e578822..ecae52c 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -666,6 +666,14 @@ public final class AssetManager implements AutoCloseable {
/**
* Get the locales that this asset manager contains data for.
+ *
+ * <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid
+ * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be
+ * parsed using {@link java.util.Locale#forLanguageTag(String)}.
+ *
+ * <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings
+ * are of the form {@code ll_CC} where {@code ll} is a two letter language code,
+ * and {@code CC} is a two letter country code.
*/
public native final String[] getLocales();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0145e05..78d3e9c 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -747,7 +747,7 @@ public class Resources {
*/
public Drawable getDrawable(int id) throws NotFoundException {
final Drawable d = getDrawable(id, null);
- if (d.canApplyTheme()) {
+ if (d != null && d.canApplyTheme()) {
Log.w(TAG, "Drawable " + getResourceName(id) + " has unresolved theme "
+ "attributes! Consider using Resources.getDrawable(int, Theme) or "
+ "Context.getDrawable(int).", new RuntimeException());
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 77b8a33..9652db7 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -311,13 +311,13 @@ final class StringBlock {
* the color black is returned instead.
*
* @param color The color as a string. Can be a resource reference,
- * HTML hexadecimal, octal or a name
+ * hexadecimal, octal or a name
* @param foreground True if the color will be used as the foreground color,
* false otherwise
*
* @return A CharacterStyle
*
- * @see Color#getHtmlColor(String)
+ * @see Color#parseColor(String)
*/
private static CharacterStyle getColor(String color, boolean foreground) {
int c = 0xff000000;
@@ -336,7 +336,11 @@ final class StringBlock {
}
}
} else {
- c = Color.getHtmlColor(color);
+ try {
+ c = Color.parseColor(color);
+ } catch (IllegalArgumentException e) {
+ c = Color.BLACK;
+ }
}
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index fb3eaff..98096dc 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -395,6 +395,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p><b>Range of valid values:</b><br></p>
* <p><code>Min.exposure compensation * {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} &lt;= -2 EV</code></p>
* <p><code>Max.exposure compensation * {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} &gt;= 2 EV</code></p>
+ * <p>LEGACY devices may support a smaller range than this, including the range [0,0], which
+ * indicates that changing the exposure compensation is not supported.</p>
* <p>This key is available on all devices.</p>
*
* @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP
@@ -1155,6 +1157,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING MANUAL_POST_PROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -1163,6 +1167,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
*/
@PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -2283,12 +2289,16 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>Camera devices will come in three flavors: LEGACY, LIMITED and FULL.</p>
* <p>A FULL device will support below capabilities:</p>
* <ul>
- * <li>30fps at maximum resolution (== sensor resolution) is preferred, more than 20fps is required.</li>
+ * <li>30fps operation at maximum resolution (== sensor resolution) is preferred, more than
+ * 20fps is required, for at least uncompressed YUV
+ * output. ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains BURST_CAPTURE)</li>
* <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
* <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR)</li>
- * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_POST_PROCESSING)</li>
+ * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+ * MANUAL_POST_PROCESSING)</li>
* <li>Arbitrary cropping region ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>==</code> FREEFORM)</li>
- * <li>At least 3 processed (but not stalling) format output streams ({@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC android.request.maxNumOutputProc} <code>&gt;=</code> 3)</li>
+ * <li>At least 3 processed (but not stalling) format output streams
+ * ({@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC android.request.maxNumOutputProc} <code>&gt;=</code> 3)</li>
* <li>The required stream configuration defined in android.scaler.availableStreamConfigurations</li>
* <li>The required exposure time range defined in {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
* <li>The required maxFrameDuration defined in {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0bb742c..9e90d01 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -334,6 +334,24 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
+ * <p>BURST-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}) devices
+ * support at least the below stream combinations in addition to those for
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices. Note that all
+ * FULL-level devices support the BURST capability, and the below list is a strict subset of the
+ * list for FULL-level devices, so this table is only relevant for LIMITED-level devices that
+ * support the BURST_CAPTURE capability.
+ *
+ * <table>
+ * <tr><th colspan="5">BURST-capability additional guaranteed configurations</th></tr>
+ * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution GPU processing with preview.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution in-app processing with preview.</td> </tr>
+ * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input in-app processsing.</td> </tr>
+ * </table><br>
+ * </p>
+ *
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
* by attempting to create a session with such targets.</p>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 9a9e4c2..b6bb33b 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -54,29 +54,17 @@ public final class CameraManager {
private static final String TAG = "CameraManager";
private final boolean DEBUG;
- /**
- * This should match the ICameraService definition
- */
- private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
private static final int USE_CALLING_UID = -1;
@SuppressWarnings("unused")
private static final int API_VERSION_1 = 1;
private static final int API_VERSION_2 = 2;
- // Access only through getCameraServiceLocked to deal with binder death
- private ICameraService mCameraService;
-
private ArrayList<String> mDeviceIdList;
- private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
- new ArrayMap<AvailabilityCallback, Handler>();
-
private final Context mContext;
private final Object mLock = new Object();
- private final CameraServiceListener mServiceListener = new CameraServiceListener();
-
/**
* @hide
*/
@@ -84,8 +72,6 @@ public final class CameraManager {
DEBUG = Log.isLoggable(TAG, Log.DEBUG);
synchronized(mLock) {
mContext = context;
-
- connectCameraServiceLocked();
}
}
@@ -116,6 +102,12 @@ public final class CameraManager {
* <p>The first time a callback is registered, it is immediately called
* with the availability status of all currently known camera devices.</p>
*
+ * <p>Since this callback will be registered with the camera service, remember to unregister it
+ * once it is no longer needed; otherwise the callback will continue to receive events
+ * indefinitely and it may prevent other resources from being released. Specifically, the
+ * callbacks will be invoked independently of the general activity lifecycle and independently
+ * of the state of individual CameraManager instances.</p>
+ *
* @param callback the new callback to send camera availability notices to
* @param handler The handler on which the callback should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper looper}.
@@ -130,13 +122,7 @@ public final class CameraManager {
handler = new Handler(looper);
}
- synchronized (mLock) {
- Handler oldHandler = mCallbackMap.put(callback, handler);
- // For new callbacks, provide initial availability information
- if (oldHandler == null) {
- mServiceListener.updateCallbackLocked(callback, handler);
- }
- }
+ CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
}
/**
@@ -148,9 +134,7 @@ public final class CameraManager {
* @param callback The callback to remove from the notification list
*/
public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
- synchronized (mLock) {
- mCallbackMap.remove(callback);
- }
+ CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
}
/**
@@ -187,7 +171,7 @@ public final class CameraManager {
* otherwise get them from the legacy shim instead.
*/
- ICameraService cameraService = getCameraServiceLocked();
+ ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"Camera service is currently unavailable");
@@ -268,7 +252,7 @@ public final class CameraManager {
try {
if (supportsCamera2ApiLocked(cameraId)) {
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
- ICameraService cameraService = getCameraServiceLocked();
+ ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
if (cameraService == null) {
throw new CameraRuntimeException(
CameraAccessException.CAMERA_DISCONNECTED,
@@ -444,13 +428,6 @@ public final class CameraManager {
}
/**
- * Temporary for migrating to Callback naming
- * @hide
- */
- public static abstract class AvailabilityListener extends AvailabilityCallback {
- }
-
- /**
* Return or create the list of currently connected camera devices.
*
* <p>In case of errors connecting to the camera service, will return an empty list.</p>
@@ -458,7 +435,7 @@ public final class CameraManager {
private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
if (mDeviceIdList == null) {
int numCameras = 0;
- ICameraService cameraService = getCameraServiceLocked();
+ ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
ArrayList<String> deviceIdList = new ArrayList<>();
// If no camera service, then no devices
@@ -515,18 +492,6 @@ public final class CameraManager {
return mDeviceIdList;
}
- private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) {
- int problem = e.getReason();
- switch (problem) {
- case CameraAccessException.CAMERA_DISCONNECTED:
- String errorMsg = CameraAccessException.getDefaultMessage(problem);
- Log.w(TAG, msg + ": " + errorMsg);
- break;
- default:
- throw new IllegalStateException(msg, e.asChecked());
- }
- }
-
/**
* Queries the camera service if it supports the camera2 api directly, or needs a shim.
*
@@ -556,7 +521,7 @@ public final class CameraManager {
* Anything else is an unexpected error we don't want to recover from.
*/
try {
- ICameraService cameraService = getCameraServiceLocked();
+ ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
// If no camera service, no support
if (cameraService == null) return false;
@@ -578,97 +543,23 @@ public final class CameraManager {
}
/**
- * Connect to the camera service if it's available, and set up listeners.
- *
- * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
+ * A per-process global camera manager instance, to retain a connection to the camera service,
+ * and to distribute camera availability notices to API-registered callbacks
*/
- private void connectCameraServiceLocked() {
- mCameraService = null;
- IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
- if (cameraServiceBinder == null) {
- // Camera service is now down, leave mCameraService as null
- return;
- }
- try {
- cameraServiceBinder.linkToDeath(new CameraServiceDeathListener(), /*flags*/ 0);
- } catch (RemoteException e) {
- // Camera service is now down, leave mCameraService as null
- return;
- }
+ private static final class CameraManagerGlobal extends ICameraServiceListener.Stub
+ implements IBinder.DeathRecipient {
+
+ private static final String TAG = "CameraManagerGlobal";
+ private final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+ // Singleton instance
+ private static final CameraManagerGlobal gCameraManager =
+ new CameraManagerGlobal();
/**
- * Wrap the camera service in a decorator which automatically translates return codes
- * into exceptions.
+ * This must match the ICameraService definition
*/
- ICameraService cameraService = CameraServiceBinderDecorator.newInstance(cameraServiceRaw);
-
- try {
- CameraServiceBinderDecorator.throwOnError(
- CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
- } catch (CameraRuntimeException e) {
- handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
- }
-
- try {
- cameraService.addListener(mServiceListener);
- mCameraService = cameraService;
- } catch(CameraRuntimeException e) {
- // Unexpected failure
- throw new IllegalStateException("Failed to register a camera service listener",
- e.asChecked());
- } catch (RemoteException e) {
- // Camera service is now down, leave mCameraService as null
- }
- }
-
- /**
- * Return a best-effort ICameraService.
- *
- * <p>This will be null if the camera service
- * is not currently available. If the camera service has died since the last
- * use of the camera service, will try to reconnect to the service.</p>
- */
- private ICameraService getCameraServiceLocked() {
- if (mCameraService == null) {
- Log.i(TAG, "getCameraServiceLocked: Reconnecting to camera service");
- connectCameraServiceLocked();
- if (mCameraService == null) {
- Log.e(TAG, "Camera service is unavailable");
- }
- }
- return mCameraService;
- }
-
- /**
- * Listener for camera service death.
- *
- * <p>The camera service isn't supposed to die under any normal circumstances, but can be turned
- * off during debug, or crash due to bugs. So detect that and null out the interface object, so
- * that the next calls to the manager can try to reconnect.</p>
- */
- private class CameraServiceDeathListener implements IBinder.DeathRecipient {
- public void binderDied() {
- synchronized(mLock) {
- mCameraService = null;
- // Tell listeners that the cameras are _available_, because any existing clients
- // will have gotten disconnected. This is optimistic under the assumption that the
- // service will be back shortly.
- //
- // Without this, a camera service crash while a camera is open will never signal to
- // listeners that previously in-use cameras are now available.
- for (String cameraId : mDeviceIdList) {
- mServiceListener.onStatusChangedLocked(CameraServiceListener.STATUS_PRESENT,
- cameraId);
- }
- }
- }
- }
-
- // TODO: this class needs unit tests
- // TODO: extract class into top level
- private class CameraServiceListener extends ICameraServiceListener.Stub {
+ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
// Keep up-to-date with ICameraServiceListener.h
@@ -683,16 +574,112 @@ public final class CameraManager {
// Camera is in use by another app and cannot be used exclusively
public static final int STATUS_NOT_AVAILABLE = 0x80000000;
+ // End enums shared with ICameraServiceListener.h
+
// Camera ID -> Status map
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
- private static final String TAG = "CameraServiceListener";
+ // Registered availablility callbacks and their handlers
+ private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
+ new ArrayMap<AvailabilityCallback, Handler>();
+
+ private final Object mLock = new Object();
+
+ // Access only through getCameraService to deal with binder death
+ private ICameraService mCameraService;
+
+ // Singleton, don't allow construction
+ private CameraManagerGlobal() {
+ }
+
+ public static CameraManagerGlobal get() {
+ return gCameraManager;
+ }
@Override
public IBinder asBinder() {
return this;
}
+ /**
+ * Return a best-effort ICameraService.
+ *
+ * <p>This will be null if the camera service is not currently available. If the camera
+ * service has died since the last use of the camera service, will try to reconnect to the
+ * service.</p>
+ */
+ public ICameraService getCameraService() {
+ synchronized(mLock) {
+ if (mCameraService == null) {
+ Log.i(TAG, "getCameraService: Reconnecting to camera service");
+ connectCameraServiceLocked();
+ if (mCameraService == null) {
+ Log.e(TAG, "Camera service is unavailable");
+ }
+ }
+ return mCameraService;
+ }
+ }
+
+ /**
+ * Connect to the camera service if it's available, and set up listeners.
+ *
+ * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
+ */
+ private void connectCameraServiceLocked() {
+ mCameraService = null;
+ IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
+ if (cameraServiceBinder == null) {
+ // Camera service is now down, leave mCameraService as null
+ return;
+ }
+ try {
+ cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
+ } catch (RemoteException e) {
+ // Camera service is now down, leave mCameraService as null
+ return;
+ }
+
+ ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+
+ /**
+ * Wrap the camera service in a decorator which automatically translates return codes
+ * into exceptions.
+ */
+ ICameraService cameraService =
+ CameraServiceBinderDecorator.newInstance(cameraServiceRaw);
+
+ try {
+ CameraServiceBinderDecorator.throwOnError(
+ CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
+ } catch (CameraRuntimeException e) {
+ handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
+ }
+
+ try {
+ cameraService.addListener(this);
+ mCameraService = cameraService;
+ } catch(CameraRuntimeException e) {
+ // Unexpected failure
+ throw new IllegalStateException("Failed to register a camera service listener",
+ e.asChecked());
+ } catch (RemoteException e) {
+ // Camera service is now down, leave mCameraService as null
+ }
+ }
+
+ private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) {
+ int problem = e.getReason();
+ switch (problem) {
+ case CameraAccessException.CAMERA_DISCONNECTED:
+ String errorMsg = CameraAccessException.getDefaultMessage(problem);
+ Log.w(TAG, msg + ": " + errorMsg);
+ break;
+ default:
+ throw new IllegalStateException(msg, e.asChecked());
+ }
+ }
+
private boolean isAvailable(int status) {
switch (status) {
case STATUS_PRESENT:
@@ -739,7 +726,7 @@ public final class CameraManager {
* Send the state of all known cameras to the provided listener, to initialize
* the listener's knowledge of camera state.
*/
- public void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
+ private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
for (int i = 0; i < mDeviceStatus.size(); i++) {
String id = mDeviceStatus.keyAt(i);
Integer status = mDeviceStatus.valueAt(i);
@@ -747,14 +734,7 @@ public final class CameraManager {
}
}
- @Override
- public void onStatusChanged(int status, int cameraId) throws RemoteException {
- synchronized(CameraManager.this.mLock) {
- onStatusChangedLocked(status, String.valueOf(cameraId));
- }
- }
-
- public void onStatusChangedLocked(int status, String id) {
+ private void onStatusChangedLocked(int status, String id) {
if (DEBUG) {
Log.v(TAG,
String.format("Camera id %s has status changed to 0x%x", id, status));
@@ -811,5 +791,72 @@ public final class CameraManager {
}
} // onStatusChangedLocked
- } // CameraServiceListener
+ /**
+ * Register a callback to be notified about camera device availability with the
+ * global listener singleton.
+ *
+ * @param callback the new callback to send camera availability notices to
+ * @param handler The handler on which the callback should be invoked. May not be null.
+ */
+ public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+ synchronized (mLock) {
+ Handler oldHandler = mCallbackMap.put(callback, handler);
+ // For new callbacks, provide initial availability information
+ if (oldHandler == null) {
+ updateCallbackLocked(callback, handler);
+ }
+ }
+ }
+
+ /**
+ * Remove a previously-added callback; the callback will no longer receive connection and
+ * disconnection callbacks, and is no longer referenced by the global listener singleton.
+ *
+ * @param callback The callback to remove from the notification list
+ */
+ public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
+ synchronized (mLock) {
+ mCallbackMap.remove(callback);
+ }
+ }
+
+ /**
+ * Callback from camera service notifying the process about camera availability changes
+ */
+ @Override
+ public void onStatusChanged(int status, int cameraId) throws RemoteException {
+ synchronized(mLock) {
+ onStatusChangedLocked(status, String.valueOf(cameraId));
+ }
+ }
+
+ /**
+ * Listener for camera service death.
+ *
+ * <p>The camera service isn't supposed to die under any normal circumstances, but can be
+ * turned off during debug, or crash due to bugs. So detect that and null out the interface
+ * object, so that the next calls to the manager can try to reconnect.</p>
+ */
+ public void binderDied() {
+ synchronized(mLock) {
+ // Only do this once per service death
+ if (mCameraService == null) return;
+
+ mCameraService = null;
+
+ // Tell listeners that the cameras are _available_, because any existing clients
+ // will have gotten disconnected. This is optimistic under the assumption that
+ // the service will be back shortly.
+ //
+ // Without this, a camera service crash while a camera is open will never signal
+ // to listeners that previously in-use cameras are now available.
+ for (int i = 0; i < mDeviceStatus.size(); i++) {
+ String cameraId = mDeviceStatus.keyAt(i);
+ onStatusChangedLocked(STATUS_PRESENT, cameraId);
+ }
+ }
+ }
+
+ } // CameraManagerGlobal
+
} // CameraManager
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 16df844..895ae04 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -450,6 +450,62 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4;
+ /**
+ * <p>The camera device supports accurately reporting the sensor settings for many of
+ * the sensor controls while the built-in 3A algorithm is running. This allows
+ * reporting of sensor settings even when these settings cannot be manually changed.</p>
+ * <p>The values reported for the following controls are guaranteed to be available
+ * in the CaptureResult, including when 3A is enabled:</p>
+ * <ul>
+ * <li>Exposure control<ul>
+ * <li>{@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</li>
+ * </ul>
+ * </li>
+ * <li>Sensitivity control<ul>
+ * <li>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</li>
+ * </ul>
+ * </li>
+ * <li>Lens controls (if the lens is adjustable)<ul>
+ * <li>{@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance}</li>
+ * <li>{@link CaptureRequest#LENS_APERTURE android.lens.aperture}</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <p>This capability is a subset of the MANUAL_SENSOR control capability, and will
+ * always be included if the MANUAL_SENSOR capability is available.</p>
+ *
+ * @see CaptureRequest#LENS_APERTURE
+ * @see CaptureRequest#LENS_FOCUS_DISTANCE
+ * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5;
+
+ /**
+ * <p>The camera device supports capturing maximum-resolution
+ * images at &gt;= 20 frames per second, in at least the
+ * uncompressed YUV format, when post-processing settings
+ * are set to FAST.</p>
+ * <p>More specifically, this means that a size matching the
+ * camera device's active array size is listed as a
+ * supported size for the YUV_420_888 format in
+ * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}, the minimum frame
+ * duration for that format and size is &lt;= 1/20 s, and
+ * the {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} entry
+ * lists at least one FPS range where the minimum FPS
+ * is &gt;= 1 / minimumFrameDuration for the maximum-size
+ * YUV_420_888 format.</p>
+ * <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is
+ * guaranted to have a value between 0 and 4, inclusive.</p>
+ *
+ * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ * @see CameraCharacteristics#SYNC_MAX_LATENCY
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
@@ -807,9 +863,21 @@ public abstract class CameraMetadata<TKey> {
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are used by the camera
* device, along with android.flash.* fields, if there's
* a flash unit for this camera device.</p>
+ * <p>Note that auto-white balance (AWB) and auto-focus (AF)
+ * behavior is device dependent when AE is in OFF mode.
+ * To have consistent behavior across different devices,
+ * it is recommended to either set AWB and AF to OFF mode
+ * or lock AWB and AF before setting AE to OFF.
+ * See {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode},
+ * {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}, and {@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}
+ * for more details.</p>
* <p>LEGACY devices do not support the OFF mode and will
* override attempts to use this value to ON.</p>
*
+ * @see CaptureRequest#CONTROL_AF_MODE
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
+ * @see CaptureRequest#CONTROL_AWB_LOCK
+ * @see CaptureRequest#CONTROL_AWB_MODE
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
* @see CaptureRequest#SENSOR_FRAME_DURATION
* @see CaptureRequest#SENSOR_SENSITIVITY
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6aec72a..48af67c 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -906,7 +906,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p>Whether auto-focus (AF) is currently enabled, and what
* mode it is set to.</p>
* <p>Only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} = AUTO and the lens is not fixed focus
- * (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} &gt; 0</code>).</p>
+ * (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} &gt; 0</code>). Also note that
+ * when {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is OFF, the behavior of AF is device
+ * dependent. It is recommended to lock AF by using {@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger} before
+ * setting {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} to OFF, or set AF mode to OFF when AE is OFF.</p>
* <p>If the lens is controlled by the camera device auto-focus algorithm,
* the camera device will report the current AF status in {@link CaptureResult#CONTROL_AF_STATE android.control.afState}
* in result metadata.</p>
@@ -923,8 +926,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* {@link CameraCharacteristics#CONTROL_AF_AVAILABLE_MODES android.control.afAvailableModes}</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_MODE
* @see CameraCharacteristics#CONTROL_AF_AVAILABLE_MODES
* @see CaptureResult#CONTROL_AF_STATE
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
* @see #CONTROL_AF_MODE_OFF
@@ -1046,7 +1051,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p>When set to the ON mode, the camera device's auto-white balance
* routine is enabled, overriding the application's selected
* {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
- * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
+ * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}. Note that when {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}
+ * is OFF, the behavior of AWB is device dependent. It is recommened to
+ * also set AWB mode to OFF or lock AWB by using {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock} before
+ * setting AE mode to OFF.</p>
* <p>When set to the OFF mode, the camera device's auto-white balance
* routine is disabled. The application manually controls the white
* balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}
@@ -1077,7 +1085,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @see CaptureRequest#COLOR_CORRECTION_MODE
* @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
+ * @see CaptureRequest#CONTROL_AE_MODE
* @see CameraCharacteristics#CONTROL_AWB_AVAILABLE_MODES
+ * @see CaptureRequest#CONTROL_AWB_LOCK
* @see CaptureRequest#CONTROL_MODE
* @see #CONTROL_AWB_MODE_OFF
* @see #CONTROL_AWB_MODE_AUTO
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d208649..c5c843d 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -970,7 +970,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>Whether auto-focus (AF) is currently enabled, and what
* mode it is set to.</p>
* <p>Only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} = AUTO and the lens is not fixed focus
- * (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} &gt; 0</code>).</p>
+ * (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} &gt; 0</code>). Also note that
+ * when {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is OFF, the behavior of AF is device
+ * dependent. It is recommended to lock AF by using {@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger} before
+ * setting {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} to OFF, or set AF mode to OFF when AE is OFF.</p>
* <p>If the lens is controlled by the camera device auto-focus algorithm,
* the camera device will report the current AF status in {@link CaptureResult#CONTROL_AF_STATE android.control.afState}
* in result metadata.</p>
@@ -987,8 +990,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* {@link CameraCharacteristics#CONTROL_AF_AVAILABLE_MODES android.control.afAvailableModes}</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_MODE
* @see CameraCharacteristics#CONTROL_AF_AVAILABLE_MODES
* @see CaptureResult#CONTROL_AF_STATE
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
* @see #CONTROL_AF_MODE_OFF
@@ -1519,7 +1524,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>When set to the ON mode, the camera device's auto-white balance
* routine is enabled, overriding the application's selected
* {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
- * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
+ * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}. Note that when {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}
+ * is OFF, the behavior of AWB is device dependent. It is recommened to
+ * also set AWB mode to OFF or lock AWB by using {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock} before
+ * setting AE mode to OFF.</p>
* <p>When set to the OFF mode, the camera device's auto-white balance
* routine is disabled. The application manually controls the white
* balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}
@@ -1550,7 +1558,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @see CaptureRequest#COLOR_CORRECTION_MODE
* @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
+ * @see CaptureRequest#CONTROL_AE_MODE
* @see CameraCharacteristics#CONTROL_AWB_AVAILABLE_MODES
+ * @see CaptureRequest#CONTROL_AWB_LOCK
* @see CaptureRequest#CONTROL_MODE
* @see #CONTROL_AWB_MODE_OFF
* @see #CONTROL_AWB_MODE_AUTO
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 98adcea..3b10eb5 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -918,7 +918,9 @@ public class ParameterUtils {
convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea);
Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth;
- if (leftEye != null && rightEye != null && mouth != null) {
+ if (leftEye != null && rightEye != null && mouth != null && leftEye.x != -2000 &&
+ leftEye.y != -2000 && rightEye.x != -2000 && rightEye.y != -2000 &&
+ mouth.x != -2000 && mouth.y != -2000) {
leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
leftEye, /*usePreviewCrop*/true);
rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index e52baed..1f0f45a 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -136,7 +136,7 @@ public final class HdmiPortInfo implements Parcelable {
boolean cec = (source.readInt() == 1);
boolean arc = (source.readInt() == 1);
boolean mhl = (source.readInt() == 1);
- return new HdmiPortInfo(id, type, address, cec, arc, mhl);
+ return new HdmiPortInfo(id, type, address, cec, mhl, arc);
}
@Override
@@ -172,4 +172,15 @@ public final class HdmiPortInfo implements Parcelable {
s.append("mhl: ").append(mMhlSupported);
return s.toString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof HdmiPortInfo)) {
+ return false;
+ }
+ final HdmiPortInfo other = (HdmiPortInfo) o;
+ return mId == other.mId && mType == other.mType && mAddress == other.mAddress
+ && mCecSupported == other.mCecSupported && mArcSupported == other.mArcSupported
+ && mMhlSupported == other.mMhlSupported;
+ }
}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index af75a0a..3175345 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -31,6 +31,7 @@ import android.inputmethodservice.Keyboard.Key;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -980,8 +981,9 @@ public class KeyboardView extends View implements View.OnClickListener {
onInitializeAccessibilityEvent(event);
String text = null;
// This is very efficient since the properties are cached.
- final boolean speakPassword = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0;
+ final boolean speakPassword = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0,
+ UserHandle.USER_CURRENT) != 0;
// Add text only if password announcement is enabled or if headset is
// used to avoid leaking passwords.
if (speakPassword || mAudioManager.isBluetoothA2dpOn()
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1c9f4c6..d4242ef 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -509,6 +509,8 @@ public class ConnectivityManager {
return "MOBILE_EMERGENCY";
case TYPE_PROXY:
return "PROXY";
+ case TYPE_VPN:
+ return "VPN";
default:
return Integer.toString(type);
}
@@ -1921,45 +1923,6 @@ public class ConnectivityManager {
}
/**
- * get the information about a specific network link
- * @hide
- */
- public LinkQualityInfo getLinkQualityInfo(int networkType) {
- try {
- LinkQualityInfo li = mService.getLinkQualityInfo(networkType);
- return li;
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * get the information of currently active network link
- * @hide
- */
- public LinkQualityInfo getActiveLinkQualityInfo() {
- try {
- LinkQualityInfo li = mService.getActiveLinkQualityInfo();
- return li;
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * get the information of all network links
- * @hide
- */
- public LinkQualityInfo[] getAllLinkQualityInfo() {
- try {
- LinkQualityInfo[] li = mService.getAllLinkQualityInfo();
- return li;
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
* Set sign in error notification to visible or in visible
*
* @param visible
@@ -2381,15 +2344,14 @@ public class ConnectivityManager {
* successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
*/
- public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
+ public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
/**
* The lookup key for a {@link NetworkRequest} object included with the intent after
* successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
*/
- public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST =
- "networkRequestNetworkRequest";
+ public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
/**
@@ -2405,8 +2367,8 @@ public class ConnectivityManager {
* &lt;receiver&gt; tag in an AndroidManifest.xml file
* <p>
* The operation Intent is delivered with two extras, a {@link Network} typed
- * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkRequest}
- * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_REQUEST} containing
+ * extra called {@link #EXTRA_NETWORK} and a {@link NetworkRequest}
+ * typed extra called {@link #EXTRA_NETWORK_REQUEST} containing
* the original requests parameters. It is important to create a new,
* {@link NetworkCallback} based request before completing the processing of the
* Intent to reserve the network or it will be released shortly after the Intent
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index d965f27..f45737a 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -18,11 +18,14 @@ package android.net;
import android.content.Context;
import android.net.IEthernetManager;
+import android.net.IEthernetServiceListener;
import android.net.IpConfiguration;
-import android.net.IpConfiguration.IpAssignment;
-import android.net.IpConfiguration.ProxySettings;
+import android.os.Handler;
+import android.os.Message;
import android.os.RemoteException;
+import java.util.ArrayList;
+
/**
* A class representing the IP configuration of the Ethernet network.
*
@@ -30,9 +33,41 @@ import android.os.RemoteException;
*/
public class EthernetManager {
private static final String TAG = "EthernetManager";
+ private static final int MSG_AVAILABILITY_CHANGED = 1000;
private final Context mContext;
private final IEthernetManager mService;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_AVAILABILITY_CHANGED) {
+ boolean isAvailable = (msg.arg1 == 1);
+ for (Listener listener : mListeners) {
+ listener.onAvailabilityChanged(isAvailable);
+ }
+ }
+ }
+ };
+ private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
+ private final IEthernetServiceListener.Stub mServiceListener =
+ new IEthernetServiceListener.Stub() {
+ @Override
+ public void onAvailabilityChanged(boolean isAvailable) {
+ mHandler.obtainMessage(
+ MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
+ }
+ };
+
+ /**
+ * A listener interface to receive notification on changes in Ethernet.
+ */
+ public interface Listener {
+ /**
+ * Called when Ethernet port's availability is changed.
+ * @param isAvailable {@code true} if one or more Ethernet port exists.
+ */
+ public void onAvailabilityChanged(boolean isAvailable);
+ }
/**
* Create a new EthernetManager instance.
@@ -50,12 +85,9 @@ public class EthernetManager {
* @return the Ethernet Configuration, contained in {@link IpConfiguration}.
*/
public IpConfiguration getConfiguration() {
- if (mService == null) {
- return new IpConfiguration();
- }
try {
return mService.getConfiguration();
- } catch (RemoteException e) {
+ } catch (NullPointerException | RemoteException e) {
return new IpConfiguration();
}
}
@@ -64,12 +96,57 @@ public class EthernetManager {
* Set Ethernet configuration.
*/
public void setConfiguration(IpConfiguration config) {
- if (mService == null) {
- return;
- }
try {
mService.setConfiguration(config);
- } catch (RemoteException e) {
+ } catch (NullPointerException | RemoteException e) {
+ }
+ }
+
+ /**
+ * Indicates whether the system currently has one or more
+ * Ethernet interfaces.
+ */
+ public boolean isAvailable() {
+ try {
+ return mService.isAvailable();
+ } catch (NullPointerException | RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Adds a listener.
+ * @param listener A {@link Listener} to add.
+ * @throws IllegalArgumentException If the listener is null.
+ */
+ public void addListener(Listener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ try {
+ mService.addListener(mServiceListener);
+ } catch (NullPointerException | RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Removes a listener.
+ * @param listener A {@link Listener} to remove.
+ * @throws IllegalArgumentException If the listener is null.
+ */
+ public void removeListener(Listener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ mListeners.remove(listener);
+ if (mListeners.isEmpty()) {
+ try {
+ mService.removeListener(mServiceListener);
+ } catch (NullPointerException | RemoteException e) {
+ }
}
}
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a7bbc53..79f920e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -17,7 +17,6 @@
package android.net;
import android.app.PendingIntent;
-import android.net.LinkQualityInfo;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -133,12 +132,6 @@ interface IConnectivityManager
String getMobileRedirectedProvisioningUrl();
- LinkQualityInfo getLinkQualityInfo(int networkType);
-
- LinkQualityInfo getActiveLinkQualityInfo();
-
- LinkQualityInfo[] getAllLinkQualityInfo();
-
void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);
void setAirplaneMode(boolean enable);
@@ -170,4 +163,5 @@ interface IConnectivityManager
boolean addVpnAddress(String address, int prefixLength);
boolean removeVpnAddress(String address, int prefixLength);
+ boolean setUnderlyingNetworksForVpn(in Network[] networks);
}
diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl
index 3fa08f8..7a92eb9 100644
--- a/core/java/android/net/IEthernetManager.aidl
+++ b/core/java/android/net/IEthernetManager.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.net.IpConfiguration;
+import android.net.IEthernetServiceListener;
/**
* Interface that answers queries about, and allows changing
@@ -27,4 +28,7 @@ interface IEthernetManager
{
IpConfiguration getConfiguration();
void setConfiguration(in IpConfiguration config);
+ boolean isAvailable();
+ void addListener(in IEthernetServiceListener listener);
+ void removeListener(in IEthernetServiceListener listener);
}
diff --git a/core/java/android/webkit/MustOverrideException.java b/core/java/android/net/IEthernetServiceListener.aidl
index 0643bf0..356690e 100644
--- a/core/java/android/webkit/MustOverrideException.java
+++ b/core/java/android/net/IEthernetServiceListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package android.webkit;
+package android.net;
-// TODO: Remove MustOverrideException and make all methods throwing it abstract instead;
-// needs API file update.
-class MustOverrideException extends RuntimeException {
- MustOverrideException() {
- super("abstract function called: must be overriden!");
- }
-} \ No newline at end of file
+/** @hide */
+oneway interface IEthernetServiceListener
+{
+ void onAvailabilityChanged(boolean isAvailable);
+}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 36dd2fdfb..d36707e 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -34,7 +34,7 @@ import java.util.Objects;
*
* @hide
*/
-public class NetworkIdentity {
+public class NetworkIdentity implements Comparable<NetworkIdentity> {
/**
* When enabled, combine all {@link #mSubType} together under
* {@link #SUBTYPE_COMBINED}.
@@ -76,7 +76,7 @@ public class NetworkIdentity {
@Override
public String toString() {
- final StringBuilder builder = new StringBuilder("[");
+ final StringBuilder builder = new StringBuilder("{");
builder.append("type=").append(getNetworkTypeName(mType));
builder.append(", subType=");
if (COMBINE_SUBTYPE_ENABLED) {
@@ -95,7 +95,7 @@ public class NetworkIdentity {
if (mRoaming) {
builder.append(", ROAMING");
}
- return builder.append("]").toString();
+ return builder.append("}").toString();
}
public int getType() {
@@ -170,4 +170,22 @@ public class NetworkIdentity {
return new NetworkIdentity(type, subType, subscriberId, networkId, roaming);
}
+
+ @Override
+ public int compareTo(NetworkIdentity another) {
+ int res = Integer.compare(mType, another.mType);
+ if (res == 0) {
+ res = Integer.compare(mSubType, another.mSubType);
+ }
+ if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
+ res = mSubscriberId.compareTo(another.mSubscriberId);
+ }
+ if (res == 0 && mNetworkId != null && another.mNetworkId != null) {
+ res = mNetworkId.compareTo(another.mNetworkId);
+ }
+ if (res == 0) {
+ res = Boolean.compare(mRoaming, another.mRoaming);
+ }
+ return res;
+ }
}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 2e0e9e4..d26c70d 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -29,20 +29,23 @@ public class NetworkState implements Parcelable {
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
public final NetworkCapabilities networkCapabilities;
+ public final Network network;
/** Currently only used by testing. */
public final String subscriberId;
public final String networkId;
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities) {
- this(networkInfo, linkProperties, networkCapabilities, null, null);
+ NetworkCapabilities networkCapabilities, Network network) {
+ this(networkInfo, linkProperties, networkCapabilities, network, null, null);
}
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, String subscriberId, String networkId) {
+ NetworkCapabilities networkCapabilities, Network network, String subscriberId,
+ String networkId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
this.networkCapabilities = networkCapabilities;
+ this.network = network;
this.subscriberId = subscriberId;
this.networkId = networkId;
}
@@ -51,6 +54,7 @@ public class NetworkState implements Parcelable {
networkInfo = in.readParcelable(null);
linkProperties = in.readParcelable(null);
networkCapabilities = in.readParcelable(null);
+ network = in.readParcelable(null);
subscriberId = in.readString();
networkId = in.readString();
}
@@ -65,6 +69,7 @@ public class NetworkState implements Parcelable {
out.writeParcelable(networkInfo, flags);
out.writeParcelable(linkProperties, flags);
out.writeParcelable(networkCapabilities, flags);
+ out.writeParcelable(network, flags);
out.writeString(subscriberId);
out.writeString(networkId);
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index ea5dfd1..2afe578 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -733,6 +733,22 @@ public class NetworkStats implements Parcelable {
}
/**
+ * Return text description of {@link #set} value.
+ */
+ public static String setToCheckinString(int set) {
+ switch (set) {
+ case SET_ALL:
+ return "all";
+ case SET_DEFAULT:
+ return "def";
+ case SET_FOREGROUND:
+ return "fg";
+ default:
+ return "unk";
+ }
+ }
+
+ /**
* Return text description of {@link #tag} value.
*/
public static String tagToString(int tag) {
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 62d8738..4a4accb 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -26,6 +26,7 @@ import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray;
import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.internal.util.ArrayUtils.total;
import android.os.Parcel;
@@ -38,6 +39,7 @@ import java.io.CharArrayWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.net.ProtocolException;
import java.util.Arrays;
import java.util.Random;
@@ -573,8 +575,22 @@ public class NetworkStatsHistory implements Parcelable {
return (long) (start + (r.nextFloat() * (end - start)));
}
+ /**
+ * Quickly determine if this history intersects with given window.
+ */
+ public boolean intersects(long start, long end) {
+ final long dataStart = getStart();
+ final long dataEnd = getEnd();
+ if (start >= dataStart && start <= dataEnd) return true;
+ if (end >= dataStart && end <= dataEnd) return true;
+ if (dataStart >= start && dataStart <= end) return true;
+ if (dataEnd >= start && dataEnd <= end) return true;
+ return false;
+ }
+
public void dump(IndentingPrintWriter pw, boolean fullHistory) {
- pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
+ pw.print("NetworkStatsHistory: bucketDuration=");
+ pw.println(bucketDuration / SECOND_IN_MILLIS);
pw.increaseIndent();
final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
@@ -583,19 +599,35 @@ public class NetworkStatsHistory implements Parcelable {
}
for (int i = start; i < bucketCount; i++) {
- pw.print("bucketStart="); pw.print(bucketStart[i]);
- if (activeTime != null) { pw.print(" activeTime="); pw.print(activeTime[i]); }
- if (rxBytes != null) { pw.print(" rxBytes="); pw.print(rxBytes[i]); }
- if (rxPackets != null) { pw.print(" rxPackets="); pw.print(rxPackets[i]); }
- if (txBytes != null) { pw.print(" txBytes="); pw.print(txBytes[i]); }
- if (txPackets != null) { pw.print(" txPackets="); pw.print(txPackets[i]); }
- if (operations != null) { pw.print(" operations="); pw.print(operations[i]); }
+ pw.print("st="); pw.print(bucketStart[i] / SECOND_IN_MILLIS);
+ if (rxBytes != null) { pw.print(" rb="); pw.print(rxBytes[i]); }
+ if (rxPackets != null) { pw.print(" rp="); pw.print(rxPackets[i]); }
+ if (txBytes != null) { pw.print(" tb="); pw.print(txBytes[i]); }
+ if (txPackets != null) { pw.print(" tp="); pw.print(txPackets[i]); }
+ if (operations != null) { pw.print(" op="); pw.print(operations[i]); }
pw.println();
}
pw.decreaseIndent();
}
+ public void dumpCheckin(PrintWriter pw) {
+ pw.print("d,");
+ pw.print(bucketDuration / SECOND_IN_MILLIS);
+ pw.println();
+
+ for (int i = 0; i < bucketCount; i++) {
+ pw.print("b,");
+ pw.print(bucketStart[i] / SECOND_IN_MILLIS); pw.print(',');
+ if (rxBytes != null) { pw.print(rxBytes[i]); } else { pw.print("*"); } pw.print(',');
+ if (rxPackets != null) { pw.print(rxPackets[i]); } else { pw.print("*"); } pw.print(',');
+ if (txBytes != null) { pw.print(txBytes[i]); } else { pw.print("*"); } pw.print(',');
+ if (txPackets != null) { pw.print(txPackets[i]); } else { pw.print("*"); } pw.print(',');
+ if (operations != null) { pw.print(operations[i]); } else { pw.print("*"); }
+ pw.println();
+ }
+ }
+
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 27197cc..b839e0a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
@@ -34,10 +35,10 @@ import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.Objects;
-
import com.android.internal.annotations.VisibleForTesting;
+import java.util.Objects;
+
/**
* Template definition used to generically match {@link NetworkIdentity},
* usually when collecting statistics.
@@ -53,6 +54,7 @@ public class NetworkTemplate implements Parcelable {
public static final int MATCH_ETHERNET = 5;
public static final int MATCH_MOBILE_WILDCARD = 6;
public static final int MATCH_WIFI_WILDCARD = 7;
+ public static final int MATCH_BLUETOOTH = 8;
/**
* Set of {@link NetworkInfo#getType()} that reflect data usage.
@@ -134,6 +136,14 @@ public class NetworkTemplate implements Parcelable {
return new NetworkTemplate(MATCH_ETHERNET, null, null);
}
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_BLUETOOTH} style
+ * networks together.
+ */
+ public static NetworkTemplate buildTemplateBluetooth() {
+ return new NetworkTemplate(MATCH_BLUETOOTH, null, null);
+ }
+
private final int mMatchRule;
private final String mSubscriberId;
private final String mNetworkId;
@@ -222,6 +232,8 @@ public class NetworkTemplate implements Parcelable {
return matchesMobileWildcard(ident);
case MATCH_WIFI_WILDCARD:
return matchesWifiWildcard(ident);
+ case MATCH_BLUETOOTH:
+ return matchesBluetooth(ident);
default:
throw new IllegalArgumentException("unknown network template");
}
@@ -316,6 +328,16 @@ public class NetworkTemplate implements Parcelable {
}
}
+ /**
+ * Check if matches Bluetooth network template.
+ */
+ private boolean matchesBluetooth(NetworkIdentity ident) {
+ if (ident.mType == TYPE_BLUETOOTH) {
+ return true;
+ }
+ return false;
+ }
+
private static String getMatchRuleName(int matchRule) {
switch (matchRule) {
case MATCH_MOBILE_3G_LOWER:
@@ -332,6 +354,8 @@ public class NetworkTemplate implements Parcelable {
return "MOBILE_WILDCARD";
case MATCH_WIFI_WILDCARD:
return "WIFI_WILDCARD";
+ case MATCH_BLUETOOTH:
+ return "BLUETOOTH";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index c848993..78b9c18 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -19,6 +19,7 @@ package android.net;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
+import android.annotation.SystemApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.Service;
@@ -26,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.net.Network;
import android.net.NetworkUtils;
import android.os.Binder;
import android.os.IBinder;
@@ -164,6 +166,32 @@ public class VpnService extends Service {
}
/**
+ * Version of {@link #prepare(Context)} which does not require user consent.
+ *
+ * <p>Requires {@link android.Manifest.permission#CONTROL_VPN} and should generally not be
+ * used. Only acceptable in situations where user consent has been obtained through other means.
+ *
+ * <p>Once this is run, future preparations may be done with the standard prepare method as this
+ * will authorize the package to prepare the VPN without consent in the future.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static void prepareAndAuthorize(Context context) {
+ IConnectivityManager cm = getService();
+ String packageName = context.getPackageName();
+ try {
+ // Only prepare if we're not already prepared.
+ if (!cm.prepareVpn(packageName, null)) {
+ cm.prepareVpn(null, packageName);
+ }
+ cm.setVpnPackageAuthorization(true);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+
+ /**
* Protect a socket from VPN connections. After protecting, data sent
* through this socket will go directly to the underlying network,
* so its traffic will not be forwarded through the VPN.
@@ -261,6 +289,46 @@ public class VpnService extends Service {
}
/**
+ * Sets the underlying networks used by the VPN for its upstream connections.
+ *
+ * Used by the system to know the actual networks that carry traffic for apps affected by this
+ * VPN in order to present this information to the user (e.g., via status bar icons).
+ *
+ * This method only needs to be called if the VPN has explicitly bound its underlying
+ * communications channels &mdash; such as the socket(s) passed to {@link #protect(int)} &mdash;
+ * to a {@code Network} using APIs such as {@link Network#bindSocket(Socket)} or
+ * {@link Network#bindSocket(DatagramSocket)}. The VPN should call this method every time
+ * the set of {@code Network}s it is using changes.
+ *
+ * {@code networks} is one of the following:
+ * <ul>
+ * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
+ * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
+ * networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear
+ * first in the array.</li>
+ * <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no
+ * underlying network connection, and thus, app traffic will not be sent or received.</li>
+ * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
+ * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
+ * APIs mentioned above to send traffic over specific channels.
+ * </ul>
+ *
+ * This call will succeed only if the VPN is currently established. For setting this value when
+ * the VPN has not yet been established, see {@link Builder#setUnderlyingNetworks}.
+ *
+ * @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
+ *
+ * @return {@code true} on success.
+ */
+ public boolean setUnderlyingNetworks(Network[] networks) {
+ try {
+ return getService().setUnderlyingNetworksForVpn(networks);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
* Return the communication interface to the service. This method returns
* {@code null} on {@link Intent}s other than {@link #SERVICE_INTERFACE}
* action. Applications overriding this method must identify the intent
@@ -636,6 +704,20 @@ public class VpnService extends Service {
}
/**
+ * Sets the underlying networks used by the VPN for its upstream connections.
+ *
+ * @see VpnService#setUnderlyingNetworks
+ *
+ * @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
+ *
+ * @return this {@link Builder} object to facilitate chaining method calls.
+ */
+ public Builder setUnderlyingNetworks(Network[] networks) {
+ mConfig.underlyingNetworks = networks != null ? networks.clone() : null;
+ return this;
+ }
+
+ /**
* Create a VPN interface using the parameters supplied to this
* builder. The interface works on IP packets, and a file descriptor
* is returned for the application to access them. Each read
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index 8126b75..24d4eb8 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -18,6 +18,8 @@ package android.net;
import static android.util.Patterns.GOOD_IRI_CHAR;
+import android.annotation.SystemApi;
+
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -38,6 +40,9 @@ import java.util.regex.Pattern;
* If given an https scheme but no port, fills in port
*
*/
+// TODO(igsolla): remove WebAddress from the system SDK once the WebView apk does not
+// longer need to be binary compatible with the API 21 version of the framework.
+@SystemApi
public class WebAddress {
private String mScheme;
@@ -136,42 +141,52 @@ public class WebAddress {
return mScheme + "://" + authInfo + mHost + port + mPath;
}
+ /** {@hide} */
public void setScheme(String scheme) {
mScheme = scheme;
}
+ /** {@hide} */
public String getScheme() {
return mScheme;
}
+ /** {@hide} */
public void setHost(String host) {
mHost = host;
}
+ /** {@hide} */
public String getHost() {
return mHost;
}
+ /** {@hide} */
public void setPort(int port) {
mPort = port;
}
+ /** {@hide} */
public int getPort() {
return mPort;
}
+ /** {@hide} */
public void setPath(String path) {
mPath = path;
}
+ /** {@hide} */
public String getPath() {
return mPath;
}
+ /** {@hide} */
public void setAuthInfo(String authInfo) {
mAuthInfo = authInfo;
}
+ /** {@hide} */
public String getAuthInfo() {
return mAuthInfo;
}
diff --git a/core/java/android/net/http/CharArrayBuffers.java b/core/java/android/net/http/CharArrayBuffers.java
deleted file mode 100644
index 77d45f6..0000000
--- a/core/java/android/net/http/CharArrayBuffers.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.http;
-
-import org.apache.http.util.CharArrayBuffer;
-import org.apache.http.protocol.HTTP;
-
-/**
- * Utility methods for working on CharArrayBuffers.
- *
- * {@hide}
- */
-class CharArrayBuffers {
-
- static final char uppercaseAddon = 'a' - 'A';
-
- /**
- * Returns true if the buffer contains the given string. Ignores leading
- * whitespace and case.
- *
- * @param buffer to search
- * @param beginIndex index at which we should start
- * @param str to search for
- */
- static boolean containsIgnoreCaseTrimmed(CharArrayBuffer buffer,
- int beginIndex, final String str) {
- int len = buffer.length();
- char[] chars = buffer.buffer();
- while (beginIndex < len && HTTP.isWhitespace(chars[beginIndex])) {
- beginIndex++;
- }
- int size = str.length();
- boolean ok = len >= beginIndex + size;
- for (int j=0; ok && (j<size); j++) {
- char a = chars[beginIndex+j];
- char b = str.charAt(j);
- if (a != b) {
- a = toLower(a);
- b = toLower(b);
- ok = a == b;
- }
- }
- return ok;
- }
-
- /**
- * Returns index of first occurence ch. Lower cases characters leading up
- * to first occurrence of ch.
- */
- static int setLowercaseIndexOf(CharArrayBuffer buffer, final int ch) {
-
- int beginIndex = 0;
- int endIndex = buffer.length();
- char[] chars = buffer.buffer();
-
- for (int i = beginIndex; i < endIndex; i++) {
- char current = chars[i];
- if (current == ch) {
- return i;
- } else if (current >= 'A' && current <= 'Z'){
- // make lower case
- current += uppercaseAddon;
- chars[i] = current;
- }
- }
- return -1;
- }
-
- private static char toLower(char c) {
- if (c >= 'A' && c <= 'Z'){
- c += uppercaseAddon;
- }
- return c;
- }
-}
diff --git a/core/java/android/net/http/Headers.java b/core/java/android/net/http/Headers.java
index 657e071..0f8b105 100644
--- a/core/java/android/net/http/Headers.java
+++ b/core/java/android/net/http/Headers.java
@@ -158,7 +158,7 @@ public final class Headers {
}
public void parseHeader(CharArrayBuffer buffer) {
- int pos = CharArrayBuffers.setLowercaseIndexOf(buffer, ':');
+ int pos = setLowercaseIndexOf(buffer, ':');
if (pos == -1) {
return;
}
@@ -459,12 +459,63 @@ public final class Headers {
}
private void setConnectionType(CharArrayBuffer buffer, int pos) {
- if (CharArrayBuffers.containsIgnoreCaseTrimmed(
- buffer, pos, HTTP.CONN_CLOSE)) {
+ if (containsIgnoreCaseTrimmed(buffer, pos, HTTP.CONN_CLOSE)) {
connectionType = CONN_CLOSE;
- } else if (CharArrayBuffers.containsIgnoreCaseTrimmed(
+ } else if (containsIgnoreCaseTrimmed(
buffer, pos, HTTP.CONN_KEEP_ALIVE)) {
connectionType = CONN_KEEP_ALIVE;
}
}
+
+
+ /**
+ * Returns true if the buffer contains the given string. Ignores leading
+ * whitespace and case.
+ *
+ * @param buffer to search
+ * @param beginIndex index at which we should start
+ * @param str to search for
+ */
+ static boolean containsIgnoreCaseTrimmed(CharArrayBuffer buffer,
+ int beginIndex, final String str) {
+ int len = buffer.length();
+ char[] chars = buffer.buffer();
+ while (beginIndex < len && HTTP.isWhitespace(chars[beginIndex])) {
+ beginIndex++;
+ }
+ int size = str.length();
+ boolean ok = len >= (beginIndex + size);
+ for (int j=0; ok && (j < size); j++) {
+ char a = chars[beginIndex + j];
+ char b = str.charAt(j);
+ if (a != b) {
+ a = Character.toLowerCase(a);
+ b = Character.toLowerCase(b);
+ ok = a == b;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns index of first occurence ch. Lower cases characters leading up
+ * to first occurrence of ch.
+ */
+ static int setLowercaseIndexOf(CharArrayBuffer buffer, final int ch) {
+
+ int beginIndex = 0;
+ int endIndex = buffer.length();
+ char[] chars = buffer.buffer();
+
+ for (int i = beginIndex; i < endIndex; i++) {
+ char current = chars[i];
+ if (current == ch) {
+ return i;
+ } else {
+ chars[i] = Character.toLowerCase(current);
+ }
+ }
+ return -1;
+ }
}
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index 6a63a0c..bb36c20 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -36,7 +36,7 @@ import javax.net.ssl.X509TrustManager;
*/
public class X509TrustManagerExtensions {
- TrustManagerImpl mDelegate;
+ final TrustManagerImpl mDelegate;
/**
* Constructs a new X509TrustManagerExtensions wrapper.
@@ -48,6 +48,7 @@ public class X509TrustManagerExtensions {
if (tm instanceof TrustManagerImpl) {
mDelegate = (TrustManagerImpl) tm;
} else {
+ mDelegate = null;
throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() +
" which is not a supported type of X509TrustManager");
}
diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java
index c30ba14..918ec3d 100644
--- a/core/java/android/nfc/BeamShareData.java
+++ b/core/java/android/nfc/BeamShareData.java
@@ -3,6 +3,7 @@ package android.nfc;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
/**
* Class to IPC data to be shared over Android Beam.
@@ -14,11 +15,13 @@ import android.os.Parcelable;
public final class BeamShareData implements Parcelable {
public final NdefMessage ndefMessage;
public final Uri[] uris;
+ public final UserHandle userHandle;
public final int flags;
- public BeamShareData(NdefMessage msg, Uri[] uris, int flags) {
+ public BeamShareData(NdefMessage msg, Uri[] uris, UserHandle userHandle, int flags) {
this.ndefMessage = msg;
this.uris = uris;
+ this.userHandle = userHandle;
this.flags = flags;
}
@@ -35,6 +38,7 @@ public final class BeamShareData implements Parcelable {
if (urisLength > 0) {
dest.writeTypedArray(uris, 0);
}
+ dest.writeParcelable(userHandle, 0);
dest.writeInt(this.flags);
}
@@ -49,9 +53,10 @@ public final class BeamShareData implements Parcelable {
uris = new Uri[numUris];
source.readTypedArray(uris, Uri.CREATOR);
}
+ UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader());
int flags = source.readInt();
- return new BeamShareData(msg, uris, flags);
+ return new BeamShareData(msg, uris, userHandle, flags);
}
@Override
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 5b926ad..961a3f4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -60,4 +60,6 @@ interface INfcAdapter
void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
+
+ void verifyNfcPermission();
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 8643f2e..d009295 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -18,12 +18,14 @@ package android.nfc;
import android.app.Activity;
import android.app.Application;
+import android.content.ContentProvider;
import android.content.Intent;
import android.net.Uri;
import android.nfc.NfcAdapter.ReaderCallback;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -252,7 +254,11 @@ public final class NfcActivityManager extends IAppCallback.Stub
isResumed = state.resumed;
}
if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
}
}
@@ -266,7 +272,11 @@ public final class NfcActivityManager extends IAppCallback.Stub
isResumed = state.resumed;
}
if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
}
}
@@ -279,7 +289,11 @@ public final class NfcActivityManager extends IAppCallback.Stub
isResumed = state.resumed;
}
if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
}
}
@@ -293,7 +307,11 @@ public final class NfcActivityManager extends IAppCallback.Stub
isResumed = state.resumed;
}
if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
}
}
@@ -306,7 +324,11 @@ public final class NfcActivityManager extends IAppCallback.Stub
isResumed = state.resumed;
}
if (isResumed) {
+ // requestNfcServiceCallback() verifies permission also
requestNfcServiceCallback();
+ } else {
+ // Crash API calls early in case NFC permission is missing
+ verifyNfcPermission();
}
}
@@ -322,6 +344,14 @@ public final class NfcActivityManager extends IAppCallback.Stub
}
}
+ void verifyNfcPermission() {
+ try {
+ NfcAdapter.sService.verifyNfcPermission();
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+
/** Callback from NFC service, usually on binder thread */
@Override
public BeamShareData createBeamShareData() {
@@ -350,19 +380,24 @@ public final class NfcActivityManager extends IAppCallback.Stub
if (urisCallback != null) {
uris = urisCallback.createBeamUris(mDefaultEvent);
if (uris != null) {
+ ArrayList<Uri> validUris = new ArrayList<Uri>();
for (Uri uri : uris) {
if (uri == null) {
Log.e(TAG, "Uri not allowed to be null.");
- return null;
+ continue;
}
String scheme = uri.getScheme();
if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
!scheme.equalsIgnoreCase("content"))) {
Log.e(TAG, "Uri needs to have " +
"either scheme file or scheme content");
- return null;
+ continue;
}
+ uri = ContentProvider.maybeAddUserId(uri, UserHandle.myUserId());
+ validUris.add(uri);
}
+
+ uris = validUris.toArray(new Uri[validUris.size()]);
}
}
if (uris != null && uris.length > 0) {
@@ -372,7 +407,7 @@ public final class NfcActivityManager extends IAppCallback.Stub
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
- return new BeamShareData(message, uris, flags);
+ return new BeamShareData(message, uris, UserHandle.CURRENT, flags);
}
/** Callback from NFC service, usually on binder thread */
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 4f91d19..7785f2b 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -209,9 +209,9 @@ public abstract class AsyncTask<Params, Progress, Result> {
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
- private static final InternalHandler sHandler = new InternalHandler();
-
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
+ private static InternalHandler sHandler;
+
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
@@ -265,9 +265,13 @@ public abstract class AsyncTask<Params, Progress, Result> {
FINISHED,
}
- /** @hide Used to force static handler to be created. */
- public static void init() {
- sHandler.getLooper();
+ private static Handler getHandler() {
+ synchronized (AsyncTask.class) {
+ if (sHandler == null) {
+ sHandler = new InternalHandler();
+ }
+ return sHandler;
+ }
}
/** @hide */
@@ -315,7 +319,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
- Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
+ Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
@@ -620,7 +624,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
*/
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
- sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+ getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
@@ -635,10 +639,14 @@ public abstract class AsyncTask<Params, Progress, Result> {
}
private static class InternalHandler extends Handler {
+ public InternalHandler() {
+ super(Looper.getMainLooper());
+ }
+
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
- AsyncTaskResult result = (AsyncTaskResult) msg.obj;
+ AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 461469c..4709443 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3669,6 +3669,45 @@ public abstract class BatteryStats implements Parcelable {
pw.print(suffix);
}
+ private static boolean dumpTimeEstimate(PrintWriter pw, String label, long[] steps,
+ int count, long modesOfInterest, long modeValues) {
+ if (count <= 0) {
+ return false;
+ }
+ long total = 0;
+ int numOfInterest = 0;
+ for (int i=0; i<count; i++) {
+ long initMode = (steps[i] & STEP_LEVEL_INITIAL_MODE_MASK)
+ >> STEP_LEVEL_INITIAL_MODE_SHIFT;
+ long modMode = (steps[i] & STEP_LEVEL_MODIFIED_MODE_MASK)
+ >> STEP_LEVEL_MODIFIED_MODE_SHIFT;
+ // If the modes of interest didn't change during this step period...
+ if ((modMode&modesOfInterest) == 0) {
+ // And the mode values during this period match those we are measuring...
+ if ((initMode&modesOfInterest) == modeValues) {
+ // Then this can be used to estimate the total time!
+ numOfInterest++;
+ total += steps[i] & STEP_LEVEL_TIME_MASK;
+ }
+ }
+ }
+ if (numOfInterest <= 0) {
+ return false;
+ }
+
+ // The estimated time is the average time we spend in each level, multipled
+ // by 100 -- the total number of battery levels
+ long estimatedTime = (total / numOfInterest) * 100;
+
+ pw.print(label);
+ StringBuilder sb = new StringBuilder(64);
+ formatTimeMs(sb, estimatedTime);
+ pw.print(sb);
+ pw.println();
+
+ return true;
+ }
+
private static boolean dumpDurationSteps(PrintWriter pw, String header, long[] steps,
int count, boolean checkin) {
if (count <= 0) {
@@ -3923,6 +3962,38 @@ public abstract class BatteryStats implements Parcelable {
TimeUtils.formatDuration(timeRemaining / 1000, pw);
pw.println();
}
+ dumpTimeEstimate(pw, " Estimated screen off time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_OFF-1));
+ dumpTimeEstimate(pw, " Estimated screen off power save time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE);
+ dumpTimeEstimate(pw, " Estimated screen on time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_ON-1));
+ dumpTimeEstimate(pw, " Estimated screen on power save time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE);
+ dumpTimeEstimate(pw, " Estimated screen doze time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_DOZE-1));
+ dumpTimeEstimate(pw, " Estimated screen doze power save time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE);
+ dumpTimeEstimate(pw, " Estimated screen doze suspend time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_DOZE_SUSPEND-1));
+ dumpTimeEstimate(pw, " Estimated screen doze suspend power save time: ",
+ getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE);
pw.println();
}
if (dumpDurationSteps(pw, "Charge step durations:", getChargeStepDurationsArray(),
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 72d61e8..b209690 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -20,8 +20,11 @@ import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.telephony.TelephonyProperties;
+
import dalvik.system.VMRuntime;
+import java.util.Objects;
+
/**
* Information about the current build, extracted from system properties.
*/
@@ -641,6 +644,32 @@ public class Build {
}
}
+ /**
+ * Check that device fingerprint is defined and that it matches across
+ * various partitions.
+ *
+ * @hide
+ */
+ public static boolean isFingerprintConsistent() {
+ final String system = SystemProperties.get("ro.build.fingerprint");
+ final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
+
+ if (TextUtils.isEmpty(system)) {
+ Slog.e(TAG, "Required ro.build.fingerprint is empty!");
+ return false;
+ }
+
+ if (!TextUtils.isEmpty(vendor)) {
+ if (!Objects.equals(system, vendor)) {
+ Slog.e(TAG, "Mismatched fingerprints; system reported " + system
+ + " but vendor reported " + vendor);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
// The following properties only make sense for internal engineering builds.
public static final long TIME = getLong("ro.build.date.utc") * 1000;
public static final String USER = getString("ro.build.user");
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 52db060..878b7a0 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -156,7 +156,7 @@ public class Handler {
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
- * with represent to synchronous messages. Asynchronous messages are not subject to
+ * with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
@@ -176,7 +176,7 @@ public class Handler {
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
- * with represent to synchronous messages. Asynchronous messages are not subject to
+ * with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
@@ -214,7 +214,7 @@ public class Handler {
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
- * with represent to synchronous messages. Asynchronous messages are not subject to
+ * with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param looper The looper, must not be null.
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 6a0bddc..8c75847 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -417,38 +417,42 @@ public final class Message implements Parcelable {
}
/**
- * Returns true if the message is asynchronous.
- *
- * Asynchronous messages represent interrupts or events that do not require global ordering
- * with represent to synchronous messages. Asynchronous messages are not subject to
- * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+ * Returns true if the message is asynchronous, meaning that it is not
+ * subject to {@link Looper} synchronization barriers.
*
* @return True if the message is asynchronous.
*
* @see #setAsynchronous(boolean)
- * @see MessageQueue#enqueueSyncBarrier(long)
- * @see MessageQueue#removeSyncBarrier(int)
- *
- * @hide
*/
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
/**
- * Sets whether the message is asynchronous.
- *
- * Asynchronous messages represent interrupts or events that do not require global ordering
- * with represent to synchronous messages. Asynchronous messages are not subject to
- * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+ * Sets whether the message is asynchronous, meaning that it is not
+ * subject to {@link Looper} synchronization barriers.
+ * <p>
+ * Certain operations, such as view invalidation, may introduce synchronization
+ * barriers into the {@link Looper}'s message queue to prevent subsequent messages
+ * from being delivered until some condition is met. In the case of view invalidation,
+ * messages which are posted after a call to {@link android.view.View#invalidate}
+ * are suspended by means of a synchronization barrier until the next frame is
+ * ready to be drawn. The synchronization barrier ensures that the invalidation
+ * request is completely handled before resuming.
+ * </p><p>
+ * Asynchronous messages are exempt from synchronization barriers. They typically
+ * represent interrupts, input events, and other signals that must be handled independently
+ * even while other work has been suspended.
+ * </p><p>
+ * Note that asynchronous messages may be delivered out of order with respect to
+ * synchronous messages although they are always delivered in order among themselves.
+ * If the relative order of these messages matters then they probably should not be
+ * asynchronous in the first place. Use with caution.
+ * </p>
*
* @param async True if the message is asynchronous.
*
* @see #isAsynchronous()
- * @see MessageQueue#enqueueSyncBarrier(long)
- * @see MessageQueue#removeSyncBarrier(int)
- *
- * @hide
*/
public void setAsynchronous(boolean async) {
if (async) {
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 9d78360..6f31768 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -25,6 +25,58 @@ import android.view.Display;
*/
public abstract class PowerManagerInternal {
/**
+ * Wakefulness: The device is asleep. It can only be awoken by a call to wakeUp().
+ * The screen should be off or in the process of being turned off by the display controller.
+ * The device typically passes through the dozing state first.
+ */
+ public static final int WAKEFULNESS_ASLEEP = 0;
+
+ /**
+ * Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep().
+ * When the user activity timeout expires, the device may start dreaming or go to sleep.
+ */
+ public static final int WAKEFULNESS_AWAKE = 1;
+
+ /**
+ * Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(),
+ * which ends the dream. The device goes to sleep when goToSleep() is called, when
+ * the dream ends or when unplugged.
+ * User activity may brighten the screen but does not end the dream.
+ */
+ public static final int WAKEFULNESS_DREAMING = 2;
+
+ /**
+ * Wakefulness: The device is dozing. It is almost asleep but is allowing a special
+ * low-power "doze" dream to run which keeps the display on but lets the application
+ * processor be suspended. It can be awoken by a call to wakeUp() which ends the dream.
+ * The device fully goes to sleep if the dream cannot be started or ends on its own.
+ */
+ public static final int WAKEFULNESS_DOZING = 3;
+
+ public static String wakefulnessToString(int wakefulness) {
+ switch (wakefulness) {
+ case WAKEFULNESS_ASLEEP:
+ return "Asleep";
+ case WAKEFULNESS_AWAKE:
+ return "Awake";
+ case WAKEFULNESS_DREAMING:
+ return "Dreaming";
+ case WAKEFULNESS_DOZING:
+ return "Dozing";
+ default:
+ return Integer.toString(wakefulness);
+ }
+ }
+
+ /**
+ * Returns true if the wakefulness state represents an interactive state
+ * as defined by {@link android.os.PowerManager#isInteractive}.
+ */
+ public static boolean isInteractive(int wakefulness) {
+ return wakefulness == WAKEFULNESS_AWAKE || wakefulness == WAKEFULNESS_DREAMING;
+ }
+
+ /**
* Used by the window manager to override the screen brightness based on the
* current foreground activity.
*
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 0ff5f6a..74e064e 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -45,12 +45,12 @@ public final class UserHandle implements Parcelable {
/** @hide A user id to indicate that we would like to send to the current
* user, but if this is calling from a user process then we will send it
- * to the caller's user instead of failing wiht a security exception */
+ * to the caller's user instead of failing with a security exception */
public static final int USER_CURRENT_OR_SELF = -3;
/** @hide A user handle to indicate that we would like to send to the current
* user, but if this is calling from a user process then we will send it
- * to the caller's user instead of failing wiht a security exception */
+ * to the caller's user instead of failing with a security exception */
public static final UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF);
/** @hide An undefined user id */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index bd6eeea..ffbed94 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -383,8 +383,8 @@ public class UserManager {
*
* <p/>Key for application restrictions.
* <p/>Type: Boolean
- * @see android.app.admin.DevicePolicyManager#addApplicationRestriction()
- * @see android.app.admin.DevicePolicyManager#getApplicationRestriction()
+ * @see android.app.admin.DevicePolicyManager#setApplicationRestrictions()
+ * @see android.app.admin.DevicePolicyManager#getApplicationRestrictions()
*/
public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 3ec45e9..f023df7 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -387,7 +387,6 @@ public class CallLog {
public static Uri addCall(CallerInfo ci, Context context, String number,
int presentation, int callType, int features, PhoneAccountHandle accountHandle,
long start, int duration, Long dataUsage) {
- // FIXME using -1 as subId instead of SubscriptionManager.INVALID_SUB_ID
return addCall(ci, context, number, presentation, callType, features, accountHandle,
start, duration, dataUsage, false);
}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 932e873..1316471 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -637,7 +637,7 @@ public abstract class DocumentsProvider extends ContentProvider {
final Bundle out = new Bundle();
try {
if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri);
+ enforceWritePermissionInner(documentUri, null);
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -651,7 +651,7 @@ public abstract class DocumentsProvider extends ContentProvider {
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri);
+ enforceWritePermissionInner(documentUri, null);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -675,7 +675,7 @@ public abstract class DocumentsProvider extends ContentProvider {
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri);
+ enforceWritePermissionInner(documentUri, null);
deleteDocument(documentId);
// Document no longer exists, clean up any grants
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8585682..bf91b3e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -984,8 +984,8 @@ public final class Settings {
* InputDeviceIdentifier. This field is used by some activities to jump straight into the
* settings for the given device.
* <p>
- * Example: The {@link #INPUT_METHOD_SETTINGS} intent opens the keyboard layout dialog for the
- * given device.
+ * Example: The {@link #ACTION_INPUT_METHOD_SETTINGS} intent opens the keyboard layout
+ * dialog for the given device.
* @hide
*/
public static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier";
@@ -4816,7 +4816,7 @@ public final class Settings {
* The timeout in milliseconds before the device fully goes to sleep after
* a period of inactivity. This value sets an upper bound on how long the device
* will stay awake or dreaming without user activity. It should generally
- * be longer than {@link #SCREEN_OFF_TIMEOUT} as otherwise the device
+ * be longer than {@link Settings.System#SCREEN_OFF_TIMEOUT} as otherwise the device
* will sleep before it ever has a chance to dream.
* <p>
* Use -1 to disable this timeout.
@@ -6601,6 +6601,15 @@ public final class Settings {
public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
/**
+ * Whether user can enable/disable LTE as a preferred network. A carrier might control
+ * this via gservices, OMA-DM, carrier app, etc.
+ * <p>
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ public static final String LTE_SERVICE_FORCED = "lte_service_forced";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/service/carriermessaging/CarrierMessagingService.aidl b/core/java/android/service/carriermessaging/CarrierMessagingService.aidl
new file mode 100644
index 0000000..50c438a
--- /dev/null
+++ b/core/java/android/service/carriermessaging/CarrierMessagingService.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carriermessaging;
+
+parcelable CarrierMessagingService.SendSmsResponse; \ No newline at end of file
diff --git a/core/java/android/service/carriermessaging/CarrierMessagingService.java b/core/java/android/service/carriermessaging/CarrierMessagingService.java
new file mode 100644
index 0000000..7aea590
--- /dev/null
+++ b/core/java/android/service/carriermessaging/CarrierMessagingService.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carriermessaging;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * A service that receives calls from the system when new SMS and MMS are
+ * sent or received.
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
+ * <pre>
+ * &lt;service android:name=".MyMessagingService"
+ * android:label="&#64;string/service_name"
+ * android:permission="android.permission.BIND_CARRIER_MESSAGING_SERVICE">
+ * &lt;intent-filter>
+ * &lt;action android:name="android.service.carriermessaging.CarrierMessagingService" />
+ * &lt;/intent-filter>
+ * &lt;/service></pre>
+ */
+public abstract class CarrierMessagingService extends Service {
+ /**
+ * The {@link android.content.Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE
+ = "android.service.carriermessaging.CarrierMessagingService";
+
+ /**
+ * Indicates that an SMS or MMS message was successfully sent.
+ */
+ public static final int SEND_STATUS_OK = 0;
+
+ /**
+ * SMS/MMS sending failed. We should retry via the carrier network.
+ */
+ public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
+
+ /**
+ * SMS/MMS sending failed. We should not retry via the carrier network.
+ */
+ public static final int SEND_STATUS_ERROR = 2;
+
+ /**
+ * Successfully downloaded an MMS message.
+ */
+ public static final int DOWNLOAD_STATUS_OK = 0;
+
+ /**
+ * MMS downloading failed. We should retry via the carrier network.
+ */
+ public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
+
+ /**
+ * MMS downloading failed. We should not retry via the carrier network.
+ */
+ public static final int DOWNLOAD_STATUS_ERROR = 2;
+
+ private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper();
+
+ /**
+ * Implement this method to filter SMS messages.
+ *
+ * @param pdu the PDUs of the message
+ * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
+ * @param destPort the destination port of a binary SMS, this will be -1 for text SMS
+ *
+ * @return True to keep an inbound SMS message and delivered to SMS apps. False to
+ * drop the message.
+ */
+ public boolean onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort) {
+ // optional
+ return true;
+ }
+
+ /**
+ * Implement this method to intercept text SMSs sent from the devcie.
+ *
+ * @param text the text to send
+ * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
+ * @param destAddress phone number of the recipient of the message
+ *
+ * @return a possibly {code null} {@link SendSmsResponse}. Upon returning {@code null}, the SMS
+ * is sent using the carrier network.
+ */
+ public @Nullable SendSmsResponse onSendTextSms(
+ @NonNull String text, @NonNull String format, @NonNull String destAddress) {
+ // optional
+ return null;
+ }
+
+ /**
+ * Implement this method to intercept binary SMSs sent from the device.
+ *
+ * @param data the binary content
+ * @param format format the format of the response PDU, typically "3gpp" or "3gpp2"
+ * @param destAddress phone number of the recipient of the message
+ * @param destPort the destination port
+ *
+ * @return a possibly {code null} {@link SendSmsResponse}. Upon returning {@code null}, the SMS
+ * is sent using the carrier network.
+ */
+ public @Nullable SendSmsResponse onSendDataSms(@NonNull byte[] data, @NonNull String format,
+ @NonNull String destAddress, int destPort) {
+ // optional
+ return null;
+ }
+
+ /**
+ * Implement this method to intercept long SMSs sent from the device.
+ *
+ * @param parts a {@link List} of the message parts
+ * @param format format the format of the response PDU, typically "3gpp" or "3gpp2"
+ * @param destAddress phone number of the recipient of the message
+ *
+ * @return a possibly {code null} {@link List} of {@link SendSmsResponse}, one for each message
+ * part. Upon returning {@code null}, the SMS is sent using the carrier network.
+ */
+ public @Nullable List<SendSmsResponse> onSendMultipartTextSms(@NonNull List<String> parts,
+ @NonNull String format, @NonNull String destAddress) {
+ // optional
+ return null;
+ }
+
+ /**
+ * Implement this method to intercept MMSs sent from the device.
+ *
+ * @param pduUri the content provider URI of the PDU to send
+ * @param locationUrl the optional URL to send this MMS PDU. If this is not specified,
+ * the PDU should be sent to the default MMSC URL.
+ *
+ * @return a possibly {@code null} {@link SendMmsResult}. Upon returning {@code null}, the
+ * MMS is sent using the carrier network.
+ */
+ public @Nullable SendMmsResult onSendMms(@NonNull Uri pduUri, @Nullable String locationUrl) {
+ // optional
+ return null;
+ }
+
+ /**
+ * Implement this method to download MMSs received.
+ *
+ * @param contentUri the content provider URI of the PDU to be downloaded.
+ * @param locationUrl the URL of the message to be downloaded.
+ *
+ * @return a {@link SendMmsResult}.
+ */
+ public int onDownloadMms(@NonNull Uri contentUri, @NonNull String locationUrl) {
+ // optional
+ return DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK;
+ }
+
+ @Override
+ public @Nullable IBinder onBind(@NonNull Intent intent) {
+ if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+ return null;
+ }
+ return mWrapper;
+ }
+
+ /**
+ * The result of sending an MMS.
+ */
+ public static final class SendMmsResult {
+ private int mResult;
+ private byte[] mSendConfPdu;
+
+ /**
+ * Constructs a SendMmsResult with the MMS send result, and the SenConf PDU.
+ *
+ * @param result the result which is one of {@link #SEND_STATUS_OK},
+ * {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and
+ * {@link #SEND_STATUS_ERROR}
+ * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message
+ * was sent. sendConfPdu is ignored if the {@code result} is not
+ * {@link #SEND_STATUS_OK}
+ */
+ public SendMmsResult(int result, @Nullable byte[] sendConfPdu) {
+ mResult = result;
+ mSendConfPdu = sendConfPdu;
+ }
+
+ /**
+ * Returns the result of sending the MMS.
+ *
+ * @return the result which is one of {@link #SEND_STATUS_OK},
+ * {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}
+ */
+ public int getResult() {
+ return mResult;
+ }
+
+ /**
+ * Returns the SendConf PDU, which confirms that the message was sent.
+ *
+ * @return the SendConf PDU
+ */
+ public @Nullable byte[] getSendConfPdu() {
+ return mSendConfPdu;
+ }
+ }
+
+ /**
+ * Object passed in callbacks upon successful completion of
+ * {@link ICarrierMessagingService#sendTextSms},
+ * {@link ICarrierMessagingService#sendDataSms}, and
+ * {@link ICarrierMessagingService#sendMultipartTextSms}.
+ * Contains message reference and ackPdu.
+ */
+ public static final class SendSmsResponse implements Parcelable {
+ private int mMessageRef;
+ private byte[] mAckPdu;
+ private int mErrorCode;
+
+ /**
+ * Constructs a SendSmsResponse for the message reference, the ack PDU, and error code for
+ * the just-sent SMS.
+ *
+ * @param messageRef message reference of the just-sent SMS
+ * @param ackPdu ackPdu for the just-sent SMS
+ * @param errorCode error code. See 3GPP 27.005, 3.2.5 for GSM/UMTS,
+ * 3GPP2 N.S0005 (IS-41C) Table 171 for CDMA, -1 if unknown or not applicable.
+ */
+ public SendSmsResponse(int messageRef, @NonNull byte[] ackPdu, int errorCode) {
+ mMessageRef = messageRef;
+ mAckPdu = ackPdu;
+ mErrorCode = errorCode;
+ }
+
+ /**
+ * Returns the message reference of the just-sent SMS.
+ *
+ * @return the message reference
+ */
+ public int getMessageRef() {
+ return mMessageRef;
+ }
+
+ /**
+ * Returns the ackPdu for the just-sent SMS.
+ *
+ * @return the ackPdu
+ */
+ public @NonNull byte[] getAckPdu() {
+ return mAckPdu;
+ }
+
+ /**
+ * Returns the error code upon encountering an error while sending the SMS, -1 if unknown or
+ * not applicable.
+ *
+ * @return errorCode the errorCode as defined in 3GPP 27.005, 3.2.5 for GSM/UMTS, and 3GPP2
+ * N.S0005 (IS-41C) Table 171 for CDMA, -1 if unknown or not applicable.
+ */
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMessageRef);
+ dest.writeByteArray(mAckPdu);
+ dest.writeInt(mErrorCode);
+ }
+
+ public static final Parcelable.Creator<SendSmsResponse> CREATOR
+ = new Parcelable.Creator<SendSmsResponse>() {
+ @Override
+ public SendSmsResponse createFromParcel(Parcel source) {
+ return new SendSmsResponse(source.readInt(),
+ source.createByteArray(),
+ source.readInt());
+ }
+
+ @Override
+ public SendSmsResponse[] newArray(int size) {
+ return new SendSmsResponse[size];
+ }
+ };
+ }
+
+ /**
+ * A wrapper around ICarrierMessagingService to enable the carrier messaging APP to implement
+ * methods it cares about in the {@link ICarrierMessagingService} interface.
+ */
+ private class ICarrierMessagingWrapper extends ICarrierMessagingService.Stub {
+ @Override
+ public void filterSms(MessagePdu pdu, String format, int destPort,
+ ICarrierMessagingCallback callback) {
+ try {
+ callback.onFilterComplete(onFilterSms(pdu, format, destPort));
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void sendTextSms(String text, String format, String destAddress,
+ ICarrierMessagingCallback callback) {
+ try {
+ SendSmsResponse sendSmsResponse = onSendTextSms(text, format, destAddress);
+ if (sendSmsResponse == null) {
+ callback.onSendSmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
+ } else {
+ callback.onSendSmsComplete(SEND_STATUS_OK, sendSmsResponse);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void sendDataSms(byte[] data, String format, String destAddress, int destPort,
+ ICarrierMessagingCallback callback) {
+ try {
+ SendSmsResponse sendSmsResponse = onSendDataSms(data, format, destAddress,
+ destPort);
+ if (sendSmsResponse == null) {
+ callback.onSendSmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
+ } else {
+ callback.onSendSmsComplete(SEND_STATUS_OK, sendSmsResponse);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void sendMultipartTextSms(List<String> parts, String format, String destAddress,
+ ICarrierMessagingCallback callback) {
+ try {
+ List<SendSmsResponse> sendSmsResponses =
+ onSendMultipartTextSms(parts, format, destAddress);
+ if (sendSmsResponses == null) {
+ callback.onSendMultipartSmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
+ } else {
+ callback.onSendMultipartSmsComplete(SEND_STATUS_OK, sendSmsResponses);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void sendMms(Uri pduUri, String locationUrl, ICarrierMessagingCallback callback) {
+ try {
+ SendMmsResult result = onSendMms(pduUri, locationUrl);
+ if (result == null) {
+ callback.onSendMmsComplete(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null);
+ } else {
+ callback.onSendMmsComplete(SEND_STATUS_OK, result.getSendConfPdu());
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void downloadMms(Uri contentUri, String locationUrl,
+ ICarrierMessagingCallback callback) {
+ try {
+ callback.onDownloadMmsComplete(onDownloadMms(contentUri, locationUrl));
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/carriermessaging/CarrierMessagingServiceManager.java b/core/java/android/service/carriermessaging/CarrierMessagingServiceManager.java
new file mode 100644
index 0000000..56ee2c1
--- /dev/null
+++ b/core/java/android/service/carriermessaging/CarrierMessagingServiceManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carriermessaging;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Provides basic structure for platform to connect to the carrier messaging service.
+ * <p>
+ * <code>
+ * CarrierMessagingServiceManager carrierMessagingServiceManager =
+ * new CarrierMessagingServiceManagerImpl();
+ * if (carrierMessagingServiceManager.bindToCarrierMessagingService(context, carrierPackageName)) {
+ * // wait for onServiceReady callback
+ * } else {
+ * // Unable to bind: handle error.
+ * }
+ * </code>
+ * <p> Upon completion {@link #disposeConnection} should be called to unbind the
+ * CarrierMessagingService.
+ * @hide
+ */
+public abstract class CarrierMessagingServiceManager {
+ // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
+ // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
+ private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
+
+ /**
+ * Binds to the carrier messaging service under package {@code carrierPackageName}. This method
+ * should be called exactly once.
+ *
+ * @param context the context
+ * @param carrierPackageName the carrier package name
+ * @return true upon successfully binding to a carrier messaging service, false otherwise
+ */
+ public boolean bindToCarrierMessagingService(Context context, String carrierPackageName) {
+ Preconditions.checkState(mCarrierMessagingServiceConnection == null);
+
+ Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
+ intent.setPackage(carrierPackageName);
+ mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
+ return context.bindService(intent, mCarrierMessagingServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Unbinds the carrier messaging service. This method should be called exactly once.
+ *
+ * @param context the context
+ */
+ public void disposeConnection(Context context) {
+ Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
+ context.unbindService(mCarrierMessagingServiceConnection);
+ mCarrierMessagingServiceConnection = null;
+ }
+
+ /**
+ * Implemented by subclasses to use the carrier messaging service once it is ready.
+ *
+ * @param carrierMessagingService the carirer messaing service interface
+ */
+ protected abstract void onServiceReady(ICarrierMessagingService carrierMessagingService);
+
+ /**
+ * A basic {@link ServiceConnection}.
+ */
+ private final class CarrierMessagingServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ onServiceReady(ICarrierMessagingService.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ }
+}
diff --git a/core/java/android/service/carriermessaging/ICarrierMessagingCallback.aidl b/core/java/android/service/carriermessaging/ICarrierMessagingCallback.aidl
new file mode 100644
index 0000000..da56ad1
--- /dev/null
+++ b/core/java/android/service/carriermessaging/ICarrierMessagingCallback.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carriermessaging;
+
+import android.service.carriermessaging.CarrierMessagingService;
+
+/**
+ * Callback interface definition for the Carrier Messaging Service client to get informed of the
+ * result of various API invocations.
+ */
+oneway interface ICarrierMessagingCallback {
+ void onFilterComplete(boolean keepMessage);
+ void onSendSmsComplete(
+ int result, in CarrierMessagingService.SendSmsResponse sendSmsResponse);
+ void onSendMultipartSmsComplete(
+ int result, in List<CarrierMessagingService.SendSmsResponse> sendSmsResponses);
+ void onSendMmsComplete(int result, in byte[] sendConfPdu);
+ void onDownloadMmsComplete(int result);
+}
diff --git a/core/java/android/service/carriermessaging/ICarrierMessagingService.aidl b/core/java/android/service/carriermessaging/ICarrierMessagingService.aidl
new file mode 100644
index 0000000..6e9e3fa
--- /dev/null
+++ b/core/java/android/service/carriermessaging/ICarrierMessagingService.aidl
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carriermessaging;
+
+import android.net.Uri;
+import android.service.carriermessaging.ICarrierMessagingCallback;
+import android.service.carriermessaging.MessagePdu;
+
+/**
+ * <p class="note"><strong>Note:</strong>
+ * This service can only be implemented by a carrier privileged app.
+ */
+oneway interface ICarrierMessagingService {
+ /**
+ * Request filtering an incoming SMS message.
+ * The service will call callback.onFilterComplete with the filtering result.
+ *
+ * @param pdu the PDUs of the message
+ * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
+ * @param destPort the destination port of a data SMS. It will be -1 for text SMS
+ * @param callback the callback to notify upon completion
+ */
+ void filterSms(
+ in MessagePdu pdu, String format, int destPort, in ICarrierMessagingCallback callback);
+
+ /**
+ * Request sending a new text SMS from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send
+ * status.
+ *
+ * @param text the text to send
+ * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
+ * @param destAddress phone number of the recipient of the message
+ * @param callback the callback to notify upon completion
+ */
+ void sendTextSms(String text, String format, String destAddress,
+ in ICarrierMessagingCallback callback);
+
+ /**
+ * Request sending a new data SMS from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send
+ * status.
+ *
+ * @param data the data to send
+ * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
+ * @param destAddress phone number of the recipient of the message
+ * @param destPort port number of the recipient of the message
+ * @param callback the callback to notify upon completion
+ */
+ void sendDataSms(in byte[] data, String format, String destAddress, int destPort,
+ in ICarrierMessagingCallback callback);
+
+ /**
+ * Request sending a new multi-part text SMS from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendMultipartSmsComplete}
+ * with the send status.
+ *
+ * @param parts the parts of the multi-part text SMS to send
+ * @param format the format of the response PDU, typically "3gpp" or "3gpp2"
+ * @param destAddress phone number of the recipient of the message
+ * @param callback the callback to notify upon completion
+ */
+ void sendMultipartTextSms(in List<String> parts, String format, String destAddress,
+ in ICarrierMessagingCallback callback);
+
+ /**
+ * Request sending a new MMS PDU from the device.
+ * The service will call {@link ICarrierMessagingCallback#onSendMmsComplete} with the send
+ * status.
+ *
+ * @param pduUri the content provider URI of the PDU to send
+ * @param locationUrl the optional url to send this MMS PDU.
+ * If this is not specified, PDU should be sent to the default MMSC url.
+ * @param callback the callback to notify upon completion
+ */
+ void sendMms(in Uri pduUri, String locationUrl, in ICarrierMessagingCallback callback);
+
+ /**
+ * Request downloading a new MMS.
+ * The service will call {@link ICarrierMessagingCallback#onDownloadMmsComplete} with the
+ * download status.
+ *
+ * @param pduUri the content provider URI of the PDU to be downloaded.
+ * @param locationUrl the URL of the message to be downloaded.
+ * @param callback the callback to notify upon completion
+ */
+ void downloadMms(in Uri pduUri, String locationUrl, in ICarrierMessagingCallback callback);
+}
+
diff --git a/core/java/android/service/carriermessaging/MessagePdu.aidl b/core/java/android/service/carriermessaging/MessagePdu.aidl
new file mode 100644
index 0000000..82b3fb3
--- /dev/null
+++ b/core/java/android/service/carriermessaging/MessagePdu.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carriermessaging;
+
+parcelable MessagePdu; \ No newline at end of file
diff --git a/core/java/android/service/carriermessaging/MessagePdu.java b/core/java/android/service/carriermessaging/MessagePdu.java
new file mode 100644
index 0000000..3c78568
--- /dev/null
+++ b/core/java/android/service/carriermessaging/MessagePdu.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carriermessaging;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A parcelable list of PDUs representing contents of a possibly multi-part SMS.
+ */
+public final class MessagePdu implements Parcelable {
+ private static final int NULL_LENGTH = -1;
+
+ private final List<byte[]> mPduList;
+
+ /**
+ * Constructs a MessagePdu with the list of message PDUs.
+ *
+ * @param pduList the list of message PDUs
+ */
+ public MessagePdu(@NonNull List<byte[]> pduList) {
+ if (pduList == null || pduList.contains(null)) {
+ throw new IllegalArgumentException("pduList must not be null or contain nulls");
+ }
+ mPduList = pduList;
+ }
+
+ /**
+ * Returns the contents of a possibly multi-part SMS.
+ *
+ * @return the list of PDUs
+ */
+ public @NonNull List<byte[]> getPdus() {
+ return mPduList;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mPduList == null) {
+ dest.writeInt(NULL_LENGTH);
+ } else {
+ dest.writeInt(mPduList.size());
+ for (byte[] messagePdu : mPduList) {
+ dest.writeByteArray(messagePdu);
+ }
+ }
+ }
+
+ /**
+ * Constructs a {@link MessagePdu} from a {@link Parcel}.
+ */
+ public static final Parcelable.Creator<MessagePdu> CREATOR
+ = new Parcelable.Creator<MessagePdu>() {
+ @Override
+ public MessagePdu createFromParcel(Parcel source) {
+ int size = source.readInt();
+ List<byte[]> pduList;
+ if (size == NULL_LENGTH) {
+ pduList = null;
+ } else {
+ pduList = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ pduList.add(source.createByteArray());
+ }
+ }
+ return new MessagePdu(pduList);
+ }
+
+ @Override
+ public MessagePdu[] newArray(int size) {
+ return new MessagePdu[size];
+ }
+ };
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 36401eb..ce28d0a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -474,13 +474,14 @@ public class ZenModeConfig implements Parcelable {
return downtime;
}
- public static Condition toTimeCondition(Context context, int minutesFromNow) {
+ public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
final long now = System.currentTimeMillis();
final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
- return toTimeCondition(context, now + millis, minutesFromNow, now);
+ return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle);
}
- public static Condition toTimeCondition(Context context, long time, int minutes, long now) {
+ public static Condition toTimeCondition(Context context, long time, int minutes, long now,
+ int userHandle) {
final int num, summaryResId, line1ResId;
if (minutes < 60) {
// display as minutes
@@ -493,7 +494,7 @@ public class ZenModeConfig implements Parcelable {
summaryResId = com.android.internal.R.plurals.zen_mode_duration_hours_summary;
line1ResId = com.android.internal.R.plurals.zen_mode_duration_hours;
}
- final String skeleton = DateFormat.is24HourFormat(context) ? "Hm" : "hma";
+ final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
final CharSequence formattedTime = DateFormat.format(pattern, time);
final Resources res = context.getResources();
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index bb0c2b2..f07d0d0 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -25,6 +25,8 @@ import android.service.trust.ITrustAgentServiceCallback;
interface ITrustAgentService {
oneway void onUnlockAttempt(boolean successful);
oneway void onTrustTimeout();
+ oneway void onDeviceLocked();
+ oneway void onDeviceUnlocked();
oneway void onConfigure(in List<PersistableBundle> options, IBinder token);
oneway void setCallback(ITrustAgentServiceCallback callback);
}
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index d6c997f..62fa978 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -92,6 +92,8 @@ public class TrustAgentService extends Service {
private static final int MSG_UNLOCK_ATTEMPT = 1;
private static final int MSG_CONFIGURE = 2;
private static final int MSG_TRUST_TIMEOUT = 3;
+ private static final int MSG_DEVICE_LOCKED = 4;
+ private static final int MSG_DEVICE_UNLOCKED = 5;
/**
* Class containing raw data for a given configuration request.
@@ -134,6 +136,12 @@ public class TrustAgentService extends Service {
case MSG_TRUST_TIMEOUT:
onTrustTimeout();
break;
+ case MSG_DEVICE_LOCKED:
+ onDeviceLocked();
+ break;
+ case MSG_DEVICE_UNLOCKED:
+ onDeviceUnlocked();
+ break;
}
}
};
@@ -173,6 +181,20 @@ public class TrustAgentService extends Service {
public void onTrustTimeout() {
}
+ /**
+ * Called when the device enters a state where a PIN, pattern or
+ * password must be entered to unlock it.
+ */
+ public void onDeviceLocked() {
+ }
+
+ /**
+ * Called when the device leaves a state where a PIN, pattern or
+ * password must be entered to unlock it.
+ */
+ public void onDeviceUnlocked() {
+ }
+
private void onError(String msg) {
Slog.v(TAG, "Remote exception while " + msg);
}
@@ -300,6 +322,16 @@ public class TrustAgentService extends Service {
.sendToTarget();
}
+ @Override
+ public void onDeviceLocked() throws RemoteException {
+ mHandler.obtainMessage(MSG_DEVICE_LOCKED).sendToTarget();
+ }
+
+ @Override
+ public void onDeviceUnlocked() throws RemoteException {
+ mHandler.obtainMessage(MSG_DEVICE_UNLOCKED).sendToTarget();
+ }
+
@Override /* Binder API */
public void setCallback(ITrustAgentServiceCallback callback) {
synchronized (mLock) {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index ceaf5f8..9496b53 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -23,6 +23,7 @@ import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.ViewRootImpl;
import android.view.WindowInsets;
+
import com.android.internal.R;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
@@ -32,18 +33,17 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.app.WallpaperManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
@@ -139,7 +139,6 @@ public abstract class WallpaperService extends Service {
boolean mInitializing = true;
boolean mVisible;
- boolean mScreenOn = true;
boolean mReportedVisible;
boolean mDestroyed;
@@ -191,20 +190,10 @@ public abstract class WallpaperService extends Service {
float mPendingYOffsetStep;
boolean mPendingSync;
MotionEvent mPendingMove;
-
- final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
- mScreenOn = true;
- reportVisibility();
- } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
- mScreenOn = false;
- reportVisibility();
- }
- }
- };
-
+
+ DisplayManager mDisplayManager;
+ Display mDisplay;
+
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
{
mRequestedFormat = PixelFormat.RGBX_8888;
@@ -536,8 +525,8 @@ public abstract class WallpaperService extends Service {
out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
out.print(" mDestroyed="); out.println(mDestroyed);
out.print(prefix); out.print("mVisible="); out.print(mVisible);
- out.print(" mScreenOn="); out.print(mScreenOn);
out.print(" mReportedVisible="); out.println(mReportedVisible);
+ out.print(prefix); out.print("mDisplay="); out.println(mDisplay);
out.print(prefix); out.print("mCreated="); out.print(mCreated);
out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
out.print(" mIsCreating="); out.print(mIsCreating);
@@ -549,7 +538,7 @@ public abstract class WallpaperService extends Service {
out.print(prefix); out.print("mType="); out.print(mType);
out.print(" mWindowFlags="); out.print(mWindowFlags);
out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
- out.print(" mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
+ out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
out.print(prefix); out.print("mVisibleInsets=");
out.print(mVisibleInsets.toShortString());
@@ -876,13 +865,10 @@ public abstract class WallpaperService extends Service {
mWindow.setSession(mSession);
- mScreenOn = ((PowerManager)getSystemService(Context.POWER_SERVICE)).isScreenOn();
+ mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
+ mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- registerReceiver(mReceiver, filter);
-
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
onCreate(mSurfaceHolder);
@@ -921,7 +907,8 @@ public abstract class WallpaperService extends Service {
void reportVisibility() {
if (!mDestroyed) {
- boolean visible = mVisible && mScreenOn;
+ boolean visible = mVisible
+ & mDisplay != null && mDisplay.getState() != Display.STATE_OFF;
if (mReportedVisible != visible) {
mReportedVisible = visible;
if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
@@ -1024,7 +1011,11 @@ public abstract class WallpaperService extends Service {
}
mDestroyed = true;
-
+
+ if (mDisplayManager != null) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+
if (mVisible) {
mVisible = false;
if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
@@ -1035,9 +1026,7 @@ public abstract class WallpaperService extends Service {
if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
onDestroy();
-
- unregisterReceiver(mReceiver);
-
+
if (mCreated) {
try {
if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
@@ -1062,8 +1051,25 @@ public abstract class WallpaperService extends Service {
}
}
}
+
+ private final DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (mDisplay.getDisplayId() == displayId) {
+ reportVisibility();
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+ };
}
-
+
class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
implements HandlerCaller.Callback {
private final HandlerCaller mCaller;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 70ad3f0..6cb73ea 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -355,14 +355,12 @@ public class StaticLayout extends Layout {
while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
int endPos = paraStart + breaks[breakIndex];
- boolean moreChars = (endPos < paraEnd); // XXX is this the right way to calculate this?
-
v = out(source, here, endPos,
fmAscent, fmDescent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex],
needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
chs, widths, paraStart, ellipsize, ellipsizedWidth,
- lineWidths[breakIndex], paint, moreChars);
+ lineWidths[breakIndex], paint, true);
if (endPos < spanEnd) {
// preserve metrics for current span
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index afeb24e..c03f7a6 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -17,6 +17,7 @@
package android.text.format;
import android.content.Context;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -166,8 +167,20 @@ public class DateFormat {
* @return true if 24 hour time format is selected, false otherwise.
*/
public static boolean is24HourFormat(Context context) {
- String value = Settings.System.getString(context.getContentResolver(),
- Settings.System.TIME_12_24);
+ return is24HourFormat(context, UserHandle.myUserId());
+ }
+
+ /**
+ * Returns true if user preference with the given user handle is set to 24-hour format.
+ * @param context the context to use for the content resolver
+ * @param userHandle the user handle of the user to query.
+ * @return true if 24 hour time format is selected, false otherwise.
+ *
+ * @hide
+ */
+ public static boolean is24HourFormat(Context context, int userHandle) {
+ String value = Settings.System.getStringForUser(context.getContentResolver(),
+ Settings.System.TIME_12_24, userHandle);
if (value == null) {
Locale locale = context.getResources().getConfiguration().locale;
@@ -179,7 +192,7 @@ public class DateFormat {
}
java.text.DateFormat natural =
- java.text.DateFormat.getTimeInstance(java.text.DateFormat.LONG, locale);
+ java.text.DateFormat.getTimeInstance(java.text.DateFormat.LONG, locale);
if (natural instanceof SimpleDateFormat) {
SimpleDateFormat sdf = (SimpleDateFormat) natural;
@@ -253,74 +266,30 @@ public class DateFormat {
* @hide
*/
public static String getTimeFormatString(Context context) {
- LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
- return is24HourFormat(context) ? d.timeFormat_Hm : d.timeFormat_hm;
+ return getTimeFormatString(context, UserHandle.myUserId());
}
/**
- * Returns a {@link java.text.DateFormat} object that can format the date
- * in short form (such as 12/31/1999) according
- * to the current locale and the user's date-order preference.
+ * Returns a String pattern that can be used to format the time according
+ * to the current locale and the user's 12-/24-hour clock preference.
* @param context the application context
- * @return the {@link java.text.DateFormat} object that properly formats the date.
+ * @param userHandle the user handle of the user to query the format for
+ * @hide
*/
- public static java.text.DateFormat getDateFormat(Context context) {
- String value = Settings.System.getString(context.getContentResolver(),
- Settings.System.DATE_FORMAT);
-
- return getDateFormatForSetting(context, value);
+ public static String getTimeFormatString(Context context, int userHandle) {
+ LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
+ return is24HourFormat(context, userHandle) ? d.timeFormat_Hm : d.timeFormat_hm;
}
/**
- * Returns a {@link java.text.DateFormat} object to format the date
- * as if the date format setting were set to <code>value</code>,
- * including null to use the locale's default format.
+ * Returns a {@link java.text.DateFormat} object that can format the date
+ * in short form according to the current locale.
+ *
* @param context the application context
- * @param value the date format setting string to interpret for
- * the current locale
- * @hide
+ * @return the {@link java.text.DateFormat} object that properly formats the date.
*/
- public static java.text.DateFormat getDateFormatForSetting(Context context,
- String value) {
- String format = getDateFormatStringForSetting(context, value);
- return new java.text.SimpleDateFormat(format);
- }
-
- private static String getDateFormatStringForSetting(Context context, String value) {
- if (value != null) {
- int month = value.indexOf('M');
- int day = value.indexOf('d');
- int year = value.indexOf('y');
-
- if (month >= 0 && day >= 0 && year >= 0) {
- String template = context.getString(R.string.numeric_date_template);
- if (year < month && year < day) {
- if (month < day) {
- value = String.format(template, "yyyy", "MM", "dd");
- } else {
- value = String.format(template, "yyyy", "dd", "MM");
- }
- } else if (month < day) {
- if (day < year) {
- value = String.format(template, "MM", "dd", "yyyy");
- } else { // unlikely
- value = String.format(template, "MM", "yyyy", "dd");
- }
- } else { // day < month
- if (month < year) {
- value = String.format(template, "dd", "MM", "yyyy");
- } else { // unlikely
- value = String.format(template, "dd", "yyyy", "MM");
- }
- }
-
- return value;
- }
- }
-
- // The setting is not set; use the locale's default.
- LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
- return d.shortDateFormat4;
+ public static java.text.DateFormat getDateFormat(Context context) {
+ return java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT);
}
/**
@@ -353,14 +322,16 @@ public class DateFormat {
* order returned here.
*/
public static char[] getDateFormatOrder(Context context) {
- return ICU.getDateFormatOrder(getDateFormatString(context));
+ return ICU.getDateFormatOrder(getDateFormatString());
}
- private static String getDateFormatString(Context context) {
- String value = Settings.System.getString(context.getContentResolver(),
- Settings.System.DATE_FORMAT);
+ private static String getDateFormatString() {
+ java.text.DateFormat df = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT);
+ if (df instanceof SimpleDateFormat) {
+ return ((SimpleDateFormat) df).toPattern();
+ }
- return getDateFormatStringForSetting(context, value);
+ throw new AssertionError("!(df instanceof SimpleDateFormat)");
}
/**
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
index a159b40..9749121 100644
--- a/core/java/android/transition/ChangeTransform.java
+++ b/core/java/android/transition/ChangeTransform.java
@@ -376,7 +376,7 @@ public class ChangeTransform extends Transition {
while (outerTransition.mParent != null) {
outerTransition = outerTransition.mParent;
}
- GhostListener listener = new GhostListener(view, ghostView, endMatrix);
+ GhostListener listener = new GhostListener(view, startValues.view, ghostView);
outerTransition.addListener(listener);
if (startValues.view != endValues.view) {
@@ -466,13 +466,13 @@ public class ChangeTransform extends Transition {
private static class GhostListener extends Transition.TransitionListenerAdapter {
private View mView;
+ private View mStartView;
private GhostView mGhostView;
- private Matrix mEndMatrix;
- public GhostListener(View view, GhostView ghostView, Matrix endMatrix) {
+ public GhostListener(View view, View startView, GhostView ghostView) {
mView = view;
+ mStartView = startView;
mGhostView = ghostView;
- mEndMatrix = endMatrix;
}
@Override
@@ -481,6 +481,7 @@ public class ChangeTransform extends Transition {
GhostView.removeGhost(mView);
mView.setTagInternal(R.id.transitionTransform, null);
mView.setTagInternal(R.id.parentMatrix, null);
+ mStartView.setTransitionAlpha(1);
}
@Override
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index 623cdd1..ad6c2dd 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -44,8 +44,8 @@ public class SidePropagation extends VisibilityPropagation {
* farther from the edge. The default is {@link Gravity#BOTTOM}.
*
* @param side The side that is used to calculate the transition propagation. Must be one of
- * {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT}, or
- * {@link Gravity#BOTTOM}.
+ * {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
+ * {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
*/
public void setSide(int side) {
mSide = side;
@@ -106,7 +106,7 @@ public class SidePropagation extends VisibilityPropagation {
epicenterY = (top + bottom) / 2;
}
- float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY,
+ float distance = distance(sceneRoot, viewCenterX, viewCenterY, epicenterX, epicenterY,
left, top, right, bottom);
float maxDistance = getMaxDistance(sceneRoot);
float distanceFraction = distance/maxDistance;
@@ -119,10 +119,20 @@ public class SidePropagation extends VisibilityPropagation {
return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
}
- private int distance(int viewX, int viewY, int epicenterX, int epicenterY,
+ private int distance(View sceneRoot, int viewX, int viewY, int epicenterX, int epicenterY,
int left, int top, int right, int bottom) {
+ final int side;
+ if (mSide == Gravity.START) {
+ final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ side = isRtl ? Gravity.RIGHT : Gravity.LEFT;
+ } else if (mSide == Gravity.END) {
+ final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ side = isRtl ? Gravity.LEFT : Gravity.RIGHT;
+ } else {
+ side = mSide;
+ }
int distance = 0;
- switch (mSide) {
+ switch (side) {
case Gravity.LEFT:
distance = right - viewX + Math.abs(epicenterY - viewY);
break;
@@ -143,6 +153,8 @@ public class SidePropagation extends VisibilityPropagation {
switch (mSide) {
case Gravity.LEFT:
case Gravity.RIGHT:
+ case Gravity.START:
+ case Gravity.END:
return sceneRoot.getWidth();
default:
return sceneRoot.getHeight();
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index ae2e4aa..be1d907 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -76,6 +76,20 @@ public class Slide extends Visibility {
}
};
+ private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
+ @Override
+ public float getGoneX(ViewGroup sceneRoot, View view) {
+ final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ final float x;
+ if (isRtl) {
+ x = view.getTranslationX() + sceneRoot.getWidth();
+ } else {
+ x = view.getTranslationX() - sceneRoot.getWidth();
+ }
+ return x;
+ }
+ };
+
private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
@Override
public float getGoneY(ViewGroup sceneRoot, View view) {
@@ -90,6 +104,20 @@ public class Slide extends Visibility {
}
};
+ private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
+ @Override
+ public float getGoneX(ViewGroup sceneRoot, View view) {
+ final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ final float x;
+ if (isRtl) {
+ x = view.getTranslationX() - sceneRoot.getWidth();
+ } else {
+ x = view.getTranslationX() + sceneRoot.getWidth();
+ }
+ return x;
+ }
+ };
+
private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
@Override
public float getGoneY(ViewGroup sceneRoot, View view) {
@@ -144,7 +172,8 @@ public class Slide extends Visibility {
*
* @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
* {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
- * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
+ * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+ * {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
* @attr ref android.R.styleable#Slide_slideEdge
*/
public void setSlideEdge(int slideEdge) {
@@ -161,6 +190,12 @@ public class Slide extends Visibility {
case Gravity.BOTTOM:
mSlideCalculator = sCalculateBottom;
break;
+ case Gravity.START:
+ mSlideCalculator = sCalculateStart;
+ break;
+ case Gravity.END:
+ mSlideCalculator = sCalculateEnd;
+ break;
default:
throw new IllegalArgumentException("Invalid slide direction");
}
@@ -175,7 +210,8 @@ public class Slide extends Visibility {
*
* @return the edge of the scene to use for Views appearing and disappearing. One of
* {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
- * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
+ * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+ * {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
* @attr ref android.R.styleable#Slide_slideEdge
*/
public int getSlideEdge() {
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 36bac31..8779229 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -63,6 +63,7 @@ public abstract class Visibility extends Transition {
private static final String[] sTransitionProperties = {
PROPNAME_VISIBILITY,
+ PROPNAME_PARENT,
};
private static class VisibilityInfo {
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index e5f3b2c..92b19be 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -280,7 +280,7 @@ public class PathParser {
* @param path The target Path object.
*/
public static void nodesToPath(PathDataNode[] node, Path path) {
- float[] current = new float[4];
+ float[] current = new float[6];
char previousCommand = 'm';
for (int i = 0; i < node.length; i++) {
addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
@@ -313,6 +313,8 @@ public class PathParser {
float currentY = current[1];
float ctrlPointX = current[2];
float ctrlPointY = current[3];
+ float currentSegmentStartX = current[4];
+ float currentSegmentStartY = current[5];
float reflectiveCtrlPointX;
float reflectiveCtrlPointY;
@@ -320,7 +322,15 @@ public class PathParser {
case 'z':
case 'Z':
path.close();
- return;
+ // Path is closed here, but we need to move the pen to the
+ // closed position. So we cache the segment's starting position,
+ // and restore it here.
+ currentX = currentSegmentStartX;
+ currentY = currentSegmentStartY;
+ ctrlPointX = currentSegmentStartX;
+ ctrlPointY = currentSegmentStartY;
+ path.moveTo(currentX, currentY);
+ break;
case 'm':
case 'M':
case 'l':
@@ -350,17 +360,22 @@ public class PathParser {
incr = 7;
break;
}
+
for (int k = 0; k < val.length; k += incr) {
switch (cmd) {
case 'm': // moveto - Start a new sub-path (relative)
path.rMoveTo(val[k + 0], val[k + 1]);
currentX += val[k + 0];
currentY += val[k + 1];
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
break;
case 'M': // moveto - Start a new sub-path
path.moveTo(val[k + 0], val[k + 1]);
currentX = val[k + 0];
currentY = val[k + 1];
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
break;
case 'l': // lineto - Draw a line from the current point (relative)
path.rLineTo(val[k + 0], val[k + 1]);
@@ -372,10 +387,6 @@ public class PathParser {
currentX = val[k + 0];
currentY = val[k + 1];
break;
- case 'z': // closepath - Close the current subpath
- case 'Z': // closepath - Close the current subpath
- path.close();
- break;
case 'h': // horizontal lineto - Draws a horizontal line (relative)
path.rLineTo(val[k + 0], 0);
currentX += val[k + 0];
@@ -526,6 +537,8 @@ public class PathParser {
current[1] = currentY;
current[2] = ctrlPointX;
current[3] = ctrlPointY;
+ current[4] = currentSegmentStartX;
+ current[5] = currentSegmentStartY;
}
private static void drawArc(Path p,
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 41a4a7c..19b7861 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -216,7 +216,7 @@ class GLES20Canvas extends HardwareCanvas {
///////////////////////////////////////////////////////////////////////////
@Override
- public void callDrawGLFunction(long drawGLFunction) {
+ public void callDrawGLFunction2(long drawGLFunction) {
nCallDrawGLFunction(mRenderer, drawGLFunction);
}
@@ -879,6 +879,9 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
+ if ((start | end | (end - start) | (text.length() - end)) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 4e30749..98e3927 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -98,7 +98,7 @@ public abstract class HardwareCanvas extends Canvas {
*
* @hide
*/
- public void callDrawGLFunction(long drawGLFunction) {
+ public void callDrawGLFunction2(long drawGLFunction) {
// Noop - this is done in the display list recorder subclass
}
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 1ecdf30..e2ad3ad 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -196,6 +196,13 @@ public abstract class InputEvent implements Parcelable {
public abstract long getEventTimeNano();
/**
+ * Marks the input event as being canceled.
+ *
+ * @hide
+ */
+ public abstract void cancel();
+
+ /**
* Gets the unique sequence number of this event.
* Every input event that is created or received by a process has a
* unique sequence number. Moreover, a new sequence number is obtained
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0701b53..243a0fc 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -2304,6 +2304,16 @@ public class KeyEvent extends InputEvent implements Parcelable {
}
/**
+ * Set {@link #FLAG_CANCELED} flag for the key event.
+ *
+ * @hide
+ */
+ @Override
+ public final void cancel() {
+ mFlags |= FLAG_CANCELED;
+ }
+
+ /**
* Call this during {@link Callback#onKeyDown} to have the system track
* the key through its final up (possibly including a long press). Note
* that only one key can be tracked at a time -- if another key down
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index ae39b7a..1c5c41c 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3168,6 +3168,12 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return ev;
}
+ /** @hide */
+ @Override
+ public final void cancel() {
+ setAction(ACTION_CANCEL);
+ }
+
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_MOTION_EVENT);
nativeWriteToParcel(mNativePtr, out);
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index b95f9a4..7feca30 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -189,9 +189,6 @@ public class RenderNodeAnimator extends Animator {
}
private void doStart() {
- mState = STATE_RUNNING;
- nStart(mNativePtr.get(), this);
-
// Alpha is a special snowflake that has the canonical value stored
// in mTransformationInfo instead of in RenderNode, so we need to update
// it with the final value here.
@@ -201,7 +198,7 @@ public class RenderNodeAnimator extends Animator {
mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
}
- notifyStartListeners();
+ moveToRunningState();
if (mViewTarget != null) {
// Kick off a frame to start the process
@@ -209,6 +206,12 @@ public class RenderNodeAnimator extends Animator {
}
}
+ private void moveToRunningState() {
+ mState = STATE_RUNNING;
+ nStart(mNativePtr.get(), this);
+ notifyStartListeners();
+ }
+
private void notifyStartListeners() {
final ArrayList<AnimatorListener> listeners = cloneListeners();
final int numListeners = listeners == null ? 0 : listeners.size();
@@ -222,7 +225,7 @@ public class RenderNodeAnimator extends Animator {
if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
if (mState == STATE_DELAYED) {
getHelper().removeDelayedAnimation(this);
- notifyStartListeners();
+ moveToRunningState();
}
nEnd(mNativePtr.get());
@@ -242,7 +245,15 @@ public class RenderNodeAnimator extends Animator {
@Override
public void end() {
if (mState != STATE_FINISHED) {
+ if (mState < STATE_RUNNING) {
+ getHelper().removeDelayedAnimation(this);
+ doStart();
+ }
nEnd(mNativePtr.get());
+ if (mViewTarget != null) {
+ // Kick off a frame to flush the state change
+ mViewTarget.invalidateViewProperty(true, false);
+ }
}
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 82ef171..83b8100 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -53,6 +53,9 @@ public class Surface implements Parcelable {
private static native void nativeAllocateBuffers(long nativeObject);
+ private static native int nativeGetWidth(long nativeObject);
+ private static native int nativeGetHeight(long nativeObject);
+
public static final Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
@Override
@@ -326,7 +329,9 @@ public class Surface implements Parcelable {
if (mHwuiContext == null) {
mHwuiContext = new HwuiContext();
}
- return mHwuiContext.lockCanvas();
+ return mHwuiContext.lockCanvas(
+ nativeGetWidth(mNativeObject),
+ nativeGetHeight(mNativeObject));
}
}
@@ -575,11 +580,11 @@ public class Surface implements Parcelable {
mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
}
- Canvas lockCanvas() {
+ Canvas lockCanvas(int width, int height) {
if (mCanvas != null) {
throw new IllegalStateException("Surface was already locked!");
}
- mCanvas = mRenderNode.start(0, 0);
+ mCanvas = mRenderNode.start(width, height);
return mCanvas;
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 5579c13..131c039 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -274,7 +274,7 @@ public class ThreadedRenderer extends HardwareRenderer {
}
private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
updateViewTreeDisplayList(view);
if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
@@ -340,6 +340,7 @@ public class ThreadedRenderer extends HardwareRenderer {
recordDuration, view.getResources().getDisplayMetrics().density);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
setEnabled(false);
+ attachInfo.mViewRootImpl.mSurface.release();
// Invalidate since we failed to draw. This should fetch a Surface
// if it is still needed or do nothing if we are no longer drawing
attachInfo.mViewRootImpl.invalidate();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a735d54..d74ab13 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -56,6 +56,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -2400,12 +2401,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
static final int PFLAG3_NESTED_SCROLLING_ENABLED = 0x80;
- /**
- * Flag indicating that outline was invalidated and should be rebuilt the next time
- * the DisplayList is updated.
- */
- static final int PFLAG3_OUTLINE_INVALID = 0x100;
-
/* End of masks for mPrivateFlags3 */
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
@@ -5901,6 +5896,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
region.op(interactiveRegion, Region.Op.INTERSECT);
}
+ // Take into account the window bounds.
+ final View root = getRootView();
+ if (root != null) {
+ region.op(dx, dy, root.getWidth() + dx, root.getHeight() + dy, Region.Op.INTERSECT);
+ }
+
// If the view is completely covered, done.
if (region.isEmpty()) {
return false;
@@ -11271,7 +11272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setOutlineProvider(ViewOutlineProvider)
*/
public void invalidateOutline() {
- mPrivateFlags3 |= PFLAG3_OUTLINE_INVALID;
+ rebuildOutline();
notifySubtreeAccessibilityStateChangedIfNeeded();
invalidateViewProperty(false, false);
@@ -13174,7 +13175,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
// Should resolve Drawables before Padding because we need the layout direction of the
// Drawable to correctly resolve Padding.
- if (!isDrawablesResolved()) {
+ if (!areDrawablesResolved()) {
resolveDrawables();
}
if (!isPaddingResolved()) {
@@ -13438,6 +13439,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
public void resetResolvedPadding() {
+ resetResolvedPaddingInternal();
+ }
+
+ /**
+ * Used when we only want to reset *this* view's padding and not trigger overrides
+ * in ViewGroup that reset children too.
+ */
+ void resetResolvedPaddingInternal() {
mPrivateFlags2 &= ~PFLAG2_PADDING_RESOLVED;
}
@@ -14397,143 +14406,158 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
- mCachingFailed = false;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+ "buildDrawingCache/SW Layer for " + getClass().getSimpleName());
+ }
+ try {
+ buildDrawingCacheImpl(autoScale);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+ }
- int width = mRight - mLeft;
- int height = mBottom - mTop;
+ /**
+ * private, internal implementation of buildDrawingCache, used to enable tracing
+ */
+ private void buildDrawingCacheImpl(boolean autoScale) {
+ mCachingFailed = false;
- final AttachInfo attachInfo = mAttachInfo;
- final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
+ int width = mRight - mLeft;
+ int height = mBottom - mTop;
+
+ final AttachInfo attachInfo = mAttachInfo;
+ final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
+
+ if (autoScale && scalingRequired) {
+ width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
+ height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
+ }
+
+ final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
+ final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
+ final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
- if (autoScale && scalingRequired) {
- width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
- height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
+ final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
+ final long drawingCacheSize =
+ ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
+ if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
+ if (width > 0 && height > 0) {
+ Log.w(VIEW_LOG_TAG, "View too large to fit into drawing cache, needs "
+ + projectedBitmapSize + " bytes, only "
+ + drawingCacheSize + " available");
}
+ destroyDrawingCache();
+ mCachingFailed = true;
+ return;
+ }
- final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
- final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
- final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
+ boolean clear = true;
+ Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
- final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
- final long drawingCacheSize =
- ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
- if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
- if (width > 0 && height > 0) {
- Log.w(VIEW_LOG_TAG, "View too large to fit into drawing cache, needs "
- + projectedBitmapSize + " bytes, only "
- + drawingCacheSize + " available");
+ if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
+ Bitmap.Config quality;
+ if (!opaque) {
+ // Never pick ARGB_4444 because it looks awful
+ // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
+ switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
+ case DRAWING_CACHE_QUALITY_AUTO:
+ case DRAWING_CACHE_QUALITY_LOW:
+ case DRAWING_CACHE_QUALITY_HIGH:
+ default:
+ quality = Bitmap.Config.ARGB_8888;
+ break;
}
- destroyDrawingCache();
- mCachingFailed = true;
- return;
+ } else {
+ // Optimization for translucent windows
+ // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
+ quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
}
- boolean clear = true;
- Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
+ // Try to cleanup memory
+ if (bitmap != null) bitmap.recycle();
- if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
- Bitmap.Config quality;
- if (!opaque) {
- // Never pick ARGB_4444 because it looks awful
- // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
- switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
- case DRAWING_CACHE_QUALITY_AUTO:
- case DRAWING_CACHE_QUALITY_LOW:
- case DRAWING_CACHE_QUALITY_HIGH:
- default:
- quality = Bitmap.Config.ARGB_8888;
- break;
- }
+ try {
+ bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
+ width, height, quality);
+ bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
+ if (autoScale) {
+ mDrawingCache = bitmap;
} else {
- // Optimization for translucent windows
- // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
- quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+ mUnscaledDrawingCache = bitmap;
}
-
- // Try to cleanup memory
- if (bitmap != null) bitmap.recycle();
-
- try {
- bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
- width, height, quality);
- bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
- if (autoScale) {
- mDrawingCache = bitmap;
- } else {
- mUnscaledDrawingCache = bitmap;
- }
- if (opaque && use32BitCache) bitmap.setHasAlpha(false);
- } catch (OutOfMemoryError e) {
- // If there is not enough memory to create the bitmap cache, just
- // ignore the issue as bitmap caches are not required to draw the
- // view hierarchy
- if (autoScale) {
- mDrawingCache = null;
- } else {
- mUnscaledDrawingCache = null;
- }
- mCachingFailed = true;
- return;
+ if (opaque && use32BitCache) bitmap.setHasAlpha(false);
+ } catch (OutOfMemoryError e) {
+ // If there is not enough memory to create the bitmap cache, just
+ // ignore the issue as bitmap caches are not required to draw the
+ // view hierarchy
+ if (autoScale) {
+ mDrawingCache = null;
+ } else {
+ mUnscaledDrawingCache = null;
}
-
- clear = drawingCacheBackgroundColor != 0;
+ mCachingFailed = true;
+ return;
}
- Canvas canvas;
- if (attachInfo != null) {
- canvas = attachInfo.mCanvas;
- if (canvas == null) {
- canvas = new Canvas();
- }
- canvas.setBitmap(bitmap);
- // Temporarily clobber the cached Canvas in case one of our children
- // is also using a drawing cache. Without this, the children would
- // steal the canvas by attaching their own bitmap to it and bad, bad
- // thing would happen (invisible views, corrupted drawings, etc.)
- attachInfo.mCanvas = null;
- } else {
- // This case should hopefully never or seldom happen
- canvas = new Canvas(bitmap);
- }
+ clear = drawingCacheBackgroundColor != 0;
+ }
- if (clear) {
- bitmap.eraseColor(drawingCacheBackgroundColor);
+ Canvas canvas;
+ if (attachInfo != null) {
+ canvas = attachInfo.mCanvas;
+ if (canvas == null) {
+ canvas = new Canvas();
}
+ canvas.setBitmap(bitmap);
+ // Temporarily clobber the cached Canvas in case one of our children
+ // is also using a drawing cache. Without this, the children would
+ // steal the canvas by attaching their own bitmap to it and bad, bad
+ // thing would happen (invisible views, corrupted drawings, etc.)
+ attachInfo.mCanvas = null;
+ } else {
+ // This case should hopefully never or seldom happen
+ canvas = new Canvas(bitmap);
+ }
- computeScroll();
- final int restoreCount = canvas.save();
+ if (clear) {
+ bitmap.eraseColor(drawingCacheBackgroundColor);
+ }
- if (autoScale && scalingRequired) {
- final float scale = attachInfo.mApplicationScale;
- canvas.scale(scale, scale);
- }
+ computeScroll();
+ final int restoreCount = canvas.save();
+
+ if (autoScale && scalingRequired) {
+ final float scale = attachInfo.mApplicationScale;
+ canvas.scale(scale, scale);
+ }
- canvas.translate(-mScrollX, -mScrollY);
+ canvas.translate(-mScrollX, -mScrollY);
- mPrivateFlags |= PFLAG_DRAWN;
- if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
- mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
- }
+ mPrivateFlags |= PFLAG_DRAWN;
+ if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
+ mLayerType != LAYER_TYPE_NONE) {
+ mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
+ }
- // Fast path for layouts with no backgrounds
- if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
- mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- dispatchDraw(canvas);
- if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.getOverlayView().draw(canvas);
- }
- } else {
- draw(canvas);
+ // Fast path for layouts with no backgrounds
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
+ mPrivateFlags &= ~PFLAG_DIRTY_MASK;
+ dispatchDraw(canvas);
+ if (mOverlay != null && !mOverlay.isEmpty()) {
+ mOverlay.getOverlayView().draw(canvas);
}
+ } else {
+ draw(canvas);
+ }
- canvas.restoreToCount(restoreCount);
- canvas.setBitmap(null);
+ canvas.restoreToCount(restoreCount);
+ canvas.setBitmap(null);
- if (attachInfo != null) {
- // Restore the cached Canvas for our siblings
- attachInfo.mCanvas = canvas;
- }
+ if (attachInfo != null) {
+ // Restore the cached Canvas for our siblings
+ attachInfo.mCanvas = canvas;
}
}
@@ -14760,27 +14784,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* this view, to which future drawing operations will be clipped.
*/
public void setClipBounds(Rect clipBounds) {
+ if (clipBounds == mClipBounds
+ || (clipBounds != null && clipBounds.equals(mClipBounds))) {
+ return;
+ }
if (clipBounds != null) {
- if (clipBounds.equals(mClipBounds)) {
- return;
- }
if (mClipBounds == null) {
- invalidate();
mClipBounds = new Rect(clipBounds);
} else {
- invalidate(Math.min(mClipBounds.left, clipBounds.left),
- Math.min(mClipBounds.top, clipBounds.top),
- Math.max(mClipBounds.right, clipBounds.right),
- Math.max(mClipBounds.bottom, clipBounds.bottom));
mClipBounds.set(clipBounds);
}
} else {
- if (mClipBounds != null) {
- invalidate();
- mClipBounds = null;
- }
+ mClipBounds = null;
}
mRenderNode.setClipBounds(mClipBounds);
+ invalidateViewProperty(false, false);
}
/**
@@ -14859,10 +14877,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
void setDisplayListProperties(RenderNode renderNode) {
if (renderNode != null) {
- if ((mPrivateFlags3 & PFLAG3_OUTLINE_INVALID) != 0) {
- rebuildOutline();
- mPrivateFlags3 &= ~PFLAG3_OUTLINE_INVALID;
- }
renderNode.setHasOverlappingRendering(hasOverlappingRendering());
if (mParent instanceof ViewGroup) {
renderNode.setClipToBounds(
@@ -15464,7 +15478,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
- mPrivateFlags3 |= PFLAG3_OUTLINE_INVALID;
+ rebuildOutline();
}
// Attempt to use a display list if requested.
@@ -15472,10 +15486,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
&& mAttachInfo.mHardwareRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
- final RenderNode displayList = mBackgroundRenderNode;
- if (displayList != null && displayList.isValid()) {
- setBackgroundDisplayListProperties(displayList);
- ((HardwareCanvas) canvas).drawRenderNode(displayList);
+ final RenderNode renderNode = mBackgroundRenderNode;
+ if (renderNode != null && renderNode.isValid()) {
+ setBackgroundRenderNodeProperties(renderNode);
+ ((HardwareCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
@@ -15491,14 +15505,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- /**
- * Set up background drawable display list properties.
- *
- * @param displayList Valid display list for the background drawable
- */
- private void setBackgroundDisplayListProperties(RenderNode displayList) {
- displayList.setTranslationX(mScrollX);
- displayList.setTranslationY(mScrollY);
+ private void setBackgroundRenderNodeProperties(RenderNode renderNode) {
+ renderNode.setTranslationX(mScrollX);
+ renderNode.setTranslationY(mScrollY);
}
/**
@@ -15847,7 +15856,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
- mPrivateFlags3 |= PFLAG3_OUTLINE_INVALID;
+ rebuildOutline();
}
/**
@@ -15883,8 +15892,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
-
- mPrivateFlags3 |= PFLAG3_OUTLINE_INVALID;
+ rebuildOutline();
}
}
@@ -15974,6 +15982,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
onResolveDrawables(layoutDirection);
}
+ boolean areDrawablesResolved() {
+ return (mPrivateFlags2 & PFLAG2_DRAWABLE_RESOLVED) == PFLAG2_DRAWABLE_RESOLVED;
+ }
+
/**
* Called when layout direction has been resolved.
*
@@ -15993,11 +16005,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
protected void resetResolvedDrawables() {
- mPrivateFlags2 &= ~PFLAG2_DRAWABLE_RESOLVED;
+ resetResolvedDrawablesInternal();
}
- private boolean isDrawablesResolved() {
- return (mPrivateFlags2 & PFLAG2_DRAWABLE_RESOLVED) == PFLAG2_DRAWABLE_RESOLVED;
+ void resetResolvedDrawablesInternal() {
+ mPrivateFlags2 &= ~PFLAG2_DRAWABLE_RESOLVED;
}
/**
@@ -16297,10 +16309,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
padding = new Rect();
sThreadLocal.set(padding);
}
- resetResolvedDrawables();
+ resetResolvedDrawablesInternal();
background.setLayoutDirection(getLayoutDirection());
if (background.getPadding(padding)) {
- resetResolvedPadding();
+ resetResolvedPaddingInternal();
switch (background.getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
mUserPaddingLeftInitial = padding.right;
@@ -16500,7 +16512,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param bottom the bottom padding in pixels
*/
public void setPadding(int left, int top, int right, int bottom) {
- resetResolvedPadding();
+ resetResolvedPaddingInternal();
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
@@ -16592,7 +16604,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param bottom the bottom padding in pixels
*/
public void setPaddingRelative(int start, int top, int end, int bottom) {
- resetResolvedPadding();
+ resetResolvedPaddingInternal();
mUserPaddingStart = start;
mUserPaddingEnd = end;
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
index 001cd01..d44df31 100644
--- a/core/java/android/view/ViewAnimationUtils.java
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -17,6 +17,7 @@
package android.view;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.animation.RevealAnimator;
/**
@@ -35,7 +36,11 @@ public final class ViewAnimationUtils {
* {@link View#setClipToOutline(boolean) View Outline clipping}.
* <p>
* Note that the animation returned here is a one-shot animation. It cannot
- * be re-used, and once started it cannot be paused or resumed.
+ * be re-used, and once started it cannot be paused or resumed. It is also
+ * an asynchronous animation that automatically runs off of the UI thread.
+ * As a result {@link AnimatorListener#onAnimationEnd(Animator)}
+ * will occur after the animation has ended, but it may be delayed depending
+ * on thread responsiveness.
*
* @param view The View will be clipped to the animating circle.
* @param centerX The x coordinate of the center of the animating circle.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 748e87b..abf23fc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3534,8 +3534,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * By default, children are clipped to the padding of the ViewGroup. This
- * allows view groups to override this behavior
+ * Sets whether this ViewGroup will clip its children to its padding, if
+ * padding is present.
+ * <p>
+ * By default, children are clipped to the padding of their parent
+ * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
*
* @param clipToPadding true to clip children to the padding of the
* group, false otherwise
@@ -3549,7 +3552,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * Check if this ViewGroup is configured to clip child views to its padding.
+ * Returns whether this ViewGroup will clip its children to its padding, if
+ * padding is present.
+ * <p>
+ * By default, children are clipped to the padding of their parent
+ * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
*
* @return true if this ViewGroup clips children to its padding, false otherwise
*
@@ -6150,7 +6157,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
- if (child.isLayoutDirectionInherited()) {
+ if (child.isLayoutDirectionInherited() && !child.isPaddingResolved()) {
child.resolvePadding();
}
}
@@ -6165,7 +6172,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
- if (child.isLayoutDirectionInherited()) {
+ if (child.isLayoutDirectionInherited() && !child.areDrawablesResolved()) {
child.resolveDrawables();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5d2a24b..b12c747 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -245,7 +245,7 @@ public final class ViewRootImpl implements ViewParent,
// These can be accessed by any thread, must be protected with a lock.
// Surface can never be reassigned or cleared (use Surface.clear()).
- private final Surface mSurface = new Surface();
+ final Surface mSurface = new Surface();
boolean mAdded;
boolean mAddedTouchMode;
@@ -3596,12 +3596,19 @@ public final class ViewRootImpl implements ViewParent,
if (mView == null || !mAdded) {
Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
return true;
- } else if (!mAttachInfo.mHasWindowFocus &&
- !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
- !isTerminalInputEvent(q.mEvent)) {
- // If this is a focused event and the window doesn't currently have input focus,
- // then drop this event. This could be an event that came back from the previous
- // stage but the window has lost focus in the meantime.
+ } else if ((!mAttachInfo.mHasWindowFocus || mStopped)
+ && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+ // This is a focus event and the window doesn't currently have input focus or
+ // has stopped. This could be an event that came back from the previous stage
+ // but the window has lost focus or stopped in the meantime.
+ if (isTerminalInputEvent(q.mEvent)) {
+ // Don't drop terminal input events, however mark them as canceled.
+ q.mEvent.cancel();
+ Slog.w(TAG, "Cancelling event due to no window focus: " + q.mEvent);
+ return false;
+ }
+
+ // Drop non-terminal input events.
Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
return true;
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index b85fec8..521fd31 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -37,6 +37,8 @@ public final class ViewTreeObserver {
private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
+ private CopyOnWriteArrayList<OnEnterAnimationCompleteListener>
+ mOnEnterAnimationCompleteListeners;
// Non-recursive listeners use CopyOnWriteArray
// Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
@@ -316,6 +318,13 @@ public final class ViewTreeObserver {
}
/**
+ * @hide
+ */
+ public interface OnEnterAnimationCompleteListener {
+ public void onEnterAnimationComplete();
+ }
+
+ /**
* Creates a new ViewTreeObserver. This constructor should not be called
*/
ViewTreeObserver() {
@@ -780,6 +789,29 @@ public final class ViewTreeObserver {
mOnComputeInternalInsetsListeners.remove(victim);
}
+ /**
+ * @hide
+ */
+ public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
+ checkIsAlive();
+ if (mOnEnterAnimationCompleteListeners == null) {
+ mOnEnterAnimationCompleteListeners =
+ new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>();
+ }
+ mOnEnterAnimationCompleteListeners.add(listener);
+ }
+
+ /**
+ * @hide
+ */
+ public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
+ checkIsAlive();
+ if (mOnEnterAnimationCompleteListeners == null) {
+ return;
+ }
+ mOnEnterAnimationCompleteListeners.remove(listener);
+ }
+
private void checkIsAlive() {
if (!mAlive) {
throw new IllegalStateException("This ViewTreeObserver is not alive, call "
@@ -1022,6 +1054,23 @@ public final class ViewTreeObserver {
}
/**
+ * @hide
+ */
+ public final void dispatchOnEnterAnimationComplete() {
+ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+ // perform the dispatching. The iterator is a safe guard against listeners that
+ // could mutate the list by calling the various add/remove methods. This prevents
+ // the array from being modified while we iterate it.
+ final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
+ mOnEnterAnimationCompleteListeners;
+ if (listeners != null && !listeners.isEmpty()) {
+ for (OnEnterAnimationCompleteListener listener : listeners) {
+ listener.onEnterAnimationComplete();
+ }
+ }
+ }
+
+ /**
* Copy on write array. This array is not thread safe, and only one loop can
* iterate over this array at any given time. This class avoids allocations
* until a concurrent modification happens.
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0076abf..2e5c1e0 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
@@ -155,6 +156,7 @@ public abstract class Window {
"android:navigation:background";
/** The default features enabled */
+ @Deprecated
@SuppressWarnings({"PointlessBitwiseExpression"})
protected static final int DEFAULT_FEATURES = (1 << FEATURE_OPTIONS_PANEL) |
(1 << FEATURE_CONTEXT_MENU);
@@ -183,8 +185,8 @@ public abstract class Window {
private boolean mSetCloseOnTouchOutside = false;
private int mForcedWindowFlags = 0;
- private int mFeatures = DEFAULT_FEATURES;
- private int mLocalFeatures = DEFAULT_FEATURES;
+ private int mFeatures;
+ private int mLocalFeatures;
private boolean mHaveWindowFormat = false;
private boolean mHaveDimAmount = false;
@@ -442,6 +444,7 @@ public abstract class Window {
public Window(Context context) {
mContext = context;
+ mFeatures = mLocalFeatures = getDefaultFeatures(context);
}
/**
@@ -1270,6 +1273,25 @@ public abstract class Window {
}
/**
+ * Return the feature bits set by default on a window.
+ * @param context The context used to access resources
+ */
+ public static int getDefaultFeatures(Context context) {
+ int features = 0;
+
+ final Resources res = context.getResources();
+ if (res.getBoolean(com.android.internal.R.bool.config_defaultWindowFeatureOptionsPanel)) {
+ features |= 1 << FEATURE_OPTIONS_PANEL;
+ }
+
+ if (res.getBoolean(com.android.internal.R.bool.config_defaultWindowFeatureContextMenu)) {
+ features |= 1 << FEATURE_CONTEXT_MENU;
+ }
+
+ return features;
+ }
+
+ /**
* Query for the availability of a certain feature.
*
* @param feature The feature ID to check
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 82b1073..0d82087 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -118,12 +118,13 @@ public final class WindowManagerGlobal {
private Runnable mSystemPropertyUpdater;
- /** Default token to apply to added views. */
- private IBinder mDefaultToken;
-
private WindowManagerGlobal() {
}
+ public static void initialize() {
+ getWindowManagerService();
+ }
+
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
@@ -138,6 +139,12 @@ public final class WindowManagerGlobal {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
+ try {
+ sWindowManagerService = getWindowManagerService();
+ ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
+ }
}
return sWindowManagerService;
}
@@ -157,7 +164,6 @@ public final class WindowManagerGlobal {
}
},
imm.getClient(), imm.getInputContext());
- ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
@@ -172,17 +178,6 @@ public final class WindowManagerGlobal {
}
}
- /**
- * Sets the default token to use in {@link #addView} when no parent window
- * token is available and no token has been explicitly set in the view's
- * layout params.
- *
- * @param token Default window token to apply to added views.
- */
- public void setDefaultToken(IBinder token) {
- mDefaultToken = token;
- }
-
public String[] getViewRootNames() {
synchronized (mLock) {
final int numRoots = mRoots.size();
@@ -230,10 +225,6 @@ public final class WindowManagerGlobal {
}
}
- if (wparams.token == null && mDefaultToken != null) {
- wparams.token = mDefaultToken;
- }
-
ViewRootImpl root;
View panelParentView = null;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 52d79f8..98e9f54 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,9 @@
package android.view;
+import android.annotation.NonNull;
+import android.os.IBinder;
+
/**
* Provides low-level communication with the system window manager for
* operations that are bound to a particular context, display or parent window.
@@ -47,6 +50,8 @@ public final class WindowManagerImpl implements WindowManager {
private final Display mDisplay;
private final Window mParentWindow;
+ private IBinder mDefaultToken;
+
public WindowManagerImpl(Display display) {
this(display, null);
}
@@ -64,16 +69,43 @@ public final class WindowManagerImpl implements WindowManager {
return new WindowManagerImpl(display, mParentWindow);
}
+ /**
+ * Sets the window token to assign when none is specified by the client or
+ * available from the parent window.
+ *
+ * @param token The default token to assign.
+ */
+ public void setDefaultToken(IBinder token) {
+ mDefaultToken = token;
+ }
+
@Override
- public void addView(View view, ViewGroup.LayoutParams params) {
+ public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
+ applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
- public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+ public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
+ applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
+ private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
+ // Only use the default token if we don't have a parent window.
+ if (mDefaultToken != null && mParentWindow == null) {
+ if (!(params instanceof WindowManager.LayoutParams)) {
+ throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
+ }
+
+ // Only use the default token if we don't already have a token.
+ final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
+ if (wparams.token == null) {
+ wparams.token = mDefaultToken;
+ }
+ }
+ }
+
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 20ef646..eca96f9 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -16,18 +16,14 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.net.WebAddress;
/**
* Manages the cookies used by an application's {@link WebView} instances.
* Cookies are manipulated according to RFC2109.
*/
-public class CookieManager {
- /**
- * @hide Only for use by WebViewProvider implementations
- */
- protected CookieManager() {
- }
+public abstract class CookieManager {
@Override
protected Object clone() throws CloneNotSupportedException {
@@ -59,9 +55,7 @@ public class CookieManager {
* @param accept whether {@link WebView} instances should send and accept
* cookies
*/
- public synchronized void setAcceptCookie(boolean accept) {
- throw new MustOverrideException();
- }
+ public abstract void setAcceptCookie(boolean accept);
/**
* Gets whether the application's {@link WebView} instances send and accept
@@ -69,9 +63,7 @@ public class CookieManager {
*
* @return true if {@link WebView} instances send and accept cookies
*/
- public synchronized boolean acceptCookie() {
- throw new MustOverrideException();
- }
+ public abstract boolean acceptCookie();
/**
* Sets whether the {@link WebView} should allow third party cookies to be set.
@@ -87,9 +79,7 @@ public class CookieManager {
* @param accept whether the {@link WebView} instance should accept
* third party cookies
*/
- public void setAcceptThirdPartyCookies(WebView webview, boolean accept) {
- throw new MustOverrideException();
- }
+ public abstract void setAcceptThirdPartyCookies(WebView webview, boolean accept);
/**
* Gets whether the {@link WebView} should allow third party cookies to be set.
@@ -97,9 +87,7 @@ public class CookieManager {
* @param webview the {@link WebView} instance to get the cookie policy for
* @return true if the {@link WebView} accepts third party cookies
*/
- public boolean acceptThirdPartyCookies(WebView webview) {
- throw new MustOverrideException();
- }
+ public abstract boolean acceptThirdPartyCookies(WebView webview);
/**
* Sets a cookie for the given URL. Any existing cookie with the same host,
@@ -110,9 +98,7 @@ public class CookieManager {
* @param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
*/
- public void setCookie(String url, String value) {
- throw new MustOverrideException();
- }
+ public abstract void setCookie(String url, String value);
/**
* Sets a cookie for the given URL. Any existing cookie with the same host,
@@ -133,9 +119,7 @@ public class CookieManager {
* HTTP response header
* @param callback a callback to be executed when the cookie has been set
*/
- public void setCookie(String url, String value, ValueCallback<Boolean> callback) {
- throw new MustOverrideException();
- }
+ public abstract void setCookie(String url, String value, ValueCallback<Boolean> callback);
/**
* Gets the cookies for the given URL.
@@ -144,9 +128,7 @@ public class CookieManager {
* @return value the cookies as a string, using the format of the 'Cookie'
* HTTP request header
*/
- public String getCookie(String url) {
- throw new MustOverrideException();
- }
+ public abstract String getCookie(String url);
/**
* See {@link #getCookie(String)}.
@@ -155,11 +137,10 @@ public class CookieManager {
* @param privateBrowsing whether to use the private browsing cookie jar
* @return value the cookies as a string, using the format of the 'Cookie'
* HTTP request header
- * @hide Used by Browser, no intention to publish.
+ * @hide Used by Browser and by WebViewProvider implementations.
*/
- public String getCookie(String url, boolean privateBrowsing) {
- throw new MustOverrideException();
- }
+ @SystemApi
+ public abstract String getCookie(String url, boolean privateBrowsing);
/**
* Gets cookie(s) for a given uri so that it can be set to "cookie:" in http
@@ -168,10 +149,11 @@ public class CookieManager {
* @param uri the WebAddress for which the cookies are requested
* @return value the cookies as a string, using the format of the 'Cookie'
* HTTP request header
- * @hide Used by RequestHandle, no intention to publish.
+ * @hide Used by RequestHandle and by WebViewProvider implementations.
*/
+ @SystemApi
public synchronized String getCookie(WebAddress uri) {
- throw new MustOverrideException();
+ return getCookie(uri.toString());
}
/**
@@ -179,9 +161,7 @@ public class CookieManager {
* date.
* @deprecated use {@link #removeSessionCookies(ValueCallback)} instead.
*/
- public void removeSessionCookie() {
- throw new MustOverrideException();
- }
+ public abstract void removeSessionCookie();
/**
* Removes all session cookies, which are cookies without an expiration
@@ -197,18 +177,14 @@ public class CookieManager {
* method from a thread without a Looper.
* @param callback a callback which is executed when the session cookies have been removed
*/
- public void removeSessionCookies(ValueCallback<Boolean> callback) {
- throw new MustOverrideException();
- }
+ public abstract void removeSessionCookies(ValueCallback<Boolean> callback);
/**
* Removes all cookies.
* @deprecated Use {@link #removeAllCookies(ValueCallback)} instead.
*/
@Deprecated
- public void removeAllCookie() {
- throw new MustOverrideException();
- }
+ public abstract void removeAllCookie();
/**
* Removes all cookies.
@@ -223,54 +199,37 @@ public class CookieManager {
* method from a thread without a Looper.
* @param callback a callback which is executed when the cookies have been removed
*/
- public void removeAllCookies(ValueCallback<Boolean> callback) {
- throw new MustOverrideException();
- }
+ public abstract void removeAllCookies(ValueCallback<Boolean> callback);
/**
* Gets whether there are stored cookies.
*
* @return true if there are stored cookies
*/
- public synchronized boolean hasCookies() {
- throw new MustOverrideException();
- }
+ public abstract boolean hasCookies();
/**
* See {@link #hasCookies()}.
*
* @param privateBrowsing whether to use the private browsing cookie jar
- * @hide Used by Browser, no intention to publish.
+ * @hide Used by Browser and WebViewProvider implementations.
*/
- public synchronized boolean hasCookies(boolean privateBrowsing) {
- throw new MustOverrideException();
- }
+ @SystemApi
+ public abstract boolean hasCookies(boolean privateBrowsing);
/**
* Removes all expired cookies.
* @deprecated The WebView handles removing expired cookies automatically.
*/
@Deprecated
- public void removeExpiredCookie() {
- throw new MustOverrideException();
- }
+ public abstract void removeExpiredCookie();
/**
* Ensures all cookies currently accessible through the getCookie API are
* written to persistent storage.
* This call will block the caller until it is done and may perform I/O.
*/
- public void flush() {
- flushCookieStore();
- }
-
- /**
- * Flushes all cookies managed by the Chrome HTTP stack to flash.
- *
- * @hide Package level api, called from CookieSyncManager
- */
- protected void flushCookieStore() {
- }
+ public abstract void flush();
/**
* Gets whether the application's {@link WebView} instances send and accept
@@ -289,9 +248,8 @@ public class CookieManager {
*
* @hide Only for use by WebViewProvider implementations
*/
- protected boolean allowFileSchemeCookiesImpl() {
- throw new MustOverrideException();
- }
+ @SystemApi
+ protected abstract boolean allowFileSchemeCookiesImpl();
/**
* Sets whether the application's {@link WebView} instances should send and
@@ -314,7 +272,6 @@ public class CookieManager {
*
* @hide Only for use by WebViewProvider implementations
*/
- protected void setAcceptFileSchemeCookiesImpl(boolean accept) {
- throw new MustOverrideException();
- }
+ @SystemApi
+ protected abstract void setAcceptFileSchemeCookiesImpl(boolean accept);
}
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
deleted file mode 100644
index 7b3cb1b..0000000
--- a/core/java/android/webkit/DebugFlags.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-/**
- * This class is a container for all of the debug flags used in the Java
- * components of webkit. These flags must be final in order to ensure that
- * the compiler optimizes the code that uses them out of the final executable.
- *
- * The name of each flags maps directly to the name of the class in which that
- * flag is used.
- *
- * @hide Only used by WebView implementations.
- */
-public class DebugFlags {
-
- public static final boolean COOKIE_SYNC_MANAGER = false;
- public static final boolean TRACE_API = false;
- public static final boolean TRACE_CALLBACK = false;
- public static final boolean URL_UTIL = false;
- public static final boolean WEB_SYNC_MANAGER = false;
-
-}
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index c68b450..ab6a2f9 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -36,6 +37,7 @@ import android.widget.TextView;
/**
* @hide
*/
+@SystemApi
public class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
View.OnClickListener, WebView.FindListener {
private View mCustomView;
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
index bc3d035..7187f22 100644
--- a/core/java/android/webkit/GeolocationPermissions.java
+++ b/core/java/android/webkit/GeolocationPermissions.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.SystemApi;
+
import java.util.Set;
/**
@@ -136,5 +138,6 @@ public class GeolocationPermissions {
* way to call createHandler() and createUIHandler(), so it would not work).
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public GeolocationPermissions() {}
}
diff --git a/core/java/android/webkit/HttpAuthHandler.java b/core/java/android/webkit/HttpAuthHandler.java
index ee3b369..45fc1f5 100644
--- a/core/java/android/webkit/HttpAuthHandler.java
+++ b/core/java/android/webkit/HttpAuthHandler.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.os.Handler;
/**
@@ -30,6 +31,7 @@ public class HttpAuthHandler extends Handler {
/**
* @hide Only for use by WebViewProvider implementations.
*/
+ @SystemApi
public HttpAuthHandler() {
}
diff --git a/core/java/android/webkit/JsDialogHelper.java b/core/java/android/webkit/JsDialogHelper.java
index bb0339e..cc475c3 100644
--- a/core/java/android/webkit/JsDialogHelper.java
+++ b/core/java/android/webkit/JsDialogHelper.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@@ -34,8 +35,9 @@ import java.net.URL;
* Helper class to create JavaScript dialogs. It is used by
* different WebView implementations.
*
- * @hide Helper class for internal use
+ * @hide
*/
+@SystemApi
public class JsDialogHelper {
private static final String TAG = "JsDialogHelper";
diff --git a/core/java/android/webkit/JsPromptResult.java b/core/java/android/webkit/JsPromptResult.java
index a1bf124..771cc32 100644
--- a/core/java/android/webkit/JsPromptResult.java
+++ b/core/java/android/webkit/JsPromptResult.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.SystemApi;
+
/**
* Public class for handling JavaScript prompt requests. The WebChromeClient will receive a
@@ -39,6 +41,7 @@ public class JsPromptResult extends JsResult {
/**
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public JsPromptResult(ResultReceiver receiver) {
super(receiver);
}
@@ -46,6 +49,7 @@ public class JsPromptResult extends JsResult {
/**
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public String getStringResult() {
return mStringResult;
}
diff --git a/core/java/android/webkit/JsResult.java b/core/java/android/webkit/JsResult.java
index e4e6851..d36ab41 100644
--- a/core/java/android/webkit/JsResult.java
+++ b/core/java/android/webkit/JsResult.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.SystemApi;
+
/**
* An instance of this class is passed as a parameter in various {@link WebChromeClient} action
* notifications. The object is used as a handle onto the underlying JavaScript-originated request,
@@ -27,6 +29,7 @@ public class JsResult {
* notifications when the JavaScript result represented by a JsResult instance has
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public interface ResultReceiver {
public void onJsResultComplete(JsResult result);
}
@@ -54,6 +57,7 @@ public class JsResult {
/**
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public JsResult(ResultReceiver receiver) {
mReceiver = receiver;
}
@@ -61,6 +65,7 @@ public class JsResult {
/**
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public final boolean getResult() {
return mResult;
}
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index af31544..537065d 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.os.Handler;
/**
@@ -30,6 +31,7 @@ public class SslErrorHandler extends Handler {
/**
* @hide Only for use by WebViewProvider implementations.
*/
+ @SystemApi
public SslErrorHandler() {}
/**
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index d115984..f5233b6 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -29,6 +29,7 @@ import android.util.Log;
public final class URLUtil {
private static final String LOGTAG = "webkit";
+ private static final boolean TRACE = false;
// to refer to bar.png under your package's asset/foo/ directory, use
// "file:///android_asset/foo/bar.png".
@@ -49,7 +50,7 @@ public final class URLUtil {
String retVal = inUrl;
WebAddress webAddress;
- if (DebugFlags.URL_UTIL) Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
+ if (TRACE) Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
if (inUrl.length() == 0) return inUrl;
if (inUrl.startsWith("about:")) return inUrl;
@@ -69,7 +70,7 @@ public final class URLUtil {
webAddress = new WebAddress(inUrl);
} catch (ParseException ex) {
- if (DebugFlags.URL_UTIL) {
+ if (TRACE) {
Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
}
return retVal;
@@ -286,7 +287,7 @@ public final class URLUtil {
}
return url;
}
-
+
/**
* Guesses canonical filename that a download would have, using
* the URL and contentDisposition. File extension, if not defined,
@@ -294,7 +295,7 @@ public final class URLUtil {
* @param url Url to the content
* @param contentDisposition Content-Disposition HTTP header or null
* @param mimeType Mime-type of the content or null
- *
+ *
* @return suggested filename
*/
public static final String guessFileName(
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index bfef2e7..e671376 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import java.io.Serializable;
/**
@@ -23,56 +24,38 @@ import java.io.Serializable;
* WebView.copyBackForwardList() will return a copy of this class used to
* inspect the entries in the list.
*/
-public class WebBackForwardList implements Cloneable, Serializable {
-
- /**
- * @hide
- */
- public WebBackForwardList() {
- }
-
+public abstract class WebBackForwardList implements Cloneable, Serializable {
/**
* Return the current history item. This method returns null if the list is
* empty.
* @return The current history item.
*/
- public synchronized WebHistoryItem getCurrentItem() {
- throw new MustOverrideException();
- }
+ public abstract WebHistoryItem getCurrentItem();
/**
* Get the index of the current history item. This index can be used to
* directly index into the array list.
* @return The current index from 0...n or -1 if the list is empty.
*/
- public synchronized int getCurrentIndex() {
- throw new MustOverrideException();
- }
+ public abstract int getCurrentIndex();
/**
* Get the history item at the given index. The index range is from 0...n
* where 0 is the first item and n is the last item.
* @param index The index to retrieve.
*/
- public synchronized WebHistoryItem getItemAtIndex(int index) {
- throw new MustOverrideException();
- }
+ public abstract WebHistoryItem getItemAtIndex(int index);
/**
* Get the total size of the back/forward list.
* @return The size of the list.
*/
- public synchronized int getSize() {
- throw new MustOverrideException();
- }
+ public abstract int getSize();
/**
* Clone the entire object to be used in the UI thread by clients of
* WebView. This creates a copy that should never be modified by any of the
* webkit package classes.
*/
- protected synchronized WebBackForwardList clone() {
- throw new MustOverrideException();
- }
-
+ protected abstract WebBackForwardList clone();
}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 46a7fd0..768dc9f 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
@@ -509,6 +510,7 @@ public class WebChromeClient {
* @deprecated Use {@link #showFileChooser} instead.
* @hide This method was not published in any SDK version.
*/
+ @SystemApi
@Deprecated
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index 9a588e4..569fccd 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.graphics.Bitmap;
/**
@@ -24,14 +25,7 @@ import android.graphics.Bitmap;
* item. Each history item may be updated during the load of a page.
* @see WebBackForwardList
*/
-public class WebHistoryItem implements Cloneable {
-
- /**
- * @hide
- */
- public WebHistoryItem() {
- }
-
+public abstract class WebHistoryItem implements Cloneable {
/**
* Return an identifier for this history item. If an item is a copy of
* another item, the identifiers will be the same even if they are not the
@@ -40,10 +34,9 @@ public class WebHistoryItem implements Cloneable {
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SystemApi
@Deprecated
- public int getId() {
- throw new MustOverrideException();
- }
+ public abstract int getId();
/**
* Return the url of this history item. The url is the base url of this
@@ -53,29 +46,23 @@ public class WebHistoryItem implements Cloneable {
* Note: The VM ensures 32-bit atomic read/write operations so we don't have
* to synchronize this method.
*/
- public String getUrl() {
- throw new MustOverrideException();
- }
+ public abstract String getUrl();
/**
* Return the original url of this history item. This was the requested
- * url, the final url may be different as there might have been
+ * url, the final url may be different as there might have been
* redirects while loading the site.
* @return The original url of this history item.
*/
- public String getOriginalUrl() {
- throw new MustOverrideException();
- }
-
+ public abstract String getOriginalUrl();
+
/**
* Return the document title of this history item.
* @return The document title of this history item.
* Note: The VM ensures 32-bit atomic read/write operations so we don't have
* to synchronize this method.
*/
- public String getTitle() {
- throw new MustOverrideException();
- }
+ public abstract String getTitle();
/**
* Return the favicon of this history item or null if no favicon was found.
@@ -83,15 +70,10 @@ public class WebHistoryItem implements Cloneable {
* Note: The VM ensures 32-bit atomic read/write operations so we don't have
* to synchronize this method.
*/
- public Bitmap getFavicon() {
- throw new MustOverrideException();
- }
+ public abstract Bitmap getFavicon();
/**
* Clone the history item for use by clients of WebView.
*/
- protected synchronized WebHistoryItem clone() {
- throw new MustOverrideException();
- }
-
+ protected abstract WebHistoryItem clone();
}
diff --git a/core/java/android/webkit/WebIconDatabase.java b/core/java/android/webkit/WebIconDatabase.java
index e574593..08956e0 100644
--- a/core/java/android/webkit/WebIconDatabase.java
+++ b/core/java/android/webkit/WebIconDatabase.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.content.ContentResolver;
import android.graphics.Bitmap;
@@ -32,7 +33,7 @@ import android.graphics.Bitmap;
* up to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
-public class WebIconDatabase {
+public abstract class WebIconDatabase {
/**
* Interface for receiving icons from the database.
* @deprecated This interface is obsolete.
@@ -52,23 +53,17 @@ public class WebIconDatabase {
* Open a the icon database and store the icons in the given path.
* @param path The directory path where the icon database will be stored.
*/
- public void open(String path) {
- throw new MustOverrideException();
- }
+ public abstract void open(String path);
/**
* Close the shared instance of the icon database.
*/
- public void close() {
- throw new MustOverrideException();
- }
+ public abstract void close();
/**
* Removes all the icons in the database.
*/
- public void removeAllIcons() {
- throw new MustOverrideException();
- }
+ public abstract void removeAllIcons();
/**
* Request the Bitmap representing the icon for the given page
@@ -76,32 +71,25 @@ public class WebIconDatabase {
* @param url The page's url.
* @param listener An implementation on IconListener to receive the result.
*/
- public void requestIconForPageUrl(String url, IconListener listener) {
- throw new MustOverrideException();
- }
+ public abstract void requestIconForPageUrl(String url, IconListener listener);
/** {@hide}
*/
- public void bulkRequestIconForPageUrl(ContentResolver cr, String where,
- IconListener listener) {
- throw new MustOverrideException();
- }
+ @SystemApi
+ public abstract void bulkRequestIconForPageUrl(ContentResolver cr, String where,
+ IconListener listener);
/**
* Retain the icon for the given page url.
* @param url The page's url.
*/
- public void retainIconForPageUrl(String url) {
- throw new MustOverrideException();
- }
+ public abstract void retainIconForPageUrl(String url);
/**
* Release the icon for the given page url.
* @param url The page's url.
*/
- public void releaseIconForPageUrl(String url) {
- throw new MustOverrideException();
- }
+ public abstract void releaseIconForPageUrl(String url);
/**
* Get the global instance of WebIconDatabase.
@@ -113,9 +101,4 @@ public class WebIconDatabase {
// XXX: Must be created in the UI thread.
return WebViewFactory.getProvider().getWebIconDatabase();
}
-
- /**
- * @hide Only for use by WebViewProvider implementations
- */
- protected WebIconDatabase() {}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 7cf3cb5..1d2c311 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -204,25 +204,15 @@ public abstract class WebSettings {
public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2;
/**
- * Hidden constructor to prevent clients from creating a new settings
- * instance or deriving the class.
- *
- * @hide
- */
- protected WebSettings() {
- }
-
- /**
* Enables dumping the pages navigation cache to a text file. The default
* is false.
*
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SystemApi
@Deprecated
- public void setNavDump(boolean enabled) {
- throw new MustOverrideException();
- }
+ public abstract void setNavDump(boolean enabled);
/**
* Gets whether dumping the navigation cache is enabled.
@@ -232,10 +222,9 @@ public abstract class WebSettings {
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SystemApi
@Deprecated
- public boolean getNavDump() {
- throw new MustOverrideException();
- }
+ public abstract boolean getNavDump();
/**
* Sets whether the WebView should support zooming using its on-screen zoom
@@ -246,9 +235,7 @@ public abstract class WebSettings {
*
* @param support whether the WebView should support zoom
*/
- public void setSupportZoom(boolean support) {
- throw new MustOverrideException();
- }
+ public abstract void setSupportZoom(boolean support);
/**
* Gets whether the WebView supports zoom.
@@ -256,9 +243,7 @@ public abstract class WebSettings {
* @return true if the WebView supports zoom
* @see #setSupportZoom
*/
- public boolean supportZoom() {
- throw new MustOverrideException();
- }
+ public abstract boolean supportZoom();
/**
* Sets whether the WebView requires a user gesture to play media.
@@ -266,9 +251,7 @@ public abstract class WebSettings {
*
* @param require whether the WebView requires a user gesture to play media
*/
- public void setMediaPlaybackRequiresUserGesture(boolean require) {
- throw new MustOverrideException();
- }
+ public abstract void setMediaPlaybackRequiresUserGesture(boolean require);
/**
* Gets whether the WebView requires a user gesture to play media.
@@ -276,9 +259,7 @@ public abstract class WebSettings {
* @return true if the WebView requires a user gesture to play media
* @see #setMediaPlaybackRequiresUserGesture
*/
- public boolean getMediaPlaybackRequiresUserGesture() {
- throw new MustOverrideException();
- }
+ public abstract boolean getMediaPlaybackRequiresUserGesture();
/**
* Sets whether the WebView should use its built-in zoom mechanisms. The
@@ -295,9 +276,7 @@ public abstract class WebSettings {
// This method was intended to select between the built-in zoom mechanisms
// and the separate zoom controls. The latter were obtained using
// {@link WebView#getZoomControls}, which is now hidden.
- public void setBuiltInZoomControls(boolean enabled) {
- throw new MustOverrideException();
- }
+ public abstract void setBuiltInZoomControls(boolean enabled);
/**
* Gets whether the zoom mechanisms built into WebView are being used.
@@ -305,9 +284,7 @@ public abstract class WebSettings {
* @return true if the zoom mechanisms built into WebView are being used
* @see #setBuiltInZoomControls
*/
- public boolean getBuiltInZoomControls() {
- throw new MustOverrideException();
- }
+ public abstract boolean getBuiltInZoomControls();
/**
* Sets whether the WebView should display on-screen zoom controls when
@@ -316,9 +293,7 @@ public abstract class WebSettings {
*
* @param enabled whether the WebView should display on-screen zoom controls
*/
- public void setDisplayZoomControls(boolean enabled) {
- throw new MustOverrideException();
- }
+ public abstract void setDisplayZoomControls(boolean enabled);
/**
* Gets whether the WebView displays on-screen zoom controls when using
@@ -328,9 +303,7 @@ public abstract class WebSettings {
* the built-in zoom mechanisms
* @see #setDisplayZoomControls
*/
- public boolean getDisplayZoomControls() {
- throw new MustOverrideException();
- }
+ public abstract boolean getDisplayZoomControls();
/**
* Enables or disables file access within WebView. File access is enabled by
@@ -338,36 +311,28 @@ public abstract class WebSettings {
* Assets and resources are still accessible using file:///android_asset and
* file:///android_res.
*/
- public void setAllowFileAccess(boolean allow) {
- throw new MustOverrideException();
- }
+ public abstract void setAllowFileAccess(boolean allow);
/**
* Gets whether this WebView supports file access.
*
* @see #setAllowFileAccess
*/
- public boolean getAllowFileAccess() {
- throw new MustOverrideException();
- }
+ public abstract boolean getAllowFileAccess();
/**
* Enables or disables content URL access within WebView. Content URL
* access allows WebView to load content from a content provider installed
* in the system. The default is enabled.
*/
- public void setAllowContentAccess(boolean allow) {
- throw new MustOverrideException();
- }
+ public abstract void setAllowContentAccess(boolean allow);
/**
* Gets whether this WebView supports content URL access.
*
* @see #setAllowContentAccess
*/
- public boolean getAllowContentAccess() {
- throw new MustOverrideException();
- }
+ public abstract boolean getAllowContentAccess();
/**
* Sets whether the WebView loads pages in overview mode, that is,
@@ -376,9 +341,7 @@ public abstract class WebSettings {
* of the WebView control, for example, when {@link #getUseWideViewPort}
* is enabled. The default is false.
*/
- public void setLoadWithOverviewMode(boolean overview) {
- throw new MustOverrideException();
- }
+ public abstract void setLoadWithOverviewMode(boolean overview);
/**
* Gets whether this WebView loads pages in overview mode.
@@ -386,9 +349,7 @@ public abstract class WebSettings {
* @return whether this WebView loads pages in overview mode
* @see #setLoadWithOverviewMode
*/
- public boolean getLoadWithOverviewMode() {
- throw new MustOverrideException();
- }
+ public abstract boolean getLoadWithOverviewMode();
/**
* Sets whether the WebView will enable smooth transition while panning or
@@ -400,9 +361,7 @@ public abstract class WebSettings {
* @deprecated This method is now obsolete, and will become a no-op in future.
*/
@Deprecated
- public void setEnableSmoothTransition(boolean enable) {
- throw new MustOverrideException();
- }
+ public abstract void setEnableSmoothTransition(boolean enable);
/**
* Gets whether the WebView enables smooth transition while panning or
@@ -413,9 +372,7 @@ public abstract class WebSettings {
* @deprecated This method is now obsolete, and will become a no-op in future.
*/
@Deprecated
- public boolean enableSmoothTransition() {
- throw new MustOverrideException();
- }
+ public abstract boolean enableSmoothTransition();
/**
* Sets whether the WebView uses its background for over scroll background.
@@ -425,10 +382,9 @@ public abstract class WebSettings {
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SystemApi
@Deprecated
- public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
- throw new MustOverrideException();
- }
+ public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean view);
/**
* Gets whether this WebView uses WebView's background instead of
@@ -438,17 +394,14 @@ public abstract class WebSettings {
* @deprecated This method is now obsolete.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SystemApi
@Deprecated
- public boolean getUseWebViewBackgroundForOverscrollBackground() {
- throw new MustOverrideException();
- }
+ public abstract boolean getUseWebViewBackgroundForOverscrollBackground();
/**
* Sets whether the WebView should save form data. The default is true.
*/
- public void setSaveFormData(boolean save) {
- throw new MustOverrideException();
- }
+ public abstract void setSaveFormData(boolean save);
/**
* Gets whether the WebView saves form data.
@@ -456,18 +409,14 @@ public abstract class WebSettings {
* @return whether the WebView saves form data
* @see #setSaveFormData
*/
- public boolean getSaveFormData() {
- throw new MustOverrideException();
- }
+ public abstract boolean getSaveFormData();
/**
* Sets whether the WebView should save passwords. The default is true.
* @deprecated Saving passwords in WebView will not be supported in future versions.
*/
@Deprecated
- public void setSavePassword(boolean save) {
- throw new MustOverrideException();
- }
+ public abstract void setSavePassword(boolean save);
/**
* Gets whether the WebView saves passwords.
@@ -477,18 +426,14 @@ public abstract class WebSettings {
* @deprecated Saving passwords in WebView will not be supported in future versions.
*/
@Deprecated
- public boolean getSavePassword() {
- throw new MustOverrideException();
- }
+ public abstract boolean getSavePassword();
/**
* Sets the text zoom of the page in percent. The default is 100.
*
* @param textZoom the text zoom in percent
*/
- public synchronized void setTextZoom(int textZoom) {
- throw new MustOverrideException();
- }
+ public abstract void setTextZoom(int textZoom);
/**
* Gets the text zoom of the page in percent.
@@ -496,27 +441,23 @@ public abstract class WebSettings {
* @return the text zoom of the page in percent
* @see #setTextZoom
*/
- public synchronized int getTextZoom() {
- throw new MustOverrideException();
- }
+ public abstract int getTextZoom();
/**
* Sets policy for third party cookies.
* Developers should access this via {@link CookieManager#setShouldAcceptThirdPartyCookies}.
* @hide Internal API.
*/
- public void setAcceptThirdPartyCookies(boolean accept) {
- throw new MustOverrideException();
- }
+ @SystemApi
+ public abstract void setAcceptThirdPartyCookies(boolean accept);
/**
* Gets policy for third party cookies.
* Developers should access this via {@link CookieManager#getShouldAcceptThirdPartyCookies}.
* @hide Internal API
*/
- public boolean getAcceptThirdPartyCookies() {
- throw new MustOverrideException();
- }
+ @SystemApi
+ public abstract boolean getAcceptThirdPartyCookies();
/**
* Sets the text size of the page. The default is {@link TextSize#NORMAL}.
@@ -569,9 +510,7 @@ public abstract class WebSettings {
* recommended alternatives.
*/
@Deprecated
- public void setDefaultZoom(ZoomDensity zoom) {
- throw new MustOverrideException();
- }
+ public abstract void setDefaultZoom(ZoomDensity zoom);
/**
* Gets the default zoom density of the page. This should be called from
@@ -583,9 +522,7 @@ public abstract class WebSettings {
* @see #setDefaultZoom
* @deprecated Will only return the default value.
*/
- public ZoomDensity getDefaultZoom() {
- throw new MustOverrideException();
- }
+ public abstract ZoomDensity getDefaultZoom();
/**
* Enables using light touches to make a selection and activate mouseovers.
@@ -593,9 +530,7 @@ public abstract class WebSettings {
* setting is obsolete and has no effect.
*/
@Deprecated
- public void setLightTouchEnabled(boolean enabled) {
- throw new MustOverrideException();
- }
+ public abstract void setLightTouchEnabled(boolean enabled);
/**
* Gets whether light touches are enabled.
@@ -603,9 +538,7 @@ public abstract class WebSettings {
* @deprecated This setting is obsolete.
*/
@Deprecated
- public boolean getLightTouchEnabled() {
- throw new MustOverrideException();
- }
+ public abstract boolean getLightTouchEnabled();
/**
* Controlled a rendering optimization that is no longer present. Setting
@@ -615,7 +548,7 @@ public abstract class WebSettings {
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
@Deprecated
- public synchronized void setUseDoubleTree(boolean use) {
+ public void setUseDoubleTree(boolean use) {
// Specified to do nothing, so no need for derived classes to override.
}
@@ -627,7 +560,7 @@ public abstract class WebSettings {
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
@Deprecated
- public synchronized boolean getUseDoubleTree() {
+ public boolean getUseDoubleTree() {
// Returns false unconditionally, so no need for derived classes to override.
return false;
}
@@ -645,10 +578,9 @@ public abstract class WebSettings {
* @deprecated Please use {@link #setUserAgentString} instead.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SystemApi
@Deprecated
- public synchronized void setUserAgent(int ua) {
- throw new MustOverrideException();
- }
+ public abstract void setUserAgent(int ua);
/**
* Gets the user-agent as an integer code.
@@ -664,10 +596,9 @@ public abstract class WebSettings {
* @deprecated Please use {@link #getUserAgentString} instead.
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
+ @SystemApi
@Deprecated
- public synchronized int getUserAgent() {
- throw new MustOverrideException();
- }
+ public abstract int getUserAgent();
/**
* Sets whether the WebView should enable support for the &quot;viewport&quot;
@@ -680,9 +611,7 @@ public abstract class WebSettings {
*
* @param use whether to enable support for the viewport meta tag
*/
- public synchronized void setUseWideViewPort(boolean use) {
- throw new MustOverrideException();
- }
+ public abstract void setUseWideViewPort(boolean use);
/**
* Gets whether the WebView supports the &quot;viewport&quot;
@@ -691,9 +620,7 @@ public abstract class WebSettings {
* @return true if the WebView supports the viewport meta tag
* @see #setUseWideViewPort
*/
- public synchronized boolean getUseWideViewPort() {
- throw new MustOverrideException();
- }
+ public abstract boolean getUseWideViewPort();
/**
* Sets whether the WebView whether supports multiple windows. If set to
@@ -702,9 +629,7 @@ public abstract class WebSettings {
*
* @param support whether to suport multiple windows
*/
- public synchronized void setSupportMultipleWindows(boolean support) {
- throw new MustOverrideException();
- }
+ public abstract void setSupportMultipleWindows(boolean support);
/**
* Gets whether the WebView supports multiple windows.
@@ -712,9 +637,7 @@ public abstract class WebSettings {
* @return true if the WebView supports multiple windows
* @see #setSupportMultipleWindows
*/
- public synchronized boolean supportMultipleWindows() {
- throw new MustOverrideException();
- }
+ public abstract boolean supportMultipleWindows();
/**
* Sets the underlying layout algorithm. This will cause a relayout of the
@@ -722,9 +645,7 @@ public abstract class WebSettings {
*
* @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value
*/
- public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
- throw new MustOverrideException();
- }
+ public abstract void setLayoutAlgorithm(LayoutAlgorithm l);
/**
* Gets the current layout algorithm.
@@ -732,18 +653,14 @@ public abstract class WebSettings {
* @return the layout algorithm in use, as a {@link LayoutAlgorithm} value
* @see #setLayoutAlgorithm
*/
- public synchronized LayoutAlgorithm getLayoutAlgorithm() {
- throw new MustOverrideException();
- }
+ public abstract LayoutAlgorithm getLayoutAlgorithm();
/**
* Sets the standard font family name. The default is "sans-serif".
*
* @param font a font family name
*/
- public synchronized void setStandardFontFamily(String font) {
- throw new MustOverrideException();
- }
+ public abstract void setStandardFontFamily(String font);
/**
* Gets the standard font family name.
@@ -751,18 +668,14 @@ public abstract class WebSettings {
* @return the standard font family name as a string
* @see #setStandardFontFamily
*/
- public synchronized String getStandardFontFamily() {
- throw new MustOverrideException();
- }
+ public abstract String getStandardFontFamily();
/**
* Sets the fixed font family name. The default is "monospace".
*
* @param font a font family name
*/
- public synchronized void setFixedFontFamily(String font) {
- throw new MustOverrideException();
- }
+ public abstract void setFixedFontFamily(String font);
/**
* Gets the fixed font family name.
@@ -770,18 +683,14 @@ public abstract class WebSettings {
* @return the fixed font family name as a string
* @see #setFixedFontFamily
*/
- public synchronized String getFixedFontFamily() {
- throw new MustOverrideException();
- }
+ public abstract String getFixedFontFamily();
/**
* Sets the sans-serif font family name. The default is "sans-serif".
*
* @param font a font family name
*/
- public synchronized void setSansSerifFontFamily(String font) {
- throw new MustOverrideException();
- }
+ public abstract void setSansSerifFontFamily(String font);
/**
* Gets the sans-serif font family name.
@@ -789,18 +698,14 @@ public abstract class WebSettings {
* @return the sans-serif font family name as a string
* @see #setSansSerifFontFamily
*/
- public synchronized String getSansSerifFontFamily() {
- throw new MustOverrideException();
- }
+ public abstract String getSansSerifFontFamily();
/**
* Sets the serif font family name. The default is "sans-serif".
*
* @param font a font family name
*/
- public synchronized void setSerifFontFamily(String font) {
- throw new MustOverrideException();
- }
+ public abstract void setSerifFontFamily(String font);
/**
* Gets the serif font family name. The default is "serif".
@@ -808,18 +713,14 @@ public abstract class WebSettings {
* @return the serif font family name as a string
* @see #setSerifFontFamily
*/
- public synchronized String getSerifFontFamily() {
- throw new MustOverrideException();
- }
+ public abstract String getSerifFontFamily();
/**
* Sets the cursive font family name. The default is "cursive".
*
* @param font a font family name
*/
- public synchronized void setCursiveFontFamily(String font) {
- throw new MustOverrideException();
- }
+ public abstract void setCursiveFontFamily(String font);
/**
* Gets the cursive font family name.
@@ -827,18 +728,14 @@ public abstract class WebSettings {
* @return the cursive font family name as a string
* @see #setCursiveFontFamily
*/
- public synchronized String getCursiveFontFamily() {
- throw new MustOverrideException();
- }
+ public abstract String getCursiveFontFamily();
/**
* Sets the fantasy font family name. The default is "fantasy".
*
* @param font a font family name
*/
- public synchronized void setFantasyFontFamily(String font) {
- throw new MustOverrideException();
- }
+ public abstract void setFantasyFontFamily(String font);
/**
* Gets the fantasy font family name.
@@ -846,9 +743,7 @@ public abstract class WebSettings {
* @return the fantasy font family name as a string
* @see #setFantasyFontFamily
*/
- public synchronized String getFantasyFontFamily() {
- throw new MustOverrideException();
- }
+ public abstract String getFantasyFontFamily();
/**
* Sets the minimum font size. The default is 8.
@@ -856,9 +751,7 @@ public abstract class WebSettings {
* @param size a non-negative integer between 1 and 72. Any number outside
* the specified range will be pinned.
*/
- public synchronized void setMinimumFontSize(int size) {
- throw new MustOverrideException();
- }
+ public abstract void setMinimumFontSize(int size);
/**
* Gets the minimum font size.
@@ -866,9 +759,7 @@ public abstract class WebSettings {
* @return a non-negative integer between 1 and 72
* @see #setMinimumFontSize
*/
- public synchronized int getMinimumFontSize() {
- throw new MustOverrideException();
- }
+ public abstract int getMinimumFontSize();
/**
* Sets the minimum logical font size. The default is 8.
@@ -876,9 +767,7 @@ public abstract class WebSettings {
* @param size a non-negative integer between 1 and 72. Any number outside
* the specified range will be pinned.
*/
- public synchronized void setMinimumLogicalFontSize(int size) {
- throw new MustOverrideException();
- }
+ public abstract void setMinimumLogicalFontSize(int size);
/**
* Gets the minimum logical font size.
@@ -886,9 +775,7 @@ public abstract class WebSettings {
* @return a non-negative integer between 1 and 72
* @see #setMinimumLogicalFontSize
*/
- public synchronized int getMinimumLogicalFontSize() {
- throw new MustOverrideException();
- }
+ public abstract int getMinimumLogicalFontSize();
/**
* Sets the default font size. The default is 16.
@@ -896,9 +783,7 @@ public abstract class WebSettings {
* @param size a non-negative integer between 1 and 72. Any number outside
* the specified range will be pinned.
*/
- public synchronized void setDefaultFontSize(int size) {
- throw new MustOverrideException();
- }
+ public abstract void setDefaultFontSize(int size);
/**
* Gets the default font size.
@@ -906,9 +791,7 @@ public abstract class WebSettings {
* @return a non-negative integer between 1 and 72
* @see #setDefaultFontSize
*/
- public synchronized int getDefaultFontSize() {
- throw new MustOverrideException();
- }
+ public abstract int getDefaultFontSize();
/**
* Sets the default fixed font size. The default is 16.
@@ -916,9 +799,7 @@ public abstract class WebSettings {
* @param size a non-negative integer between 1 and 72. Any number outside
* the specified range will be pinned.
*/
- public synchronized void setDefaultFixedFontSize(int size) {
- throw new MustOverrideException();
- }
+ public abstract void setDefaultFixedFontSize(int size);
/**
* Gets the default fixed font size.
@@ -926,9 +807,7 @@ public abstract class WebSettings {
* @return a non-negative integer between 1 and 72
* @see #setDefaultFixedFontSize
*/
- public synchronized int getDefaultFixedFontSize() {
- throw new MustOverrideException();
- }
+ public abstract int getDefaultFixedFontSize();
/**
* Sets whether the WebView should load image resources. Note that this method
@@ -941,9 +820,7 @@ public abstract class WebSettings {
*
* @param flag whether the WebView should load image resources
*/
- public synchronized void setLoadsImagesAutomatically(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setLoadsImagesAutomatically(boolean flag);
/**
* Gets whether the WebView loads image resources. This includes
@@ -952,9 +829,7 @@ public abstract class WebSettings {
* @return true if the WebView loads image resources
* @see #setLoadsImagesAutomatically
*/
- public synchronized boolean getLoadsImagesAutomatically() {
- throw new MustOverrideException();
- }
+ public abstract boolean getLoadsImagesAutomatically();
/**
* Sets whether the WebView should not load image resources from the
@@ -971,9 +846,7 @@ public abstract class WebSettings {
* network
* @see #setBlockNetworkLoads
*/
- public synchronized void setBlockNetworkImage(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setBlockNetworkImage(boolean flag);
/**
* Gets whether the WebView does not load image resources from the network.
@@ -981,9 +854,7 @@ public abstract class WebSettings {
* @return true if the WebView does not load image resources from the network
* @see #setBlockNetworkImage
*/
- public synchronized boolean getBlockNetworkImage() {
- throw new MustOverrideException();
- }
+ public abstract boolean getBlockNetworkImage();
/**
* Sets whether the WebView should not load resources from the network.
@@ -1003,9 +874,7 @@ public abstract class WebSettings {
* network
* @see android.webkit.WebView#reload
*/
- public synchronized void setBlockNetworkLoads(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setBlockNetworkLoads(boolean flag);
/**
* Gets whether the WebView does not load any resources from the network.
@@ -1013,9 +882,7 @@ public abstract class WebSettings {
* @return true if the WebView does not load any resources from the network
* @see #setBlockNetworkLoads
*/
- public synchronized boolean getBlockNetworkLoads() {
- throw new MustOverrideException();
- }
+ public abstract boolean getBlockNetworkLoads();
/**
* Tells the WebView to enable JavaScript execution.
@@ -1023,9 +890,7 @@ public abstract class WebSettings {
*
* @param flag true if the WebView should execute JavaScript
*/
- public synchronized void setJavaScriptEnabled(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setJavaScriptEnabled(boolean flag);
/**
* Sets whether JavaScript running in the context of a file scheme URL
@@ -1076,10 +941,9 @@ public abstract class WebSettings {
* {@link #setPluginState}
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
+ @SystemApi
@Deprecated
- public synchronized void setPluginsEnabled(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setPluginsEnabled(boolean flag);
/**
* Tells the WebView to enable, disable, or have plugins on demand. On
@@ -1092,9 +956,7 @@ public abstract class WebSettings {
* @deprecated Plugins will not be supported in future, and should not be used.
*/
@Deprecated
- public synchronized void setPluginState(PluginState state) {
- throw new MustOverrideException();
- }
+ public abstract void setPluginState(PluginState state);
/**
* Sets a custom path to plugins used by the WebView. This method is
@@ -1106,7 +968,7 @@ public abstract class WebSettings {
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
- public synchronized void setPluginsPath(String pluginsPath) {
+ public void setPluginsPath(String pluginsPath) {
// Specified to do nothing, so no need for derived classes to override.
}
@@ -1125,9 +987,7 @@ public abstract class WebSettings {
// Note that the WebCore Database Tracker only allows the path to be set
// once.
@Deprecated
- public synchronized void setDatabasePath(String databasePath) {
- throw new MustOverrideException();
- }
+ public abstract void setDatabasePath(String databasePath);
/**
* Sets the path where the Geolocation databases should be saved. In order
@@ -1138,9 +998,7 @@ public abstract class WebSettings {
* saved.
*/
// This will update WebCore when the Sync runs in the C++ side.
- public synchronized void setGeolocationDatabasePath(String databasePath) {
- throw new MustOverrideException();
- }
+ public abstract void setGeolocationDatabasePath(String databasePath);
/**
* Sets whether the Application Caches API should be enabled. The default
@@ -1150,9 +1008,7 @@ public abstract class WebSettings {
*
* @param flag true if the WebView should enable Application Caches
*/
- public synchronized void setAppCacheEnabled(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setAppCacheEnabled(boolean flag);
/**
* Sets the path to the Application Caches files. In order for the
@@ -1164,9 +1020,7 @@ public abstract class WebSettings {
* Application Caches files.
* @see #setAppCacheEnabled
*/
- public synchronized void setAppCachePath(String appCachePath) {
- throw new MustOverrideException();
- }
+ public abstract void setAppCachePath(String appCachePath);
/**
* Sets the maximum size for the Application Cache content. The passed size
@@ -1180,9 +1034,7 @@ public abstract class WebSettings {
* @deprecated In future quota will be managed automatically.
*/
@Deprecated
- public synchronized void setAppCacheMaxSize(long appCacheMaxSize) {
- throw new MustOverrideException();
- }
+ public abstract void setAppCacheMaxSize(long appCacheMaxSize);
/**
* Sets whether the database storage API is enabled. The default value is
@@ -1196,18 +1048,14 @@ public abstract class WebSettings {
*
* @param flag true if the WebView should use the database storage API
*/
- public synchronized void setDatabaseEnabled(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setDatabaseEnabled(boolean flag);
/**
* Sets whether the DOM storage API is enabled. The default value is false.
*
* @param flag true if the WebView should use the DOM storage API
*/
- public synchronized void setDomStorageEnabled(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setDomStorageEnabled(boolean flag);
/**
* Gets whether the DOM Storage APIs are enabled.
@@ -1215,9 +1063,8 @@ public abstract class WebSettings {
* @return true if the DOM Storage APIs are enabled
* @see #setDomStorageEnabled
*/
- public synchronized boolean getDomStorageEnabled() {
- throw new MustOverrideException();
- }
+ public abstract boolean getDomStorageEnabled();
+
/**
* Gets the path to where database storage API databases are saved.
*
@@ -1226,9 +1073,7 @@ public abstract class WebSettings {
* @deprecated Database paths are managed by the implementation this method is obsolete.
*/
@Deprecated
- public synchronized String getDatabasePath() {
- throw new MustOverrideException();
- }
+ public abstract String getDatabasePath();
/**
* Gets whether the database storage API is enabled.
@@ -1236,9 +1081,7 @@ public abstract class WebSettings {
* @return true if the database storage API is enabled
* @see #setDatabaseEnabled
*/
- public synchronized boolean getDatabaseEnabled() {
- throw new MustOverrideException();
- }
+ public abstract boolean getDatabaseEnabled();
/**
* Sets whether Geolocation is enabled. The default is true.
@@ -1260,9 +1103,7 @@ public abstract class WebSettings {
*
* @param flag whether Geolocation should be enabled
*/
- public synchronized void setGeolocationEnabled(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setGeolocationEnabled(boolean flag);
/**
* Gets whether JavaScript is enabled.
@@ -1270,9 +1111,7 @@ public abstract class WebSettings {
* @return true if JavaScript is enabled
* @see #setJavaScriptEnabled
*/
- public synchronized boolean getJavaScriptEnabled() {
- throw new MustOverrideException();
- }
+ public abstract boolean getJavaScriptEnabled();
/**
* Gets whether JavaScript running in the context of a file scheme URL can
@@ -1303,10 +1142,9 @@ public abstract class WebSettings {
* @deprecated This method has been replaced by {@link #getPluginState}
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
+ @SystemApi
@Deprecated
- public synchronized boolean getPluginsEnabled() {
- throw new MustOverrideException();
- }
+ public abstract boolean getPluginsEnabled();
/**
* Gets the current state regarding whether plugins are enabled.
@@ -1316,9 +1154,7 @@ public abstract class WebSettings {
* @deprecated Plugins will not be supported in future, and should not be used.
*/
@Deprecated
- public synchronized PluginState getPluginState() {
- throw new MustOverrideException();
- }
+ public abstract PluginState getPluginState();
/**
* Gets the directory that contains the plugin libraries. This method is
@@ -1330,7 +1166,7 @@ public abstract class WebSettings {
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
- public synchronized String getPluginsPath() {
+ public String getPluginsPath() {
// Unconditionally returns empty string, so no need for derived classes to override.
return "";
}
@@ -1341,9 +1177,7 @@ public abstract class WebSettings {
*
* @param flag true if JavaScript can open windows automatically
*/
- public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean flag);
/**
* Gets whether JavaScript can open windows automatically.
@@ -1352,9 +1186,7 @@ public abstract class WebSettings {
* window.open()
* @see #setJavaScriptCanOpenWindowsAutomatically
*/
- public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() {
- throw new MustOverrideException();
- }
+ public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
/**
* Sets the default text encoding name to use when decoding html pages.
@@ -1362,9 +1194,7 @@ public abstract class WebSettings {
*
* @param encoding the text encoding name
*/
- public synchronized void setDefaultTextEncodingName(String encoding) {
- throw new MustOverrideException();
- }
+ public abstract void setDefaultTextEncodingName(String encoding);
/**
* Gets the default text encoding name.
@@ -1372,17 +1202,13 @@ public abstract class WebSettings {
* @return the default text encoding name as a string
* @see #setDefaultTextEncodingName
*/
- public synchronized String getDefaultTextEncodingName() {
- throw new MustOverrideException();
- }
+ public abstract String getDefaultTextEncodingName();
/**
* Sets the WebView's user-agent string. If the string is null or empty,
* the system default value will be used.
*/
- public synchronized void setUserAgentString(String ua) {
- throw new MustOverrideException();
- }
+ public abstract void setUserAgentString(String ua);
/**
* Gets the WebView's user-agent string.
@@ -1390,9 +1216,7 @@ public abstract class WebSettings {
* @return the WebView's user-agent string
* @see #setUserAgentString
*/
- public synchronized String getUserAgentString() {
- throw new MustOverrideException();
- }
+ public abstract String getUserAgentString();
/**
* Returns the default User-Agent used by a WebView.
@@ -1412,9 +1236,7 @@ public abstract class WebSettings {
*
* @param flag whether the WebView needs to set a node
*/
- public void setNeedInitialFocus(boolean flag) {
- throw new MustOverrideException();
- }
+ public abstract void setNeedInitialFocus(boolean flag);
/**
* Sets the priority of the Render thread. Unlike the other settings, this
@@ -1426,9 +1248,7 @@ public abstract class WebSettings {
* not be supported in future versions.
*/
@Deprecated
- public synchronized void setRenderPriority(RenderPriority priority) {
- throw new MustOverrideException();
- }
+ public abstract void setRenderPriority(RenderPriority priority);
/**
* Overrides the way the cache is used. The way the cache is used is based
@@ -1442,9 +1262,7 @@ public abstract class WebSettings {
*
* @param mode the mode to use
*/
- public void setCacheMode(int mode) {
- throw new MustOverrideException();
- }
+ public abstract void setCacheMode(int mode);
/**
* Gets the current setting for overriding the cache mode.
@@ -1452,9 +1270,7 @@ public abstract class WebSettings {
* @return the current setting for overriding the cache mode
* @see #setCacheMode
*/
- public int getCacheMode() {
- throw new MustOverrideException();
- }
+ public abstract int getCacheMode();
/**
* Configures the WebView's behavior when a secure origin attempts to load a resource from an
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 3bfe9cf..947d0cb 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.SystemApi;
+
import java.util.Map;
/**
@@ -65,23 +67,13 @@ public class WebStorage {
private long mUsage = 0;
/** @hide */
+ @SystemApi
protected Origin(String origin, long quota, long usage) {
mOrigin = origin;
mQuota = quota;
mUsage = usage;
}
- /** @hide */
- protected Origin(String origin, long quota) {
- mOrigin = origin;
- mQuota = quota;
- }
-
- /** @hide */
- protected Origin(String origin) {
- mOrigin = origin;
- }
-
/**
* Gets the string representation of this origin.
*
@@ -210,5 +202,6 @@ public class WebStorage {
* way to call createHandler() and createUIHandler(), so it would not work).
* @hide
*/
+ @SystemApi
public WebStorage() {}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 592d6e2..6793634 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.annotation.Widget;
import android.content.Context;
import android.content.res.Configuration;
@@ -256,10 +257,12 @@ public class WebView extends AbsoluteLayout
* always stay as a hidden API.
* @hide
*/
+ @SystemApi
public static final String DATA_REDUCTION_PROXY_SETTING_CHANGED =
"android.webkit.DATA_REDUCTION_PROXY_SETTING_CHANGED";
private static final String LOGTAG = "WebView";
+ private static final boolean TRACE = false;
// Throwing an exception for incorrect thread usage if the
// build target is JB MR2 or newer. Defaults to false, and is
@@ -394,6 +397,7 @@ public class WebView extends AbsoluteLayout
/**
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public HitTestResult() {
mType = UNKNOWN_TYPE;
}
@@ -401,6 +405,7 @@ public class WebView extends AbsoluteLayout
/**
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public void setType(int type) {
mType = type;
}
@@ -408,6 +413,7 @@ public class WebView extends AbsoluteLayout
/**
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public void setExtra(String extra) {
mExtra = extra;
}
@@ -542,7 +548,7 @@ public class WebView extends AbsoluteLayout
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "WebView<init>");
+ if (TRACE) Log.d(LOGTAG, "WebView<init>");
ensureProviderCreated();
mProvider.init(javaScriptInterfaces, privateBrowsing);
@@ -557,7 +563,7 @@ public class WebView extends AbsoluteLayout
*/
public void setHorizontalScrollbarOverlay(boolean overlay) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setHorizontalScrollbarOverlay=" + overlay);
+ if (TRACE) Log.d(LOGTAG, "setHorizontalScrollbarOverlay=" + overlay);
mProvider.setHorizontalScrollbarOverlay(overlay);
}
@@ -568,7 +574,7 @@ public class WebView extends AbsoluteLayout
*/
public void setVerticalScrollbarOverlay(boolean overlay) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setVerticalScrollbarOverlay=" + overlay);
+ if (TRACE) Log.d(LOGTAG, "setVerticalScrollbarOverlay=" + overlay);
mProvider.setVerticalScrollbarOverlay(overlay);
}
@@ -623,7 +629,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void setCertificate(SslCertificate certificate) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setCertificate=" + certificate);
+ if (TRACE) Log.d(LOGTAG, "setCertificate=" + certificate);
mProvider.setCertificate(certificate);
}
@@ -647,7 +653,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void savePassword(String host, String username, String password) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "savePassword=" + host);
+ if (TRACE) Log.d(LOGTAG, "savePassword=" + host);
mProvider.savePassword(host, username, password);
}
@@ -667,7 +673,7 @@ public class WebView extends AbsoluteLayout
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setHttpAuthUsernamePassword=" + host);
+ if (TRACE) Log.d(LOGTAG, "setHttpAuthUsernamePassword=" + host);
mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
}
@@ -697,7 +703,7 @@ public class WebView extends AbsoluteLayout
*/
public void destroy() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "destroy");
+ if (TRACE) Log.d(LOGTAG, "destroy");
mProvider.destroy();
}
@@ -743,7 +749,7 @@ public class WebView extends AbsoluteLayout
*/
public void setNetworkAvailable(boolean networkUp) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setNetworkAvailable=" + networkUp);
+ if (TRACE) Log.d(LOGTAG, "setNetworkAvailable=" + networkUp);
mProvider.setNetworkAvailable(networkUp);
}
@@ -760,7 +766,7 @@ public class WebView extends AbsoluteLayout
*/
public WebBackForwardList saveState(Bundle outState) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveState");
+ if (TRACE) Log.d(LOGTAG, "saveState");
return mProvider.saveState(outState);
}
@@ -777,7 +783,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean savePicture(Bundle b, final File dest) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "savePicture=" + dest.getName());
+ if (TRACE) Log.d(LOGTAG, "savePicture=" + dest.getName());
return mProvider.savePicture(b, dest);
}
@@ -795,7 +801,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean restorePicture(Bundle b, File src) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "restorePicture=" + src.getName());
+ if (TRACE) Log.d(LOGTAG, "restorePicture=" + src.getName());
return mProvider.restorePicture(b, src);
}
@@ -813,7 +819,7 @@ public class WebView extends AbsoluteLayout
*/
public WebBackForwardList restoreState(Bundle inState) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "restoreState");
+ if (TRACE) Log.d(LOGTAG, "restoreState");
return mProvider.restoreState(inState);
}
@@ -830,7 +836,7 @@ public class WebView extends AbsoluteLayout
*/
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
checkThread();
- if (DebugFlags.TRACE_API) {
+ if (TRACE) {
StringBuilder headers = new StringBuilder();
if (additionalHttpHeaders != null) {
for (Map.Entry<String, String> entry : additionalHttpHeaders.entrySet()) {
@@ -849,7 +855,7 @@ public class WebView extends AbsoluteLayout
*/
public void loadUrl(String url) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadUrl=" + url);
+ if (TRACE) Log.d(LOGTAG, "loadUrl=" + url);
mProvider.loadUrl(url);
}
@@ -864,7 +870,7 @@ public class WebView extends AbsoluteLayout
*/
public void postUrl(String url, byte[] postData) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "postUrl=" + url);
+ if (TRACE) Log.d(LOGTAG, "postUrl=" + url);
if (URLUtil.isNetworkUrl(url)) {
mProvider.postUrl(url, postData);
} else {
@@ -903,7 +909,7 @@ public class WebView extends AbsoluteLayout
*/
public void loadData(String data, String mimeType, String encoding) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadData");
+ if (TRACE) Log.d(LOGTAG, "loadData");
mProvider.loadData(data, mimeType, encoding);
}
@@ -936,7 +942,7 @@ public class WebView extends AbsoluteLayout
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadDataWithBaseURL=" + baseUrl);
+ if (TRACE) Log.d(LOGTAG, "loadDataWithBaseURL=" + baseUrl);
mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@@ -953,7 +959,7 @@ public class WebView extends AbsoluteLayout
*/
public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "evaluateJavascript=" + script);
+ if (TRACE) Log.d(LOGTAG, "evaluateJavascript=" + script);
mProvider.evaluateJavaScript(script, resultCallback);
}
@@ -964,7 +970,7 @@ public class WebView extends AbsoluteLayout
*/
public void saveWebArchive(String filename) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveWebArchive=" + filename);
+ if (TRACE) Log.d(LOGTAG, "saveWebArchive=" + filename);
mProvider.saveWebArchive(filename);
}
@@ -982,7 +988,7 @@ public class WebView extends AbsoluteLayout
*/
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveWebArchive(auto)=" + basename);
+ if (TRACE) Log.d(LOGTAG, "saveWebArchive(auto)=" + basename);
mProvider.saveWebArchive(basename, autoname, callback);
}
@@ -991,7 +997,7 @@ public class WebView extends AbsoluteLayout
*/
public void stopLoading() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "stopLoading");
+ if (TRACE) Log.d(LOGTAG, "stopLoading");
mProvider.stopLoading();
}
@@ -1000,7 +1006,7 @@ public class WebView extends AbsoluteLayout
*/
public void reload() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "reload");
+ if (TRACE) Log.d(LOGTAG, "reload");
mProvider.reload();
}
@@ -1019,7 +1025,7 @@ public class WebView extends AbsoluteLayout
*/
public void goBack() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goBack");
+ if (TRACE) Log.d(LOGTAG, "goBack");
mProvider.goBack();
}
@@ -1038,7 +1044,7 @@ public class WebView extends AbsoluteLayout
*/
public void goForward() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goForward");
+ if (TRACE) Log.d(LOGTAG, "goForward");
mProvider.goForward();
}
@@ -1064,7 +1070,7 @@ public class WebView extends AbsoluteLayout
*/
public void goBackOrForward(int steps) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goBackOrForwad=" + steps);
+ if (TRACE) Log.d(LOGTAG, "goBackOrForwad=" + steps);
mProvider.goBackOrForward(steps);
}
@@ -1084,7 +1090,7 @@ public class WebView extends AbsoluteLayout
*/
public boolean pageUp(boolean top) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pageUp");
+ if (TRACE) Log.d(LOGTAG, "pageUp");
return mProvider.pageUp(top);
}
@@ -1096,7 +1102,7 @@ public class WebView extends AbsoluteLayout
*/
public boolean pageDown(boolean bottom) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pageDown");
+ if (TRACE) Log.d(LOGTAG, "pageDown");
return mProvider.pageDown(bottom);
}
@@ -1109,7 +1115,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void clearView() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearView");
+ if (TRACE) Log.d(LOGTAG, "clearView");
mProvider.clearView();
}
@@ -1140,7 +1146,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public Picture capturePicture() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "capturePicture");
+ if (TRACE) Log.d(LOGTAG, "capturePicture");
return mProvider.capturePicture();
}
@@ -1151,7 +1157,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public PrintDocumentAdapter createPrintDocumentAdapter() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "createPrintDocumentAdapter");
+ if (TRACE) Log.d(LOGTAG, "createPrintDocumentAdapter");
return mProvider.createPrintDocumentAdapter("default");
}
@@ -1170,7 +1176,7 @@ public class WebView extends AbsoluteLayout
*/
public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "createPrintDocumentAdapter");
+ if (TRACE) Log.d(LOGTAG, "createPrintDocumentAdapter");
return mProvider.createPrintDocumentAdapter(documentName);
}
@@ -1210,7 +1216,7 @@ public class WebView extends AbsoluteLayout
*/
public void setInitialScale(int scaleInPercent) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setInitialScale=" + scaleInPercent);
+ if (TRACE) Log.d(LOGTAG, "setInitialScale=" + scaleInPercent);
mProvider.setInitialScale(scaleInPercent);
}
@@ -1221,7 +1227,7 @@ public class WebView extends AbsoluteLayout
*/
public void invokeZoomPicker() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "invokeZoomPicker");
+ if (TRACE) Log.d(LOGTAG, "invokeZoomPicker");
mProvider.invokeZoomPicker();
}
@@ -1245,7 +1251,7 @@ public class WebView extends AbsoluteLayout
*/
public HitTestResult getHitTestResult() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "getHitTestResult");
+ if (TRACE) Log.d(LOGTAG, "getHitTestResult");
return mProvider.getHitTestResult();
}
@@ -1264,7 +1270,7 @@ public class WebView extends AbsoluteLayout
*/
public void requestFocusNodeHref(Message hrefMsg) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "requestFocusNodeHref");
+ if (TRACE) Log.d(LOGTAG, "requestFocusNodeHref");
mProvider.requestFocusNodeHref(hrefMsg);
}
@@ -1277,7 +1283,7 @@ public class WebView extends AbsoluteLayout
*/
public void requestImageRef(Message msg) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "requestImageRef");
+ if (TRACE) Log.d(LOGTAG, "requestImageRef");
mProvider.requestImageRef(msg);
}
@@ -1382,7 +1388,7 @@ public class WebView extends AbsoluteLayout
*/
public void pauseTimers() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pauseTimers");
+ if (TRACE) Log.d(LOGTAG, "pauseTimers");
mProvider.pauseTimers();
}
@@ -1392,7 +1398,7 @@ public class WebView extends AbsoluteLayout
*/
public void resumeTimers() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "resumeTimers");
+ if (TRACE) Log.d(LOGTAG, "resumeTimers");
mProvider.resumeTimers();
}
@@ -1405,7 +1411,7 @@ public class WebView extends AbsoluteLayout
*/
public void onPause() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "onPause");
+ if (TRACE) Log.d(LOGTAG, "onPause");
mProvider.onPause();
}
@@ -1414,7 +1420,7 @@ public class WebView extends AbsoluteLayout
*/
public void onResume() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "onResume");
+ if (TRACE) Log.d(LOGTAG, "onResume");
mProvider.onResume();
}
@@ -1437,7 +1443,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void freeMemory() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "freeMemory");
+ if (TRACE) Log.d(LOGTAG, "freeMemory");
mProvider.freeMemory();
}
@@ -1449,7 +1455,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearCache(boolean includeDiskFiles) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearCache");
+ if (TRACE) Log.d(LOGTAG, "clearCache");
mProvider.clearCache(includeDiskFiles);
}
@@ -1461,7 +1467,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearFormData() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearFormData");
+ if (TRACE) Log.d(LOGTAG, "clearFormData");
mProvider.clearFormData();
}
@@ -1470,7 +1476,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearHistory() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearHistory");
+ if (TRACE) Log.d(LOGTAG, "clearHistory");
mProvider.clearHistory();
}
@@ -1480,7 +1486,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearSslPreferences() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearSslPreferences");
+ if (TRACE) Log.d(LOGTAG, "clearSslPreferences");
mProvider.clearSslPreferences();
}
@@ -1496,7 +1502,7 @@ public class WebView extends AbsoluteLayout
* callback. The runnable will be called in UI thread.
*/
public static void clearClientCertPreferences(Runnable onCleared) {
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearClientCertPreferences");
+ if (TRACE) Log.d(LOGTAG, "clearClientCertPreferences");
getFactory().getStatics().clearClientCertPreferences(onCleared);
}
@@ -1538,7 +1544,7 @@ public class WebView extends AbsoluteLayout
*/
public void findNext(boolean forward) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findNext");
+ if (TRACE) Log.d(LOGTAG, "findNext");
mProvider.findNext(forward);
}
@@ -1554,7 +1560,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public int findAll(String find) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findAll");
+ if (TRACE) Log.d(LOGTAG, "findAll");
StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
return mProvider.findAll(find);
}
@@ -1569,7 +1575,7 @@ public class WebView extends AbsoluteLayout
*/
public void findAllAsync(String find) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findAllAsync");
+ if (TRACE) Log.d(LOGTAG, "findAllAsync");
mProvider.findAllAsync(find);
}
@@ -1590,7 +1596,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean showFindDialog(String text, boolean showIme) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "showFindDialog");
+ if (TRACE) Log.d(LOGTAG, "showFindDialog");
return mProvider.showFindDialog(text, showIme);
}
@@ -1646,7 +1652,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearMatches() {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearMatches");
+ if (TRACE) Log.d(LOGTAG, "clearMatches");
mProvider.clearMatches();
}
@@ -1707,7 +1713,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void setPictureListener(PictureListener listener) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setPictureListener=" + listener);
+ if (TRACE) Log.d(LOGTAG, "setPictureListener=" + listener);
mProvider.setPictureListener(listener);
}
@@ -1764,7 +1770,7 @@ public class WebView extends AbsoluteLayout
*/
public void addJavascriptInterface(Object object, String name) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "addJavascriptInterface=" + name);
+ if (TRACE) Log.d(LOGTAG, "addJavascriptInterface=" + name);
mProvider.addJavascriptInterface(object, name);
}
@@ -1777,7 +1783,7 @@ public class WebView extends AbsoluteLayout
*/
public void removeJavascriptInterface(String name) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "removeJavascriptInterface=" + name);
+ if (TRACE) Log.d(LOGTAG, "removeJavascriptInterface=" + name);
mProvider.removeJavascriptInterface(name);
}
@@ -1881,7 +1887,7 @@ public class WebView extends AbsoluteLayout
public void flingScroll(int vx, int vy) {
checkThread();
- if (DebugFlags.TRACE_API) Log.d(LOGTAG, "flingScroll");
+ if (TRACE) Log.d(LOGTAG, "flingScroll");
mProvider.flingScroll(vx, vy);
}
@@ -2006,6 +2012,7 @@ public class WebView extends AbsoluteLayout
*
* @hide WebViewProvider is not public API.
*/
+ @SystemApi
public WebViewProvider getWebViewProvider() {
return mProvider;
}
@@ -2015,6 +2022,7 @@ public class WebView extends AbsoluteLayout
* and fields, and make super-class calls in this WebView instance.
* @hide Only for use by WebViewProvider implementations
*/
+ @SystemApi
public class PrivateAccess {
// ---- Access to super-class methods ----
public int super_getScrollBarStyle() {
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 99e0ffb..bfea481 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.content.Context;
/**
@@ -28,18 +29,12 @@ import android.content.Context;
* <li>Data entered into text fields (e.g. for autocomplete suggestions)</li>
* </ul>
*/
-public class WebViewDatabase {
+public abstract class WebViewDatabase {
/**
* @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
protected static final String LOGTAG = "webviewdatabase";
- /**
- * @hide Only for use by WebViewProvider implementations.
- */
- protected WebViewDatabase() {
- }
-
public static WebViewDatabase getInstance(Context context) {
return WebViewFactory.getProvider().getWebViewDatabase(context);
}
@@ -54,9 +49,7 @@ public class WebViewDatabase {
* @deprecated Saving passwords in WebView will not be supported in future versions.
*/
@Deprecated
- public boolean hasUsernamePassword() {
- throw new MustOverrideException();
- }
+ public abstract boolean hasUsernamePassword();
/**
* Clears any saved username/password pairs for web forms.
@@ -67,9 +60,7 @@ public class WebViewDatabase {
* @deprecated Saving passwords in WebView will not be supported in future versions.
*/
@Deprecated
- public void clearUsernamePassword() {
- throw new MustOverrideException();
- }
+ public abstract void clearUsernamePassword();
/**
* Gets whether there are any saved credentials for HTTP authentication.
@@ -79,9 +70,7 @@ public class WebViewDatabase {
* @see WebView#setHttpAuthUsernamePassword
* @see #clearHttpAuthUsernamePassword
*/
- public boolean hasHttpAuthUsernamePassword() {
- throw new MustOverrideException();
- }
+ public abstract boolean hasHttpAuthUsernamePassword();
/**
* Clears any saved credentials for HTTP authentication.
@@ -90,9 +79,7 @@ public class WebViewDatabase {
* @see WebView#setHttpAuthUsernamePassword
* @see #hasHttpAuthUsernamePassword
*/
- public void clearHttpAuthUsernamePassword() {
- throw new MustOverrideException();
- }
+ public abstract void clearHttpAuthUsernamePassword();
/**
* Gets whether there is any saved data for web forms.
@@ -100,16 +87,12 @@ public class WebViewDatabase {
* @return whether there is any saved data for web forms
* @see #clearFormData
*/
- public boolean hasFormData() {
- throw new MustOverrideException();
- }
+ public abstract boolean hasFormData();
/**
* Clears any saved data for web forms.
*
* @see #hasFormData
*/
- public void clearFormData() {
- throw new MustOverrideException();
- }
+ public abstract void clearFormData();
}
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index c878b3d..e03445e 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.app.Application;
import android.content.Context;
@@ -35,6 +36,7 @@ import android.view.ViewRootImpl;
*
* @hide
*/
+@SystemApi
public final class WebViewDelegate {
/* package */ WebViewDelegate() { }
@@ -105,7 +107,7 @@ public final class WebViewDelegate {
throw new IllegalArgumentException(canvas.getClass().getName()
+ " is not hardware accelerated");
}
- ((HardwareCanvas) canvas).callDrawGLFunction(nativeDrawGLFunctor);
+ ((HardwareCanvas) canvas).callDrawGLFunction2(nativeDrawGLFunctor);
}
/**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index f185a86..cafe053 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,9 +16,10 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.app.ActivityManagerInternal;
-import android.app.Application;
import android.app.AppGlobals;
+import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -33,19 +34,20 @@ import android.os.Trace;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.Log;
+
import com.android.server.LocalServices;
+
import dalvik.system.VMRuntime;
import java.io.File;
import java.util.Arrays;
-import com.android.internal.os.Zygote;
-
/**
* Top level factory, used creating all the main WebView implementation classes.
*
* @hide
*/
+@SystemApi
public final class WebViewFactory {
private static final String CHROMIUM_WEBVIEW_FACTORY =
@@ -89,6 +91,12 @@ public final class WebViewFactory {
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
+ final int uid = android.os.Process.myUid();
+ if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID) {
+ throw new UnsupportedOperationException(
+ "For security reasons, WebView is not allowed in privileged processes");
+ }
+
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index d37d217..9105394 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -26,6 +27,7 @@ import android.net.Uri;
* implementation of this interface, and make it available to the WebView via mechanism TBD.
* @hide
*/
+@SystemApi
public interface WebViewFactoryProvider {
/**
* This Interface provides glue for implementing the backend of WebView static methods which
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index fe18138..2aee57b 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.SystemApi;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -53,6 +54,7 @@ import java.util.Map;
*
* @hide Not part of the public API; only required by system implementors.
*/
+@SystemApi
public interface WebViewProvider {
//-------------------------------------------------------------------------
// Main interface for backend provider of the WebView class.
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index d39960f..4800c7f 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -16,7 +16,6 @@
package android.widget;
-import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -65,9 +64,6 @@ public abstract class AbsSeekBar extends ProgressBar {
* progress.
*/
private int mKeyProgressIncrement = 1;
- private ObjectAnimator mPositionAnimator;
- private static final int PROGRESS_ANIMATION_DURATION = 250;
-
private static final int NO_ALPHA = 0xFF;
private float mDisabledAlpha;
@@ -388,14 +384,15 @@ public abstract class AbsSeekBar extends ProgressBar {
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
- if (!isAnimationRunning()) {
- setThumbPos(scale);
- }
- }
+ final Drawable thumb = mThumb;
+ if (thumb != null) {
+ setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- @Override
- void onAnimatePosition(float scale, boolean fromUser) {
- setThumbPos(scale);
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
+ invalidate();
+ }
}
@Override
@@ -440,18 +437,6 @@ public abstract class AbsSeekBar extends ProgressBar {
return max > 0 ? getProgress() / (float) max : 0;
}
- private void setThumbPos(float scale) {
- final Drawable thumb = mThumb;
- if (thumb != null) {
- setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- // Since we draw translated, the drawable's bounds that it signals
- // for invalidation won't be the actual bounds we want invalidated,
- // so just invalidate this whole view.
- invalidate();
-
- }
- }
-
/**
* Updates the thumb drawable bounds.
*
@@ -714,13 +699,13 @@ public abstract class AbsSeekBar extends ProgressBar {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (progress <= 0) break;
- animateSetProgress(progress - mKeyProgressIncrement);
+ setProgress(progress - mKeyProgressIncrement, true);
onKeyChange();
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (progress >= getMax()) break;
- animateSetProgress(progress + mKeyProgressIncrement);
+ setProgress(progress + mKeyProgressIncrement, true);
onKeyChange();
return true;
}
@@ -729,38 +714,6 @@ public abstract class AbsSeekBar extends ProgressBar {
return super.onKeyDown(keyCode, event);
}
- boolean isAnimationRunning() {
- return mPositionAnimator != null && mPositionAnimator.isRunning();
- }
-
- /**
- * @hide
- */
- @Override
- public void setProgress(int progress, boolean fromUser) {
- if (isAnimationRunning()) {
- mPositionAnimator.cancel();
- }
- super.setProgress(progress, fromUser);
- }
-
- void animateSetProgress(int progress) {
- float curProgress = isAnimationRunning() ? getAnimationPosition() : getProgress();
-
- if (progress < 0) {
- progress = 0;
- } else if (progress > getMax()) {
- progress = getMax();
- }
- setProgressValueOnly(progress);
-
- mPositionAnimator = ObjectAnimator.ofFloat(this, "animationPosition", curProgress,
- progress);
- mPositionAnimator.setDuration(PROGRESS_ANIMATION_DURATION);
- mPositionAnimator.setAutoCancel(true);
- mPositionAnimator.start();
- }
-
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index d779124..18f15a0 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -655,15 +655,19 @@ public class ActionMenuPresenter extends BaseMenuPresenter
protected boolean setFrame(int l, int t, int r, int b) {
final boolean changed = super.setFrame(l, t, r, b);
- // Set up the hotspot bounds to be centered on the image.
+ // Set up the hotspot bounds to square and centered on the image.
final Drawable d = getDrawable();
final Drawable bg = getBackground();
if (d != null && bg != null) {
- final float[] pts = mTempPts;
- pts[0] = d.getBounds().centerX();
- getImageMatrix().mapPoints(pts);
- final int offset = (int) pts[0] - getWidth() / 2;
- bg.setHotspotBounds(offset, 0, getWidth() + offset, getHeight());
+ final int width = getWidth();
+ final int height = getHeight();
+ final int halfEdge = Math.max(width, height) / 2;
+ final int offsetX = getPaddingLeft() - getPaddingRight();
+ final int offsetY = getPaddingTop() - getPaddingBottom();
+ final int centerX = (width + offsetX) / 2;
+ final int centerY = (height + offsetY) / 2;
+ bg.setHotspotBounds(centerX - halfEdge, centerY - halfEdge,
+ centerX + halfEdge, centerY + halfEdge);
}
return changed;
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 3b16aba..e6392b9 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1005,6 +1005,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
+
+ if (mTemporaryDetach) {
+ // If we are temporarily in the detach state, then do nothing.
+ return;
+ }
+
// Perform validation if the view is losing focus.
if (!focused) {
performValidation();
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index f380d68..ed59ea6 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -17,42 +17,24 @@
package android.widget;
import android.annotation.Widget;
-import android.app.Service;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.Style;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.AbsListView.OnScrollListener;
import com.android.internal.R;
+import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
-import libcore.icu.LocaleData;
-
/**
* This class is a calendar widget for displaying and selecting dates. The range
* of dates supported by this calendar is configurable. A user can select a date
@@ -74,13 +56,12 @@ import libcore.icu.LocaleData;
*/
@Widget
public class CalendarView extends FrameLayout {
+ private static final String LOG_TAG = "CalendarView";
- /**
- * Tag for logging.
- */
- private static final String LOG_TAG = CalendarView.class.getSimpleName();
+ private static final int MODE_HOLO = 0;
+ private static final int MODE_MATERIAL = 1;
- private CalendarViewDelegate mDelegate;
+ private final CalendarViewDelegate mDelegate;
/**
* The callback used to indicate the user changes the date.
@@ -113,7 +94,23 @@ public class CalendarView extends FrameLayout {
public CalendarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mDelegate = new LegacyCalendarViewDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes);
+ final int mode = a.getInt(R.styleable.CalendarView_calendarViewMode, MODE_HOLO);
+ a.recycle();
+
+ switch (mode) {
+ case MODE_HOLO:
+ mDelegate = new CalendarViewLegacyDelegate(
+ this, context, attrs, defStyleAttr, defStyleRes);
+ break;
+ case MODE_MATERIAL:
+ mDelegate = new CalendarViewMaterialDelegate(
+ this, context, attrs, defStyleAttr, defStyleRes);
+ break;
+ default:
+ throw new IllegalArgumentException("invalid calendarViewMode attribute");
+ }
}
/**
@@ -326,16 +323,6 @@ public class CalendarView extends FrameLayout {
return mDelegate.getDateTextAppearance();
}
- @Override
- public void setEnabled(boolean enabled) {
- mDelegate.setEnabled(enabled);
- }
-
- @Override
- public boolean isEnabled() {
- return mDelegate.isEnabled();
- }
-
/**
* Gets the minimal date supported by this {@link CalendarView} in milliseconds
* since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
@@ -516,14 +503,12 @@ public class CalendarView extends FrameLayout {
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- mDelegate.onInitializeAccessibilityEvent(event);
+ event.setClassName(CalendarView.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- mDelegate.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(CalendarView.class.getName());
}
/**
@@ -560,9 +545,6 @@ public class CalendarView extends FrameLayout {
void setDateTextAppearance(int resourceId);
int getDateTextAppearance();
- void setEnabled(boolean enabled);
- boolean isEnabled();
-
void setMinDate(long minDate);
long getMinDate();
@@ -582,21 +564,26 @@ public class CalendarView extends FrameLayout {
void setOnDateChangeListener(OnDateChangeListener listener);
void onConfigurationChanged(Configuration newConfig);
- void onInitializeAccessibilityEvent(AccessibilityEvent event);
- void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
}
/**
* An abstract class which can be used as a start for CalendarView implementations
*/
abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate {
- // The delegator
- protected CalendarView mDelegator;
+ /** String for parsing dates. */
+ private static final String DATE_FORMAT = "MM/dd/yyyy";
- // The context
- protected Context mContext;
+ /** The default minimal date. */
+ protected static final String DEFAULT_MIN_DATE = "01/01/1900";
+
+ /** The default maximal date. */
+ protected static final String DEFAULT_MAX_DATE = "01/01/2100";
- // The current locale
+ /** Date format for parsing dates. */
+ protected static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
+
+ protected CalendarView mDelegator;
+ protected Context mContext;
protected Locale mCurrentLocale;
AbstractCalendarViewDelegate(CalendarView delegator, Context context) {
@@ -613,830 +600,6 @@ public class CalendarView extends FrameLayout {
}
mCurrentLocale = locale;
}
- }
-
- /**
- * A delegate implementing the legacy CalendarView
- */
- private static class LegacyCalendarViewDelegate extends AbstractCalendarViewDelegate {
-
- /**
- * Default value whether to show week number.
- */
- private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
-
- /**
- * The number of milliseconds in a day.e
- */
- private static final long MILLIS_IN_DAY = 86400000L;
-
- /**
- * The number of day in a week.
- */
- private static final int DAYS_PER_WEEK = 7;
-
- /**
- * The number of milliseconds in a week.
- */
- private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
-
- /**
- * Affects when the month selection will change while scrolling upe
- */
- private static final int SCROLL_HYST_WEEKS = 2;
-
- /**
- * How long the GoTo fling animation should last.
- */
- private static final int GOTO_SCROLL_DURATION = 1000;
-
- /**
- * The duration of the adjustment upon a user scroll in milliseconds.
- */
- private static final int ADJUSTMENT_SCROLL_DURATION = 500;
-
- /**
- * How long to wait after receiving an onScrollStateChanged notification
- * before acting on it.
- */
- private static final int SCROLL_CHANGE_DELAY = 40;
-
- /**
- * String for parsing dates.
- */
- private static final String DATE_FORMAT = "MM/dd/yyyy";
-
- /**
- * The default minimal date.
- */
- private static final String DEFAULT_MIN_DATE = "01/01/1900";
-
- /**
- * The default maximal date.
- */
- private static final String DEFAULT_MAX_DATE = "01/01/2100";
-
- private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
-
- private static final int DEFAULT_DATE_TEXT_SIZE = 14;
-
- private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
-
- private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
-
- private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
-
- private static final int UNSCALED_BOTTOM_BUFFER = 20;
-
- private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
-
- private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
-
- private final int mWeekSeperatorLineWidth;
-
- private int mDateTextSize;
-
- private Drawable mSelectedDateVerticalBar;
-
- private final int mSelectedDateVerticalBarWidth;
-
- private int mSelectedWeekBackgroundColor;
-
- private int mFocusedMonthDateColor;
-
- private int mUnfocusedMonthDateColor;
-
- private int mWeekSeparatorLineColor;
-
- private int mWeekNumberColor;
-
- private int mWeekDayTextAppearanceResId;
-
- private int mDateTextAppearanceResId;
-
- /**
- * The top offset of the weeks list.
- */
- private int mListScrollTopOffset = 2;
-
- /**
- * The visible height of a week view.
- */
- private int mWeekMinVisibleHeight = 12;
-
- /**
- * The visible height of a week view.
- */
- private int mBottomBuffer = 20;
-
- /**
- * The number of shown weeks.
- */
- private int mShownWeekCount;
-
- /**
- * Flag whether to show the week number.
- */
- private boolean mShowWeekNumber;
-
- /**
- * The number of day per week to be shown.
- */
- private int mDaysPerWeek = 7;
-
- /**
- * The friction of the week list while flinging.
- */
- private float mFriction = .05f;
-
- /**
- * Scale for adjusting velocity of the week list while flinging.
- */
- private float mVelocityScale = 0.333f;
-
- /**
- * The adapter for the weeks list.
- */
- private WeeksAdapter mAdapter;
-
- /**
- * The weeks list.
- */
- private ListView mListView;
-
- /**
- * The name of the month to display.
- */
- private TextView mMonthName;
-
- /**
- * The header with week day names.
- */
- private ViewGroup mDayNamesHeader;
-
- /**
- * Cached abbreviations for day of week names.
- */
- private String[] mDayNamesShort;
-
- /**
- * Cached full-length day of week names.
- */
- private String[] mDayNamesLong;
-
- /**
- * The first day of the week.
- */
- private int mFirstDayOfWeek;
-
- /**
- * Which month should be displayed/highlighted [0-11].
- */
- private int mCurrentMonthDisplayed = -1;
-
- /**
- * Used for tracking during a scroll.
- */
- private long mPreviousScrollPosition;
-
- /**
- * Used for tracking which direction the view is scrolling.
- */
- private boolean mIsScrollingUp = false;
-
- /**
- * The previous scroll state of the weeks ListView.
- */
- private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- /**
- * The current scroll state of the weeks ListView.
- */
- private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- /**
- * Listener for changes in the selected day.
- */
- private OnDateChangeListener mOnDateChangeListener;
-
- /**
- * Command for adjusting the position after a scroll/fling.
- */
- private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
-
- /**
- * Temporary instance to avoid multiple instantiations.
- */
- private Calendar mTempDate;
-
- /**
- * The first day of the focused month.
- */
- private Calendar mFirstDayOfMonth;
-
- /**
- * The start date of the range supported by this picker.
- */
- private Calendar mMinDate;
-
- /**
- * The end date of the range supported by this picker.
- */
- private Calendar mMaxDate;
-
- /**
- * Date format for parsing dates.
- */
- private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
-
- LegacyCalendarViewDelegate(CalendarView delegator, Context context, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(delegator, context);
-
- // initialization based on locale
- setCurrentLocale(Locale.getDefault());
-
- TypedArray attributesArray = context.obtainStyledAttributes(attrs,
- R.styleable.CalendarView, defStyleAttr, defStyleRes);
- mShowWeekNumber = attributesArray.getBoolean(R.styleable.CalendarView_showWeekNumber,
- DEFAULT_SHOW_WEEK_NUMBER);
- mFirstDayOfWeek = attributesArray.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
- String minDate = attributesArray.getString(R.styleable.CalendarView_minDate);
- if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
- parseDate(DEFAULT_MIN_DATE, mMinDate);
- }
- String maxDate = attributesArray.getString(R.styleable.CalendarView_maxDate);
- if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
- parseDate(DEFAULT_MAX_DATE, mMaxDate);
- }
- if (mMaxDate.before(mMinDate)) {
- throw new IllegalArgumentException("Max date cannot be before min date.");
- }
- mShownWeekCount = attributesArray.getInt(R.styleable.CalendarView_shownWeekCount,
- DEFAULT_SHOWN_WEEK_COUNT);
- mSelectedWeekBackgroundColor = attributesArray.getColor(
- R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
- mFocusedMonthDateColor = attributesArray.getColor(
- R.styleable.CalendarView_focusedMonthDateColor, 0);
- mUnfocusedMonthDateColor = attributesArray.getColor(
- R.styleable.CalendarView_unfocusedMonthDateColor, 0);
- mWeekSeparatorLineColor = attributesArray.getColor(
- R.styleable.CalendarView_weekSeparatorLineColor, 0);
- mWeekNumberColor = attributesArray.getColor(R.styleable.CalendarView_weekNumberColor, 0);
- mSelectedDateVerticalBar = attributesArray.getDrawable(
- R.styleable.CalendarView_selectedDateVerticalBar);
-
- mDateTextAppearanceResId = attributesArray.getResourceId(
- R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
- updateDateTextSize();
-
- mWeekDayTextAppearanceResId = attributesArray.getResourceId(
- R.styleable.CalendarView_weekDayTextAppearance,
- DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
- attributesArray.recycle();
-
- DisplayMetrics displayMetrics = mDelegator.getResources().getDisplayMetrics();
- mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
- mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
- mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_BOTTOM_BUFFER, displayMetrics);
- mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
- mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
-
- LayoutInflater layoutInflater = (LayoutInflater) mContext
- .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
- View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
- mDelegator.addView(content);
-
- mListView = (ListView) mDelegator.findViewById(R.id.list);
- mDayNamesHeader = (ViewGroup) content.findViewById(com.android.internal.R.id.day_names);
- mMonthName = (TextView) content.findViewById(com.android.internal.R.id.month_name);
-
- setUpHeader();
- setUpListView();
- setUpAdapter();
-
- // go to today or whichever is close to today min or max date
- mTempDate.setTimeInMillis(System.currentTimeMillis());
- if (mTempDate.before(mMinDate)) {
- goTo(mMinDate, false, true, true);
- } else if (mMaxDate.before(mTempDate)) {
- goTo(mMaxDate, false, true, true);
- } else {
- goTo(mTempDate, false, true, true);
- }
-
- mDelegator.invalidate();
- }
-
- @Override
- public void setShownWeekCount(int count) {
- if (mShownWeekCount != count) {
- mShownWeekCount = count;
- mDelegator.invalidate();
- }
- }
-
- @Override
- public int getShownWeekCount() {
- return mShownWeekCount;
- }
-
- @Override
- public void setSelectedWeekBackgroundColor(int color) {
- if (mSelectedWeekBackgroundColor != color) {
- mSelectedWeekBackgroundColor = color;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasSelectedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public int getSelectedWeekBackgroundColor() {
- return mSelectedWeekBackgroundColor;
- }
-
- @Override
- public void setFocusedMonthDateColor(int color) {
- if (mFocusedMonthDateColor != color) {
- mFocusedMonthDateColor = color;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasFocusedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public int getFocusedMonthDateColor() {
- return mFocusedMonthDateColor;
- }
-
- @Override
- public void setUnfocusedMonthDateColor(int color) {
- if (mUnfocusedMonthDateColor != color) {
- mUnfocusedMonthDateColor = color;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasUnfocusedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public int getUnfocusedMonthDateColor() {
- return mFocusedMonthDateColor;
- }
-
- @Override
- public void setWeekNumberColor(int color) {
- if (mWeekNumberColor != color) {
- mWeekNumberColor = color;
- if (mShowWeekNumber) {
- invalidateAllWeekViews();
- }
- }
- }
-
- @Override
- public int getWeekNumberColor() {
- return mWeekNumberColor;
- }
-
- @Override
- public void setWeekSeparatorLineColor(int color) {
- if (mWeekSeparatorLineColor != color) {
- mWeekSeparatorLineColor = color;
- invalidateAllWeekViews();
- }
- }
-
- @Override
- public int getWeekSeparatorLineColor() {
- return mWeekSeparatorLineColor;
- }
-
- @Override
- public void setSelectedDateVerticalBar(int resourceId) {
- Drawable drawable = mDelegator.getContext().getDrawable(resourceId);
- setSelectedDateVerticalBar(drawable);
- }
-
- @Override
- public void setSelectedDateVerticalBar(Drawable drawable) {
- if (mSelectedDateVerticalBar != drawable) {
- mSelectedDateVerticalBar = drawable;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasSelectedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public Drawable getSelectedDateVerticalBar() {
- return mSelectedDateVerticalBar;
- }
-
- @Override
- public void setWeekDayTextAppearance(int resourceId) {
- if (mWeekDayTextAppearanceResId != resourceId) {
- mWeekDayTextAppearanceResId = resourceId;
- setUpHeader();
- }
- }
-
- @Override
- public int getWeekDayTextAppearance() {
- return mWeekDayTextAppearanceResId;
- }
-
- @Override
- public void setDateTextAppearance(int resourceId) {
- if (mDateTextAppearanceResId != resourceId) {
- mDateTextAppearanceResId = resourceId;
- updateDateTextSize();
- invalidateAllWeekViews();
- }
- }
-
- @Override
- public int getDateTextAppearance() {
- return mDateTextAppearanceResId;
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- mListView.setEnabled(enabled);
- }
-
- @Override
- public boolean isEnabled() {
- return mListView.isEnabled();
- }
-
- @Override
- public void setMinDate(long minDate) {
- mTempDate.setTimeInMillis(minDate);
- if (isSameDate(mTempDate, mMinDate)) {
- return;
- }
- mMinDate.setTimeInMillis(minDate);
- // make sure the current date is not earlier than
- // the new min date since the latter is used for
- // calculating the indices in the adapter thus
- // avoiding out of bounds error
- Calendar date = mAdapter.mSelectedDate;
- if (date.before(mMinDate)) {
- mAdapter.setSelectedDay(mMinDate);
- }
- // reinitialize the adapter since its range depends on min date
- mAdapter.init();
- if (date.before(mMinDate)) {
- setDate(mTempDate.getTimeInMillis());
- } else {
- // we go to the current date to force the ListView to query its
- // adapter for the shown views since we have changed the adapter
- // range and the base from which the later calculates item indices
- // note that calling setDate will not work since the date is the same
- goTo(date, false, true, false);
- }
- }
-
- @Override
- public long getMinDate() {
- return mMinDate.getTimeInMillis();
- }
-
- @Override
- public void setMaxDate(long maxDate) {
- mTempDate.setTimeInMillis(maxDate);
- if (isSameDate(mTempDate, mMaxDate)) {
- return;
- }
- mMaxDate.setTimeInMillis(maxDate);
- // reinitialize the adapter since its range depends on max date
- mAdapter.init();
- Calendar date = mAdapter.mSelectedDate;
- if (date.after(mMaxDate)) {
- setDate(mMaxDate.getTimeInMillis());
- } else {
- // we go to the current date to force the ListView to query its
- // adapter for the shown views since we have changed the adapter
- // range and the base from which the later calculates item indices
- // note that calling setDate will not work since the date is the same
- goTo(date, false, true, false);
- }
- }
-
- @Override
- public long getMaxDate() {
- return mMaxDate.getTimeInMillis();
- }
-
- @Override
- public void setShowWeekNumber(boolean showWeekNumber) {
- if (mShowWeekNumber == showWeekNumber) {
- return;
- }
- mShowWeekNumber = showWeekNumber;
- mAdapter.notifyDataSetChanged();
- setUpHeader();
- }
-
- @Override
- public boolean getShowWeekNumber() {
- return mShowWeekNumber;
- }
-
- @Override
- public void setFirstDayOfWeek(int firstDayOfWeek) {
- if (mFirstDayOfWeek == firstDayOfWeek) {
- return;
- }
- mFirstDayOfWeek = firstDayOfWeek;
- mAdapter.init();
- mAdapter.notifyDataSetChanged();
- setUpHeader();
- }
-
- @Override
- public int getFirstDayOfWeek() {
- return mFirstDayOfWeek;
- }
-
- @Override
- public void setDate(long date) {
- setDate(date, false, false);
- }
-
- @Override
- public void setDate(long date, boolean animate, boolean center) {
- mTempDate.setTimeInMillis(date);
- if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
- return;
- }
- goTo(mTempDate, animate, true, center);
- }
-
- @Override
- public long getDate() {
- return mAdapter.mSelectedDate.getTimeInMillis();
- }
-
- @Override
- public void setOnDateChangeListener(OnDateChangeListener listener) {
- mOnDateChangeListener = listener;
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- setCurrentLocale(newConfig.locale);
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- event.setClassName(CalendarView.class.getName());
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- info.setClassName(CalendarView.class.getName());
- }
-
- /**
- * Sets the current locale.
- *
- * @param locale The current locale.
- */
- @Override
- protected void setCurrentLocale(Locale locale) {
- super.setCurrentLocale(locale);
-
- mTempDate = getCalendarForLocale(mTempDate, locale);
- mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
- mMinDate = getCalendarForLocale(mMinDate, locale);
- mMaxDate = getCalendarForLocale(mMaxDate, locale);
- }
- private void updateDateTextSize() {
- TypedArray dateTextAppearance = mDelegator.getContext().obtainStyledAttributes(
- mDateTextAppearanceResId, R.styleable.TextAppearance);
- mDateTextSize = dateTextAppearance.getDimensionPixelSize(
- R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
- dateTextAppearance.recycle();
- }
-
- /**
- * Invalidates all week views.
- */
- private void invalidateAllWeekViews() {
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View view = mListView.getChildAt(i);
- view.invalidate();
- }
- }
-
- /**
- * Gets a calendar for locale bootstrapped with the value of a given calendar.
- *
- * @param oldCalendar The old calendar.
- * @param locale The locale.
- */
- private static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
- if (oldCalendar == null) {
- return Calendar.getInstance(locale);
- } else {
- final long currentTimeMillis = oldCalendar.getTimeInMillis();
- Calendar newCalendar = Calendar.getInstance(locale);
- newCalendar.setTimeInMillis(currentTimeMillis);
- return newCalendar;
- }
- }
-
- /**
- * @return True if the <code>firstDate</code> is the same as the <code>
- * secondDate</code>.
- */
- private static boolean isSameDate(Calendar firstDate, Calendar secondDate) {
- return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
- && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
- }
-
- /**
- * Creates a new adapter if necessary and sets up its parameters.
- */
- private void setUpAdapter() {
- if (mAdapter == null) {
- mAdapter = new WeeksAdapter(mContext);
- mAdapter.registerDataSetObserver(new DataSetObserver() {
- @Override
- public void onChanged() {
- if (mOnDateChangeListener != null) {
- Calendar selectedDay = mAdapter.getSelectedDay();
- mOnDateChangeListener.onSelectedDayChange(mDelegator,
- selectedDay.get(Calendar.YEAR),
- selectedDay.get(Calendar.MONTH),
- selectedDay.get(Calendar.DAY_OF_MONTH));
- }
- }
- });
- mListView.setAdapter(mAdapter);
- }
-
- // refresh the view with the new parameters
- mAdapter.notifyDataSetChanged();
- }
-
- /**
- * Sets up the strings to be used by the header.
- */
- private void setUpHeader() {
- mDayNamesShort = new String[mDaysPerWeek];
- mDayNamesLong = new String[mDaysPerWeek];
- for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
- int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
- mDayNamesShort[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
- DateUtils.LENGTH_SHORTEST);
- mDayNamesLong[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
- DateUtils.LENGTH_LONG);
- }
-
- TextView label = (TextView) mDayNamesHeader.getChildAt(0);
- if (mShowWeekNumber) {
- label.setVisibility(View.VISIBLE);
- } else {
- label.setVisibility(View.GONE);
- }
- for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
- label = (TextView) mDayNamesHeader.getChildAt(i);
- if (mWeekDayTextAppearanceResId > -1) {
- label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
- }
- if (i < mDaysPerWeek + 1) {
- label.setText(mDayNamesShort[i - 1]);
- label.setContentDescription(mDayNamesLong[i - 1]);
- label.setVisibility(View.VISIBLE);
- } else {
- label.setVisibility(View.GONE);
- }
- }
- mDayNamesHeader.invalidate();
- }
-
- /**
- * Sets all the required fields for the list view.
- */
- private void setUpListView() {
- // Configure the listview
- mListView.setDivider(null);
- mListView.setItemsCanFocus(true);
- mListView.setVerticalScrollBarEnabled(false);
- mListView.setOnScrollListener(new OnScrollListener() {
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- LegacyCalendarViewDelegate.this.onScrollStateChanged(view, scrollState);
- }
-
- public void onScroll(
- AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- LegacyCalendarViewDelegate.this.onScroll(view, firstVisibleItem,
- visibleItemCount, totalItemCount);
- }
- });
- // Make the scrolling behavior nicer
- mListView.setFriction(mFriction);
- mListView.setVelocityScale(mVelocityScale);
- }
-
- /**
- * This moves to the specified time in the view. If the time is not already
- * in range it will move the list so that the first of the month containing
- * the time is at the top of the view. If the new time is already in view
- * the list will not be scrolled unless forceScroll is true. This time may
- * optionally be highlighted as selected as well.
- *
- * @param date The time to move to.
- * @param animate Whether to scroll to the given time or just redraw at the
- * new location.
- * @param setSelected Whether to set the given time as selected.
- * @param forceScroll Whether to recenter even if the time is already
- * visible.
- *
- * @throws IllegalArgumentException of the provided date is before the
- * range start of after the range end.
- */
- private void goTo(Calendar date, boolean animate, boolean setSelected,
- boolean forceScroll) {
- if (date.before(mMinDate) || date.after(mMaxDate)) {
- throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
- + " and " + mMaxDate.getTime());
- }
- // Find the first and last entirely visible weeks
- int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
- View firstChild = mListView.getChildAt(0);
- if (firstChild != null && firstChild.getTop() < 0) {
- firstFullyVisiblePosition++;
- }
- int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
- if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
- lastFullyVisiblePosition--;
- }
- if (setSelected) {
- mAdapter.setSelectedDay(date);
- }
- // Get the week we're going to
- int position = getWeeksSinceMinDate(date);
-
- // Check if the selected day is now outside of our visible range
- // and if so scroll to the month that contains it
- if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
- || forceScroll) {
- mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
- mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
-
- setMonthDisplayed(mFirstDayOfMonth);
-
- // the earliest time we can scroll to is the min date
- if (mFirstDayOfMonth.before(mMinDate)) {
- position = 0;
- } else {
- position = getWeeksSinceMinDate(mFirstDayOfMonth);
- }
-
- mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
- if (animate) {
- mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
- GOTO_SCROLL_DURATION);
- } else {
- mListView.setSelectionFromTop(position, mListScrollTopOffset);
- // Perform any after scroll operations that are needed
- onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
- }
- } else if (setSelected) {
- // Otherwise just set the selection
- setMonthDisplayed(date);
- }
- }
/**
* Parses the given <code>date</code> and in case of success sets
@@ -1444,718 +607,15 @@ public class CalendarView extends FrameLayout {
*
* @return True if the date was parsed.
*/
- private boolean parseDate(String date, Calendar outDate) {
+ protected boolean parseDate(String date, Calendar outDate) {
try {
- outDate.setTime(mDateFormat.parse(date));
+ outDate.setTime(DATE_FORMATTER.parse(date));
return true;
} catch (ParseException e) {
Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
return false;
}
}
-
- /**
- * Called when a <code>view</code> transitions to a new <code>scrollState
- * </code>.
- */
- private void onScrollStateChanged(AbsListView view, int scrollState) {
- mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
- }
-
- /**
- * Updates the title and selected month if the <code>view</code> has moved to a new
- * month.
- */
- private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- WeekView child = (WeekView) view.getChildAt(0);
- if (child == null) {
- return;
- }
-
- // Figure out where we are
- long currScroll =
- view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
-
- // If we have moved since our last call update the direction
- if (currScroll < mPreviousScrollPosition) {
- mIsScrollingUp = true;
- } else if (currScroll > mPreviousScrollPosition) {
- mIsScrollingUp = false;
- } else {
- return;
- }
-
- // Use some hysteresis for checking which month to highlight. This
- // causes the month to transition when two full weeks of a month are
- // visible when scrolling up, and when the first day in a month reaches
- // the top of the screen when scrolling down.
- int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
- if (mIsScrollingUp) {
- child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
- } else if (offset != 0) {
- child = (WeekView) view.getChildAt(offset);
- }
-
- if (child != null) {
- // Find out which month we're moving into
- int month;
- if (mIsScrollingUp) {
- month = child.getMonthOfFirstWeekDay();
- } else {
- month = child.getMonthOfLastWeekDay();
- }
-
- // And how it relates to our current highlighted month
- int monthDiff;
- if (mCurrentMonthDisplayed == 11 && month == 0) {
- monthDiff = 1;
- } else if (mCurrentMonthDisplayed == 0 && month == 11) {
- monthDiff = -1;
- } else {
- monthDiff = month - mCurrentMonthDisplayed;
- }
-
- // Only switch months if we're scrolling away from the currently
- // selected month
- if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
- Calendar firstDay = child.getFirstDay();
- if (mIsScrollingUp) {
- firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
- } else {
- firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
- }
- setMonthDisplayed(firstDay);
- }
- }
- mPreviousScrollPosition = currScroll;
- mPreviousScrollState = mCurrentScrollState;
- }
-
- /**
- * Sets the month displayed at the top of this view based on time. Override
- * to add custom events when the title is changed.
- *
- * @param calendar A day in the new focus month.
- */
- private void setMonthDisplayed(Calendar calendar) {
- mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
- mAdapter.setFocusMonth(mCurrentMonthDisplayed);
- final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
- | DateUtils.FORMAT_SHOW_YEAR;
- final long millis = calendar.getTimeInMillis();
- String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
- mMonthName.setText(newMonthName);
- mMonthName.invalidate();
- }
-
- /**
- * @return Returns the number of weeks between the current <code>date</code>
- * and the <code>mMinDate</code>.
- */
- private int getWeeksSinceMinDate(Calendar date) {
- if (date.before(mMinDate)) {
- throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
- + " does not precede toDate: " + date.getTime());
- }
- long endTimeMillis = date.getTimeInMillis()
- + date.getTimeZone().getOffset(date.getTimeInMillis());
- long startTimeMillis = mMinDate.getTimeInMillis()
- + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
- long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
- * MILLIS_IN_DAY;
- return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
- }
-
- /**
- * Command responsible for acting upon scroll state changes.
- */
- private class ScrollStateRunnable implements Runnable {
- private AbsListView mView;
-
- private int mNewState;
-
- /**
- * Sets up the runnable with a short delay in case the scroll state
- * immediately changes again.
- *
- * @param view The list view that changed state
- * @param scrollState The new state it changed to
- */
- public void doScrollStateChange(AbsListView view, int scrollState) {
- mView = view;
- mNewState = scrollState;
- mDelegator.removeCallbacks(this);
- mDelegator.postDelayed(this, SCROLL_CHANGE_DELAY);
- }
-
- public void run() {
- mCurrentScrollState = mNewState;
- // Fix the position after a scroll or a fling ends
- if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
- View child = mView.getChildAt(0);
- if (child == null) {
- // The view is no longer visible, just return
- return;
- }
- int dist = child.getBottom() - mListScrollTopOffset;
- if (dist > mListScrollTopOffset) {
- if (mIsScrollingUp) {
- mView.smoothScrollBy(dist - child.getHeight(),
- ADJUSTMENT_SCROLL_DURATION);
- } else {
- mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
- }
- }
- }
- mPreviousScrollState = mNewState;
- }
- }
-
- /**
- * <p>
- * This is a specialized adapter for creating a list of weeks with
- * selectable days. It can be configured to display the week number, start
- * the week on a given day, show a reduced number of days, or display an
- * arbitrary number of weeks at a time.
- * </p>
- */
- private class WeeksAdapter extends BaseAdapter implements OnTouchListener {
-
- private int mSelectedWeek;
-
- private GestureDetector mGestureDetector;
-
- private int mFocusedMonth;
-
- private final Calendar mSelectedDate = Calendar.getInstance();
-
- private int mTotalWeekCount;
-
- public WeeksAdapter(Context context) {
- mContext = context;
- mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
- init();
- }
-
- /**
- * Set up the gesture detector and selected time
- */
- private void init() {
- mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
- mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
- if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
- || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
- mTotalWeekCount++;
- }
- notifyDataSetChanged();
- }
-
- /**
- * Updates the selected day and related parameters.
- *
- * @param selectedDay The time to highlight
- */
- public void setSelectedDay(Calendar selectedDay) {
- if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
- && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
- return;
- }
- mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
- mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
- mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
- notifyDataSetChanged();
- }
-
- /**
- * @return The selected day of month.
- */
- public Calendar getSelectedDay() {
- return mSelectedDate;
- }
-
- @Override
- public int getCount() {
- return mTotalWeekCount;
- }
-
- @Override
- public Object getItem(int position) {
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- WeekView weekView = null;
- if (convertView != null) {
- weekView = (WeekView) convertView;
- } else {
- weekView = new WeekView(mContext);
- android.widget.AbsListView.LayoutParams params =
- new android.widget.AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- weekView.setLayoutParams(params);
- weekView.setClickable(true);
- weekView.setOnTouchListener(this);
- }
-
- int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
- Calendar.DAY_OF_WEEK) : -1;
- weekView.init(position, selectedWeekDay, mFocusedMonth);
-
- return weekView;
- }
-
- /**
- * Changes which month is in focus and updates the view.
- *
- * @param month The month to show as in focus [0-11]
- */
- public void setFocusMonth(int month) {
- if (mFocusedMonth == month) {
- return;
- }
- mFocusedMonth = month;
- notifyDataSetChanged();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
- WeekView weekView = (WeekView) v;
- // if we cannot find a day for the given location we are done
- if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
- return true;
- }
- // it is possible that the touched day is outside the valid range
- // we draw whole weeks but range end can fall not on the week end
- if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
- return true;
- }
- onDateTapped(mTempDate);
- return true;
- }
- return false;
- }
-
- /**
- * Maintains the same hour/min/sec but moves the day to the tapped day.
- *
- * @param day The day that was tapped
- */
- private void onDateTapped(Calendar day) {
- setSelectedDay(day);
- setMonthDisplayed(day);
- }
-
- /**
- * This is here so we can identify single tap events and set the
- * selected day correctly
- */
- class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return true;
- }
- }
- }
-
- /**
- * <p>
- * This is a dynamic view for drawing a single week. It can be configured to
- * display the week number, start the week on a given day, or show a reduced
- * number of days. It is intended for use as a single view within a
- * ListView. See {@link WeeksAdapter} for usage.
- * </p>
- */
- private class WeekView extends View {
-
- private final Rect mTempRect = new Rect();
-
- private final Paint mDrawPaint = new Paint();
-
- private final Paint mMonthNumDrawPaint = new Paint();
-
- // Cache the number strings so we don't have to recompute them each time
- private String[] mDayNumbers;
-
- // Quick lookup for checking which days are in the focus month
- private boolean[] mFocusDay;
-
- // Whether this view has a focused day.
- private boolean mHasFocusedDay;
-
- // Whether this view has only focused days.
- private boolean mHasUnfocusedDay;
-
- // The first day displayed by this item
- private Calendar mFirstDay;
-
- // The month of the first day in this week
- private int mMonthOfFirstWeekDay = -1;
-
- // The month of the last day in this week
- private int mLastWeekDayMonth = -1;
-
- // The position of this week, equivalent to weeks since the week of Jan
- // 1st, 1900
- private int mWeek = -1;
-
- // Quick reference to the width of this view, matches parent
- private int mWidth;
-
- // The height this view should draw at in pixels, set by height param
- private int mHeight;
-
- // If this view contains the selected day
- private boolean mHasSelectedDay = false;
-
- // Which day is selected [0-6] or -1 if no day is selected
- private int mSelectedDay = -1;
-
- // The number of days + a spot for week number if it is displayed
- private int mNumCells;
-
- // The left edge of the selected day
- private int mSelectedLeft = -1;
-
- // The right edge of the selected day
- private int mSelectedRight = -1;
-
- public WeekView(Context context) {
- super(context);
-
- // Sets up any standard paints that will be used
- initilaizePaints();
- }
-
- /**
- * Initializes this week view.
- *
- * @param weekNumber The number of the week this view represents. The
- * week number is a zero based index of the weeks since
- * {@link CalendarView#getMinDate()}.
- * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
- * selected day.
- * @param focusedMonth The month that is currently in focus i.e.
- * highlighted.
- */
- public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
- mSelectedDay = selectedWeekDay;
- mHasSelectedDay = mSelectedDay != -1;
- mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
- mWeek = weekNumber;
- mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
-
- mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
- mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
-
- // Allocate space for caching the day numbers and focus values
- mDayNumbers = new String[mNumCells];
- mFocusDay = new boolean[mNumCells];
-
- // If we're showing the week number calculate it based on Monday
- int i = 0;
- if (mShowWeekNumber) {
- mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
- mTempDate.get(Calendar.WEEK_OF_YEAR));
- i++;
- }
-
- // Now adjust our starting day based on the start day of the week
- int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
- mTempDate.add(Calendar.DAY_OF_MONTH, diff);
-
- mFirstDay = (Calendar) mTempDate.clone();
- mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
-
- mHasUnfocusedDay = true;
- for (; i < mNumCells; i++) {
- final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
- mFocusDay[i] = isFocusedDay;
- mHasFocusedDay |= isFocusedDay;
- mHasUnfocusedDay &= !isFocusedDay;
- // do not draw dates outside the valid range to avoid user confusion
- if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
- mDayNumbers[i] = "";
- } else {
- mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
- mTempDate.get(Calendar.DAY_OF_MONTH));
- }
- mTempDate.add(Calendar.DAY_OF_MONTH, 1);
- }
- // We do one extra add at the end of the loop, if that pushed us to
- // new month undo it
- if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
- mTempDate.add(Calendar.DAY_OF_MONTH, -1);
- }
- mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
-
- updateSelectionPositions();
- }
-
- /**
- * Initialize the paint instances.
- */
- private void initilaizePaints() {
- mDrawPaint.setFakeBoldText(false);
- mDrawPaint.setAntiAlias(true);
- mDrawPaint.setStyle(Style.FILL);
-
- mMonthNumDrawPaint.setFakeBoldText(true);
- mMonthNumDrawPaint.setAntiAlias(true);
- mMonthNumDrawPaint.setStyle(Style.FILL);
- mMonthNumDrawPaint.setTextAlign(Align.CENTER);
- mMonthNumDrawPaint.setTextSize(mDateTextSize);
- }
-
- /**
- * Returns the month of the first day in this week.
- *
- * @return The month the first day of this view is in.
- */
- public int getMonthOfFirstWeekDay() {
- return mMonthOfFirstWeekDay;
- }
-
- /**
- * Returns the month of the last day in this week
- *
- * @return The month the last day of this view is in
- */
- public int getMonthOfLastWeekDay() {
- return mLastWeekDayMonth;
- }
-
- /**
- * Returns the first day in this view.
- *
- * @return The first day in the view.
- */
- public Calendar getFirstDay() {
- return mFirstDay;
- }
-
- /**
- * Calculates the day that the given x position is in, accounting for
- * week number.
- *
- * @param x The x position of the touch event.
- * @return True if a day was found for the given location.
- */
- public boolean getDayFromLocation(float x, Calendar outCalendar) {
- final boolean isLayoutRtl = isLayoutRtl();
-
- int start;
- int end;
-
- if (isLayoutRtl) {
- start = 0;
- end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
- } else {
- start = mShowWeekNumber ? mWidth / mNumCells : 0;
- end = mWidth;
- }
-
- if (x < start || x > end) {
- outCalendar.clear();
- return false;
- }
-
- // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
- int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
-
- if (isLayoutRtl) {
- dayPosition = mDaysPerWeek - 1 - dayPosition;
- }
-
- outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
- outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
-
- return true;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- drawBackground(canvas);
- drawWeekNumbersAndDates(canvas);
- drawWeekSeparators(canvas);
- drawSelectedDateVerticalBars(canvas);
- }
-
- /**
- * This draws the selection highlight if a day is selected in this week.
- *
- * @param canvas The canvas to draw on
- */
- private void drawBackground(Canvas canvas) {
- if (!mHasSelectedDay) {
- return;
- }
- mDrawPaint.setColor(mSelectedWeekBackgroundColor);
-
- mTempRect.top = mWeekSeperatorLineWidth;
- mTempRect.bottom = mHeight;
-
- final boolean isLayoutRtl = isLayoutRtl();
-
- if (isLayoutRtl) {
- mTempRect.left = 0;
- mTempRect.right = mSelectedLeft - 2;
- } else {
- mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
- mTempRect.right = mSelectedLeft - 2;
- }
- canvas.drawRect(mTempRect, mDrawPaint);
-
- if (isLayoutRtl) {
- mTempRect.left = mSelectedRight + 3;
- mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
- } else {
- mTempRect.left = mSelectedRight + 3;
- mTempRect.right = mWidth;
- }
- canvas.drawRect(mTempRect, mDrawPaint);
- }
-
- /**
- * Draws the week and month day numbers for this week.
- *
- * @param canvas The canvas to draw on
- */
- private void drawWeekNumbersAndDates(Canvas canvas) {
- final float textHeight = mDrawPaint.getTextSize();
- final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
- final int nDays = mNumCells;
- final int divisor = 2 * nDays;
-
- mDrawPaint.setTextAlign(Align.CENTER);
- mDrawPaint.setTextSize(mDateTextSize);
-
- int i = 0;
-
- if (isLayoutRtl()) {
- for (; i < nDays - 1; i++) {
- mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
- : mUnfocusedMonthDateColor);
- int x = (2 * i + 1) * mWidth / divisor;
- canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
- }
- if (mShowWeekNumber) {
- mDrawPaint.setColor(mWeekNumberColor);
- int x = mWidth - mWidth / divisor;
- canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
- }
- } else {
- if (mShowWeekNumber) {
- mDrawPaint.setColor(mWeekNumberColor);
- int x = mWidth / divisor;
- canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
- i++;
- }
- for (; i < nDays; i++) {
- mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
- : mUnfocusedMonthDateColor);
- int x = (2 * i + 1) * mWidth / divisor;
- canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
- }
- }
- }
-
- /**
- * Draws a horizontal line for separating the weeks.
- *
- * @param canvas The canvas to draw on.
- */
- private void drawWeekSeparators(Canvas canvas) {
- // If it is the topmost fully visible child do not draw separator line
- int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
- if (mListView.getChildAt(0).getTop() < 0) {
- firstFullyVisiblePosition++;
- }
- if (firstFullyVisiblePosition == mWeek) {
- return;
- }
- mDrawPaint.setColor(mWeekSeparatorLineColor);
- mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
- float startX;
- float stopX;
- if (isLayoutRtl()) {
- startX = 0;
- stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
- } else {
- startX = mShowWeekNumber ? mWidth / mNumCells : 0;
- stopX = mWidth;
- }
- canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
- }
-
- /**
- * Draws the selected date bars if this week has a selected day.
- *
- * @param canvas The canvas to draw on
- */
- private void drawSelectedDateVerticalBars(Canvas canvas) {
- if (!mHasSelectedDay) {
- return;
- }
- mSelectedDateVerticalBar.setBounds(
- mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
- mWeekSeperatorLineWidth,
- mSelectedLeft + mSelectedDateVerticalBarWidth / 2,
- mHeight);
- mSelectedDateVerticalBar.draw(canvas);
- mSelectedDateVerticalBar.setBounds(
- mSelectedRight - mSelectedDateVerticalBarWidth / 2,
- mWeekSeperatorLineWidth,
- mSelectedRight + mSelectedDateVerticalBarWidth / 2,
- mHeight);
- mSelectedDateVerticalBar.draw(canvas);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mWidth = w;
- updateSelectionPositions();
- }
-
- /**
- * This calculates the positions for the selected day lines.
- */
- private void updateSelectionPositions() {
- if (mHasSelectedDay) {
- final boolean isLayoutRtl = isLayoutRtl();
- int selectedPosition = mSelectedDay - mFirstDayOfWeek;
- if (selectedPosition < 0) {
- selectedPosition += 7;
- }
- if (mShowWeekNumber && !isLayoutRtl) {
- selectedPosition++;
- }
- if (isLayoutRtl) {
- mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
-
- } else {
- mSelectedLeft = selectedPosition * mWidth / mNumCells;
- }
- mSelectedRight = mSelectedLeft + mWidth / mNumCells;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
- .getPaddingBottom()) / mShownWeekCount;
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
- }
- }
-
}
}
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
new file mode 100644
index 0000000..2ab3548
--- /dev/null
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -0,0 +1,1527 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.R;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+import libcore.icu.LocaleData;
+
+/**
+ * A delegate implementing the legacy CalendarView
+ */
+class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelegate {
+ /**
+ * Default value whether to show week number.
+ */
+ private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
+
+ /**
+ * The number of milliseconds in a day.e
+ */
+ private static final long MILLIS_IN_DAY = 86400000L;
+
+ /**
+ * The number of day in a week.
+ */
+ private static final int DAYS_PER_WEEK = 7;
+
+ /**
+ * The number of milliseconds in a week.
+ */
+ private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
+
+ /**
+ * Affects when the month selection will change while scrolling upe
+ */
+ private static final int SCROLL_HYST_WEEKS = 2;
+
+ /**
+ * How long the GoTo fling animation should last.
+ */
+ private static final int GOTO_SCROLL_DURATION = 1000;
+
+ /**
+ * The duration of the adjustment upon a user scroll in milliseconds.
+ */
+ private static final int ADJUSTMENT_SCROLL_DURATION = 500;
+
+ /**
+ * How long to wait after receiving an onScrollStateChanged notification
+ * before acting on it.
+ */
+ private static final int SCROLL_CHANGE_DELAY = 40;
+
+ private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
+
+ private static final int DEFAULT_DATE_TEXT_SIZE = 14;
+
+ private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
+
+ private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
+
+ private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
+
+ private static final int UNSCALED_BOTTOM_BUFFER = 20;
+
+ private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
+
+ private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
+
+ private final int mWeekSeperatorLineWidth;
+
+ private int mDateTextSize;
+
+ private Drawable mSelectedDateVerticalBar;
+
+ private final int mSelectedDateVerticalBarWidth;
+
+ private int mSelectedWeekBackgroundColor;
+
+ private int mFocusedMonthDateColor;
+
+ private int mUnfocusedMonthDateColor;
+
+ private int mWeekSeparatorLineColor;
+
+ private int mWeekNumberColor;
+
+ private int mWeekDayTextAppearanceResId;
+
+ private int mDateTextAppearanceResId;
+
+ /**
+ * The top offset of the weeks list.
+ */
+ private int mListScrollTopOffset = 2;
+
+ /**
+ * The visible height of a week view.
+ */
+ private int mWeekMinVisibleHeight = 12;
+
+ /**
+ * The visible height of a week view.
+ */
+ private int mBottomBuffer = 20;
+
+ /**
+ * The number of shown weeks.
+ */
+ private int mShownWeekCount;
+
+ /**
+ * Flag whether to show the week number.
+ */
+ private boolean mShowWeekNumber;
+
+ /**
+ * The number of day per week to be shown.
+ */
+ private int mDaysPerWeek = 7;
+
+ /**
+ * The friction of the week list while flinging.
+ */
+ private float mFriction = .05f;
+
+ /**
+ * Scale for adjusting velocity of the week list while flinging.
+ */
+ private float mVelocityScale = 0.333f;
+
+ /**
+ * The adapter for the weeks list.
+ */
+ private WeeksAdapter mAdapter;
+
+ /**
+ * The weeks list.
+ */
+ private ListView mListView;
+
+ /**
+ * The name of the month to display.
+ */
+ private TextView mMonthName;
+
+ /**
+ * The header with week day names.
+ */
+ private ViewGroup mDayNamesHeader;
+
+ /**
+ * Cached abbreviations for day of week names.
+ */
+ private String[] mDayNamesShort;
+
+ /**
+ * Cached full-length day of week names.
+ */
+ private String[] mDayNamesLong;
+
+ /**
+ * The first day of the week.
+ */
+ private int mFirstDayOfWeek;
+
+ /**
+ * Which month should be displayed/highlighted [0-11].
+ */
+ private int mCurrentMonthDisplayed = -1;
+
+ /**
+ * Used for tracking during a scroll.
+ */
+ private long mPreviousScrollPosition;
+
+ /**
+ * Used for tracking which direction the view is scrolling.
+ */
+ private boolean mIsScrollingUp = false;
+
+ /**
+ * The previous scroll state of the weeks ListView.
+ */
+ private int mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
+
+ /**
+ * The current scroll state of the weeks ListView.
+ */
+ private int mCurrentScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
+
+ /**
+ * Listener for changes in the selected day.
+ */
+ private CalendarView.OnDateChangeListener mOnDateChangeListener;
+
+ /**
+ * Command for adjusting the position after a scroll/fling.
+ */
+ private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
+
+ /**
+ * Temporary instance to avoid multiple instantiations.
+ */
+ private Calendar mTempDate;
+
+ /**
+ * The first day of the focused month.
+ */
+ private Calendar mFirstDayOfMonth;
+
+ /**
+ * The start date of the range supported by this picker.
+ */
+ private Calendar mMinDate;
+
+ /**
+ * The end date of the range supported by this picker.
+ */
+ private Calendar mMaxDate;
+
+ CalendarViewLegacyDelegate(CalendarView delegator, Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(delegator, context);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.CalendarView, defStyleAttr, defStyleRes);
+ mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
+ DEFAULT_SHOW_WEEK_NUMBER);
+ mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+ LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ final String minDate = a.getString(R.styleable.CalendarView_minDate);
+ if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
+ parseDate(DEFAULT_MIN_DATE, mMinDate);
+ }
+ final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
+ if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
+ parseDate(DEFAULT_MAX_DATE, mMaxDate);
+ }
+ if (mMaxDate.before(mMinDate)) {
+ throw new IllegalArgumentException("Max date cannot be before min date.");
+ }
+ mShownWeekCount = a.getInt(R.styleable.CalendarView_shownWeekCount,
+ DEFAULT_SHOWN_WEEK_COUNT);
+ mSelectedWeekBackgroundColor = a.getColor(
+ R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
+ mFocusedMonthDateColor = a.getColor(
+ R.styleable.CalendarView_focusedMonthDateColor, 0);
+ mUnfocusedMonthDateColor = a.getColor(
+ R.styleable.CalendarView_unfocusedMonthDateColor, 0);
+ mWeekSeparatorLineColor = a.getColor(
+ R.styleable.CalendarView_weekSeparatorLineColor, 0);
+ mWeekNumberColor = a.getColor(R.styleable.CalendarView_weekNumberColor, 0);
+ mSelectedDateVerticalBar = a.getDrawable(
+ R.styleable.CalendarView_selectedDateVerticalBar);
+
+ mDateTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
+ updateDateTextSize();
+
+ mWeekDayTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_weekDayTextAppearance,
+ DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+ a.recycle();
+
+ DisplayMetrics displayMetrics = mDelegator.getResources().getDisplayMetrics();
+ mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
+ mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
+ mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_BOTTOM_BUFFER, displayMetrics);
+ mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
+ mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
+
+ LayoutInflater layoutInflater = (LayoutInflater) mContext
+ .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
+ View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
+ mDelegator.addView(content);
+
+ mListView = (ListView) mDelegator.findViewById(R.id.list);
+ mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names);
+ mMonthName = (TextView) content.findViewById(R.id.month_name);
+
+ setUpHeader();
+ setUpListView();
+ setUpAdapter();
+
+ // go to today or whichever is close to today min or max date
+ mTempDate.setTimeInMillis(System.currentTimeMillis());
+ if (mTempDate.before(mMinDate)) {
+ goTo(mMinDate, false, true, true);
+ } else if (mMaxDate.before(mTempDate)) {
+ goTo(mMaxDate, false, true, true);
+ } else {
+ goTo(mTempDate, false, true, true);
+ }
+
+ mDelegator.invalidate();
+ }
+
+ @Override
+ public void setShownWeekCount(int count) {
+ if (mShownWeekCount != count) {
+ mShownWeekCount = count;
+ mDelegator.invalidate();
+ }
+ }
+
+ @Override
+ public int getShownWeekCount() {
+ return mShownWeekCount;
+ }
+
+ @Override
+ public void setSelectedWeekBackgroundColor(int color) {
+ if (mSelectedWeekBackgroundColor != color) {
+ mSelectedWeekBackgroundColor = color;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasSelectedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getSelectedWeekBackgroundColor() {
+ return mSelectedWeekBackgroundColor;
+ }
+
+ @Override
+ public void setFocusedMonthDateColor(int color) {
+ if (mFocusedMonthDateColor != color) {
+ mFocusedMonthDateColor = color;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasFocusedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getFocusedMonthDateColor() {
+ return mFocusedMonthDateColor;
+ }
+
+ @Override
+ public void setUnfocusedMonthDateColor(int color) {
+ if (mUnfocusedMonthDateColor != color) {
+ mUnfocusedMonthDateColor = color;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasUnfocusedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getUnfocusedMonthDateColor() {
+ return mFocusedMonthDateColor;
+ }
+
+ @Override
+ public void setWeekNumberColor(int color) {
+ if (mWeekNumberColor != color) {
+ mWeekNumberColor = color;
+ if (mShowWeekNumber) {
+ invalidateAllWeekViews();
+ }
+ }
+ }
+
+ @Override
+ public int getWeekNumberColor() {
+ return mWeekNumberColor;
+ }
+
+ @Override
+ public void setWeekSeparatorLineColor(int color) {
+ if (mWeekSeparatorLineColor != color) {
+ mWeekSeparatorLineColor = color;
+ invalidateAllWeekViews();
+ }
+ }
+
+ @Override
+ public int getWeekSeparatorLineColor() {
+ return mWeekSeparatorLineColor;
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(int resourceId) {
+ Drawable drawable = mDelegator.getContext().getDrawable(resourceId);
+ setSelectedDateVerticalBar(drawable);
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(Drawable drawable) {
+ if (mSelectedDateVerticalBar != drawable) {
+ mSelectedDateVerticalBar = drawable;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasSelectedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Drawable getSelectedDateVerticalBar() {
+ return mSelectedDateVerticalBar;
+ }
+
+ @Override
+ public void setWeekDayTextAppearance(int resourceId) {
+ if (mWeekDayTextAppearanceResId != resourceId) {
+ mWeekDayTextAppearanceResId = resourceId;
+ setUpHeader();
+ }
+ }
+
+ @Override
+ public int getWeekDayTextAppearance() {
+ return mWeekDayTextAppearanceResId;
+ }
+
+ @Override
+ public void setDateTextAppearance(int resourceId) {
+ if (mDateTextAppearanceResId != resourceId) {
+ mDateTextAppearanceResId = resourceId;
+ updateDateTextSize();
+ invalidateAllWeekViews();
+ }
+ }
+
+ @Override
+ public int getDateTextAppearance() {
+ return mDateTextAppearanceResId;
+ }
+
+ @Override
+ public void setMinDate(long minDate) {
+ mTempDate.setTimeInMillis(minDate);
+ if (isSameDate(mTempDate, mMinDate)) {
+ return;
+ }
+ mMinDate.setTimeInMillis(minDate);
+ // make sure the current date is not earlier than
+ // the new min date since the latter is used for
+ // calculating the indices in the adapter thus
+ // avoiding out of bounds error
+ Calendar date = mAdapter.mSelectedDate;
+ if (date.before(mMinDate)) {
+ mAdapter.setSelectedDay(mMinDate);
+ }
+ // reinitialize the adapter since its range depends on min date
+ mAdapter.init();
+ if (date.before(mMinDate)) {
+ setDate(mTempDate.getTimeInMillis());
+ } else {
+ // we go to the current date to force the ListView to query its
+ // adapter for the shown views since we have changed the adapter
+ // range and the base from which the later calculates item indices
+ // note that calling setDate will not work since the date is the same
+ goTo(date, false, true, false);
+ }
+ }
+
+ @Override
+ public long getMinDate() {
+ return mMinDate.getTimeInMillis();
+ }
+
+ @Override
+ public void setMaxDate(long maxDate) {
+ mTempDate.setTimeInMillis(maxDate);
+ if (isSameDate(mTempDate, mMaxDate)) {
+ return;
+ }
+ mMaxDate.setTimeInMillis(maxDate);
+ // reinitialize the adapter since its range depends on max date
+ mAdapter.init();
+ Calendar date = mAdapter.mSelectedDate;
+ if (date.after(mMaxDate)) {
+ setDate(mMaxDate.getTimeInMillis());
+ } else {
+ // we go to the current date to force the ListView to query its
+ // adapter for the shown views since we have changed the adapter
+ // range and the base from which the later calculates item indices
+ // note that calling setDate will not work since the date is the same
+ goTo(date, false, true, false);
+ }
+ }
+
+ @Override
+ public long getMaxDate() {
+ return mMaxDate.getTimeInMillis();
+ }
+
+ @Override
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ if (mShowWeekNumber == showWeekNumber) {
+ return;
+ }
+ mShowWeekNumber = showWeekNumber;
+ mAdapter.notifyDataSetChanged();
+ setUpHeader();
+ }
+
+ @Override
+ public boolean getShowWeekNumber() {
+ return mShowWeekNumber;
+ }
+
+ @Override
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ if (mFirstDayOfWeek == firstDayOfWeek) {
+ return;
+ }
+ mFirstDayOfWeek = firstDayOfWeek;
+ mAdapter.init();
+ mAdapter.notifyDataSetChanged();
+ setUpHeader();
+ }
+
+ @Override
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
+ @Override
+ public void setDate(long date) {
+ setDate(date, false, false);
+ }
+
+ @Override
+ public void setDate(long date, boolean animate, boolean center) {
+ mTempDate.setTimeInMillis(date);
+ if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
+ return;
+ }
+ goTo(mTempDate, animate, true, center);
+ }
+
+ @Override
+ public long getDate() {
+ return mAdapter.mSelectedDate.getTimeInMillis();
+ }
+
+ @Override
+ public void setOnDateChangeListener(CalendarView.OnDateChangeListener listener) {
+ mOnDateChangeListener = listener;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ setCurrentLocale(newConfig.locale);
+ }
+
+ /**
+ * Sets the current locale.
+ *
+ * @param locale The current locale.
+ */
+ @Override
+ protected void setCurrentLocale(Locale locale) {
+ super.setCurrentLocale(locale);
+
+ mTempDate = getCalendarForLocale(mTempDate, locale);
+ mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
+ mMinDate = getCalendarForLocale(mMinDate, locale);
+ mMaxDate = getCalendarForLocale(mMaxDate, locale);
+ }
+ private void updateDateTextSize() {
+ TypedArray dateTextAppearance = mDelegator.getContext().obtainStyledAttributes(
+ mDateTextAppearanceResId, R.styleable.TextAppearance);
+ mDateTextSize = dateTextAppearance.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
+ dateTextAppearance.recycle();
+ }
+
+ /**
+ * Invalidates all week views.
+ */
+ private void invalidateAllWeekViews() {
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View view = mListView.getChildAt(i);
+ view.invalidate();
+ }
+ }
+
+ /**
+ * Gets a calendar for locale bootstrapped with the value of a given calendar.
+ *
+ * @param oldCalendar The old calendar.
+ * @param locale The locale.
+ */
+ private static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+ if (oldCalendar == null) {
+ return Calendar.getInstance(locale);
+ } else {
+ final long currentTimeMillis = oldCalendar.getTimeInMillis();
+ Calendar newCalendar = Calendar.getInstance(locale);
+ newCalendar.setTimeInMillis(currentTimeMillis);
+ return newCalendar;
+ }
+ }
+
+ /**
+ * @return True if the <code>firstDate</code> is the same as the <code>
+ * secondDate</code>.
+ */
+ private static boolean isSameDate(Calendar firstDate, Calendar secondDate) {
+ return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
+ && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
+ }
+
+ /**
+ * Creates a new adapter if necessary and sets up its parameters.
+ */
+ private void setUpAdapter() {
+ if (mAdapter == null) {
+ mAdapter = new WeeksAdapter(mContext);
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ if (mOnDateChangeListener != null) {
+ Calendar selectedDay = mAdapter.getSelectedDay();
+ mOnDateChangeListener.onSelectedDayChange(mDelegator,
+ selectedDay.get(Calendar.YEAR),
+ selectedDay.get(Calendar.MONTH),
+ selectedDay.get(Calendar.DAY_OF_MONTH));
+ }
+ }
+ });
+ mListView.setAdapter(mAdapter);
+ }
+
+ // refresh the view with the new parameters
+ mAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Sets up the strings to be used by the header.
+ */
+ private void setUpHeader() {
+ mDayNamesShort = new String[mDaysPerWeek];
+ mDayNamesLong = new String[mDaysPerWeek];
+ for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
+ int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
+ mDayNamesShort[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
+ DateUtils.LENGTH_SHORTEST);
+ mDayNamesLong[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
+ DateUtils.LENGTH_LONG);
+ }
+
+ TextView label = (TextView) mDayNamesHeader.getChildAt(0);
+ if (mShowWeekNumber) {
+ label.setVisibility(View.VISIBLE);
+ } else {
+ label.setVisibility(View.GONE);
+ }
+ for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
+ label = (TextView) mDayNamesHeader.getChildAt(i);
+ if (mWeekDayTextAppearanceResId > -1) {
+ label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
+ }
+ if (i < mDaysPerWeek + 1) {
+ label.setText(mDayNamesShort[i - 1]);
+ label.setContentDescription(mDayNamesLong[i - 1]);
+ label.setVisibility(View.VISIBLE);
+ } else {
+ label.setVisibility(View.GONE);
+ }
+ }
+ mDayNamesHeader.invalidate();
+ }
+
+ /**
+ * Sets all the required fields for the list view.
+ */
+ private void setUpListView() {
+ // Configure the listview
+ mListView.setDivider(null);
+ mListView.setItemsCanFocus(true);
+ mListView.setVerticalScrollBarEnabled(false);
+ mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ CalendarViewLegacyDelegate.this.onScrollStateChanged(view, scrollState);
+ }
+
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ CalendarViewLegacyDelegate.this.onScroll(view, firstVisibleItem,
+ visibleItemCount, totalItemCount);
+ }
+ });
+ // Make the scrolling behavior nicer
+ mListView.setFriction(mFriction);
+ mListView.setVelocityScale(mVelocityScale);
+ }
+
+ /**
+ * This moves to the specified time in the view. If the time is not already
+ * in range it will move the list so that the first of the month containing
+ * the time is at the top of the view. If the new time is already in view
+ * the list will not be scrolled unless forceScroll is true. This time may
+ * optionally be highlighted as selected as well.
+ *
+ * @param date The time to move to.
+ * @param animate Whether to scroll to the given time or just redraw at the
+ * new location.
+ * @param setSelected Whether to set the given time as selected.
+ * @param forceScroll Whether to recenter even if the time is already
+ * visible.
+ *
+ * @throws IllegalArgumentException of the provided date is before the
+ * range start of after the range end.
+ */
+ private void goTo(Calendar date, boolean animate, boolean setSelected,
+ boolean forceScroll) {
+ if (date.before(mMinDate) || date.after(mMaxDate)) {
+ throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
+ + " and " + mMaxDate.getTime());
+ }
+ // Find the first and last entirely visible weeks
+ int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+ View firstChild = mListView.getChildAt(0);
+ if (firstChild != null && firstChild.getTop() < 0) {
+ firstFullyVisiblePosition++;
+ }
+ int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
+ if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
+ lastFullyVisiblePosition--;
+ }
+ if (setSelected) {
+ mAdapter.setSelectedDay(date);
+ }
+ // Get the week we're going to
+ int position = getWeeksSinceMinDate(date);
+
+ // Check if the selected day is now outside of our visible range
+ // and if so scroll to the month that contains it
+ if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
+ || forceScroll) {
+ mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
+ mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
+
+ setMonthDisplayed(mFirstDayOfMonth);
+
+ // the earliest time we can scroll to is the min date
+ if (mFirstDayOfMonth.before(mMinDate)) {
+ position = 0;
+ } else {
+ position = getWeeksSinceMinDate(mFirstDayOfMonth);
+ }
+
+ mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_FLING;
+ if (animate) {
+ mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
+ GOTO_SCROLL_DURATION);
+ } else {
+ mListView.setSelectionFromTop(position, mListScrollTopOffset);
+ // Perform any after scroll operations that are needed
+ onScrollStateChanged(mListView, AbsListView.OnScrollListener.SCROLL_STATE_IDLE);
+ }
+ } else if (setSelected) {
+ // Otherwise just set the selection
+ setMonthDisplayed(date);
+ }
+ }
+
+ /**
+ * Called when a <code>view</code> transitions to a new <code>scrollState
+ * </code>.
+ */
+ private void onScrollStateChanged(AbsListView view, int scrollState) {
+ mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
+ }
+
+ /**
+ * Updates the title and selected month if the <code>view</code> has moved to a new
+ * month.
+ */
+ private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ WeekView child = (WeekView) view.getChildAt(0);
+ if (child == null) {
+ return;
+ }
+
+ // Figure out where we are
+ long currScroll =
+ view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
+
+ // If we have moved since our last call update the direction
+ if (currScroll < mPreviousScrollPosition) {
+ mIsScrollingUp = true;
+ } else if (currScroll > mPreviousScrollPosition) {
+ mIsScrollingUp = false;
+ } else {
+ return;
+ }
+
+ // Use some hysteresis for checking which month to highlight. This
+ // causes the month to transition when two full weeks of a month are
+ // visible when scrolling up, and when the first day in a month reaches
+ // the top of the screen when scrolling down.
+ int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
+ if (mIsScrollingUp) {
+ child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
+ } else if (offset != 0) {
+ child = (WeekView) view.getChildAt(offset);
+ }
+
+ if (child != null) {
+ // Find out which month we're moving into
+ int month;
+ if (mIsScrollingUp) {
+ month = child.getMonthOfFirstWeekDay();
+ } else {
+ month = child.getMonthOfLastWeekDay();
+ }
+
+ // And how it relates to our current highlighted month
+ int monthDiff;
+ if (mCurrentMonthDisplayed == 11 && month == 0) {
+ monthDiff = 1;
+ } else if (mCurrentMonthDisplayed == 0 && month == 11) {
+ monthDiff = -1;
+ } else {
+ monthDiff = month - mCurrentMonthDisplayed;
+ }
+
+ // Only switch months if we're scrolling away from the currently
+ // selected month
+ if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
+ Calendar firstDay = child.getFirstDay();
+ if (mIsScrollingUp) {
+ firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
+ } else {
+ firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
+ }
+ setMonthDisplayed(firstDay);
+ }
+ }
+ mPreviousScrollPosition = currScroll;
+ mPreviousScrollState = mCurrentScrollState;
+ }
+
+ /**
+ * Sets the month displayed at the top of this view based on time. Override
+ * to add custom events when the title is changed.
+ *
+ * @param calendar A day in the new focus month.
+ */
+ private void setMonthDisplayed(Calendar calendar) {
+ mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
+ mAdapter.setFocusMonth(mCurrentMonthDisplayed);
+ final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
+ | DateUtils.FORMAT_SHOW_YEAR;
+ final long millis = calendar.getTimeInMillis();
+ String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
+ mMonthName.setText(newMonthName);
+ mMonthName.invalidate();
+ }
+
+ /**
+ * @return Returns the number of weeks between the current <code>date</code>
+ * and the <code>mMinDate</code>.
+ */
+ private int getWeeksSinceMinDate(Calendar date) {
+ if (date.before(mMinDate)) {
+ throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
+ + " does not precede toDate: " + date.getTime());
+ }
+ long endTimeMillis = date.getTimeInMillis()
+ + date.getTimeZone().getOffset(date.getTimeInMillis());
+ long startTimeMillis = mMinDate.getTimeInMillis()
+ + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
+ long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
+ * MILLIS_IN_DAY;
+ return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
+ }
+
+ /**
+ * Command responsible for acting upon scroll state changes.
+ */
+ private class ScrollStateRunnable implements Runnable {
+ private AbsListView mView;
+
+ private int mNewState;
+
+ /**
+ * Sets up the runnable with a short delay in case the scroll state
+ * immediately changes again.
+ *
+ * @param view The list view that changed state
+ * @param scrollState The new state it changed to
+ */
+ public void doScrollStateChange(AbsListView view, int scrollState) {
+ mView = view;
+ mNewState = scrollState;
+ mDelegator.removeCallbacks(this);
+ mDelegator.postDelayed(this, SCROLL_CHANGE_DELAY);
+ }
+
+ public void run() {
+ mCurrentScrollState = mNewState;
+ // Fix the position after a scroll or a fling ends
+ if (mNewState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE
+ && mPreviousScrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
+ View child = mView.getChildAt(0);
+ if (child == null) {
+ // The view is no longer visible, just return
+ return;
+ }
+ int dist = child.getBottom() - mListScrollTopOffset;
+ if (dist > mListScrollTopOffset) {
+ if (mIsScrollingUp) {
+ mView.smoothScrollBy(dist - child.getHeight(),
+ ADJUSTMENT_SCROLL_DURATION);
+ } else {
+ mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
+ }
+ }
+ }
+ mPreviousScrollState = mNewState;
+ }
+ }
+
+ /**
+ * <p>
+ * This is a specialized adapter for creating a list of weeks with
+ * selectable days. It can be configured to display the week number, start
+ * the week on a given day, show a reduced number of days, or display an
+ * arbitrary number of weeks at a time.
+ * </p>
+ */
+ private class WeeksAdapter extends BaseAdapter implements View.OnTouchListener {
+
+ private int mSelectedWeek;
+
+ private GestureDetector mGestureDetector;
+
+ private int mFocusedMonth;
+
+ private final Calendar mSelectedDate = Calendar.getInstance();
+
+ private int mTotalWeekCount;
+
+ public WeeksAdapter(Context context) {
+ mContext = context;
+ mGestureDetector = new GestureDetector(mContext, new WeeksAdapter.CalendarGestureListener());
+ init();
+ }
+
+ /**
+ * Set up the gesture detector and selected time
+ */
+ private void init() {
+ mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+ mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
+ if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
+ || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
+ mTotalWeekCount++;
+ }
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Updates the selected day and related parameters.
+ *
+ * @param selectedDay The time to highlight
+ */
+ public void setSelectedDay(Calendar selectedDay) {
+ if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
+ && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
+ return;
+ }
+ mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
+ mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+ mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
+ notifyDataSetChanged();
+ }
+
+ /**
+ * @return The selected day of month.
+ */
+ public Calendar getSelectedDay() {
+ return mSelectedDate;
+ }
+
+ @Override
+ public int getCount() {
+ return mTotalWeekCount;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ WeekView weekView = null;
+ if (convertView != null) {
+ weekView = (WeekView) convertView;
+ } else {
+ weekView = new WeekView(mContext);
+ AbsListView.LayoutParams params =
+ new AbsListView.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT);
+ weekView.setLayoutParams(params);
+ weekView.setClickable(true);
+ weekView.setOnTouchListener(this);
+ }
+
+ int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
+ Calendar.DAY_OF_WEEK) : -1;
+ weekView.init(position, selectedWeekDay, mFocusedMonth);
+
+ return weekView;
+ }
+
+ /**
+ * Changes which month is in focus and updates the view.
+ *
+ * @param month The month to show as in focus [0-11]
+ */
+ public void setFocusMonth(int month) {
+ if (mFocusedMonth == month) {
+ return;
+ }
+ mFocusedMonth = month;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
+ WeekView weekView = (WeekView) v;
+ // if we cannot find a day for the given location we are done
+ if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
+ return true;
+ }
+ // it is possible that the touched day is outside the valid range
+ // we draw whole weeks but range end can fall not on the week end
+ if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+ return true;
+ }
+ onDateTapped(mTempDate);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Maintains the same hour/min/sec but moves the day to the tapped day.
+ *
+ * @param day The day that was tapped
+ */
+ private void onDateTapped(Calendar day) {
+ setSelectedDay(day);
+ setMonthDisplayed(day);
+ }
+
+ /**
+ * This is here so we can identify single tap events and set the
+ * selected day correctly
+ */
+ class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * This is a dynamic view for drawing a single week. It can be configured to
+ * display the week number, start the week on a given day, or show a reduced
+ * number of days. It is intended for use as a single view within a
+ * ListView. See {@link WeeksAdapter} for usage.
+ * </p>
+ */
+ private class WeekView extends View {
+
+ private final Rect mTempRect = new Rect();
+
+ private final Paint mDrawPaint = new Paint();
+
+ private final Paint mMonthNumDrawPaint = new Paint();
+
+ // Cache the number strings so we don't have to recompute them each time
+ private String[] mDayNumbers;
+
+ // Quick lookup for checking which days are in the focus month
+ private boolean[] mFocusDay;
+
+ // Whether this view has a focused day.
+ private boolean mHasFocusedDay;
+
+ // Whether this view has only focused days.
+ private boolean mHasUnfocusedDay;
+
+ // The first day displayed by this item
+ private Calendar mFirstDay;
+
+ // The month of the first day in this week
+ private int mMonthOfFirstWeekDay = -1;
+
+ // The month of the last day in this week
+ private int mLastWeekDayMonth = -1;
+
+ // The position of this week, equivalent to weeks since the week of Jan
+ // 1st, 1900
+ private int mWeek = -1;
+
+ // Quick reference to the width of this view, matches parent
+ private int mWidth;
+
+ // The height this view should draw at in pixels, set by height param
+ private int mHeight;
+
+ // If this view contains the selected day
+ private boolean mHasSelectedDay = false;
+
+ // Which day is selected [0-6] or -1 if no day is selected
+ private int mSelectedDay = -1;
+
+ // The number of days + a spot for week number if it is displayed
+ private int mNumCells;
+
+ // The left edge of the selected day
+ private int mSelectedLeft = -1;
+
+ // The right edge of the selected day
+ private int mSelectedRight = -1;
+
+ public WeekView(Context context) {
+ super(context);
+
+ // Sets up any standard paints that will be used
+ initilaizePaints();
+ }
+
+ /**
+ * Initializes this week view.
+ *
+ * @param weekNumber The number of the week this view represents. The
+ * week number is a zero based index of the weeks since
+ * {@link android.widget.CalendarView#getMinDate()}.
+ * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
+ * selected day.
+ * @param focusedMonth The month that is currently in focus i.e.
+ * highlighted.
+ */
+ public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
+ mSelectedDay = selectedWeekDay;
+ mHasSelectedDay = mSelectedDay != -1;
+ mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
+ mWeek = weekNumber;
+ mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
+
+ mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
+ mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
+
+ // Allocate space for caching the day numbers and focus values
+ mDayNumbers = new String[mNumCells];
+ mFocusDay = new boolean[mNumCells];
+
+ // If we're showing the week number calculate it based on Monday
+ int i = 0;
+ if (mShowWeekNumber) {
+ mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
+ mTempDate.get(Calendar.WEEK_OF_YEAR));
+ i++;
+ }
+
+ // Now adjust our starting day based on the start day of the week
+ int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
+ mTempDate.add(Calendar.DAY_OF_MONTH, diff);
+
+ mFirstDay = (Calendar) mTempDate.clone();
+ mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
+
+ mHasUnfocusedDay = true;
+ for (; i < mNumCells; i++) {
+ final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
+ mFocusDay[i] = isFocusedDay;
+ mHasFocusedDay |= isFocusedDay;
+ mHasUnfocusedDay &= !isFocusedDay;
+ // do not draw dates outside the valid range to avoid user confusion
+ if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+ mDayNumbers[i] = "";
+ } else {
+ mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
+ mTempDate.get(Calendar.DAY_OF_MONTH));
+ }
+ mTempDate.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ // We do one extra add at the end of the loop, if that pushed us to
+ // new month undo it
+ if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
+ mTempDate.add(Calendar.DAY_OF_MONTH, -1);
+ }
+ mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
+
+ updateSelectionPositions();
+ }
+
+ /**
+ * Initialize the paint instances.
+ */
+ private void initilaizePaints() {
+ mDrawPaint.setFakeBoldText(false);
+ mDrawPaint.setAntiAlias(true);
+ mDrawPaint.setStyle(Paint.Style.FILL);
+
+ mMonthNumDrawPaint.setFakeBoldText(true);
+ mMonthNumDrawPaint.setAntiAlias(true);
+ mMonthNumDrawPaint.setStyle(Paint.Style.FILL);
+ mMonthNumDrawPaint.setTextAlign(Paint.Align.CENTER);
+ mMonthNumDrawPaint.setTextSize(mDateTextSize);
+ }
+
+ /**
+ * Returns the month of the first day in this week.
+ *
+ * @return The month the first day of this view is in.
+ */
+ public int getMonthOfFirstWeekDay() {
+ return mMonthOfFirstWeekDay;
+ }
+
+ /**
+ * Returns the month of the last day in this week
+ *
+ * @return The month the last day of this view is in
+ */
+ public int getMonthOfLastWeekDay() {
+ return mLastWeekDayMonth;
+ }
+
+ /**
+ * Returns the first day in this view.
+ *
+ * @return The first day in the view.
+ */
+ public Calendar getFirstDay() {
+ return mFirstDay;
+ }
+
+ /**
+ * Calculates the day that the given x position is in, accounting for
+ * week number.
+ *
+ * @param x The x position of the touch event.
+ * @return True if a day was found for the given location.
+ */
+ public boolean getDayFromLocation(float x, Calendar outCalendar) {
+ final boolean isLayoutRtl = isLayoutRtl();
+
+ int start;
+ int end;
+
+ if (isLayoutRtl) {
+ start = 0;
+ end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+ } else {
+ start = mShowWeekNumber ? mWidth / mNumCells : 0;
+ end = mWidth;
+ }
+
+ if (x < start || x > end) {
+ outCalendar.clear();
+ return false;
+ }
+
+ // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
+ int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
+
+ if (isLayoutRtl) {
+ dayPosition = mDaysPerWeek - 1 - dayPosition;
+ }
+
+ outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
+ outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
+
+ return true;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ drawBackground(canvas);
+ drawWeekNumbersAndDates(canvas);
+ drawWeekSeparators(canvas);
+ drawSelectedDateVerticalBars(canvas);
+ }
+
+ /**
+ * This draws the selection highlight if a day is selected in this week.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawBackground(Canvas canvas) {
+ if (!mHasSelectedDay) {
+ return;
+ }
+ mDrawPaint.setColor(mSelectedWeekBackgroundColor);
+
+ mTempRect.top = mWeekSeperatorLineWidth;
+ mTempRect.bottom = mHeight;
+
+ final boolean isLayoutRtl = isLayoutRtl();
+
+ if (isLayoutRtl) {
+ mTempRect.left = 0;
+ mTempRect.right = mSelectedLeft - 2;
+ } else {
+ mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
+ mTempRect.right = mSelectedLeft - 2;
+ }
+ canvas.drawRect(mTempRect, mDrawPaint);
+
+ if (isLayoutRtl) {
+ mTempRect.left = mSelectedRight + 3;
+ mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+ } else {
+ mTempRect.left = mSelectedRight + 3;
+ mTempRect.right = mWidth;
+ }
+ canvas.drawRect(mTempRect, mDrawPaint);
+ }
+
+ /**
+ * Draws the week and month day numbers for this week.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawWeekNumbersAndDates(Canvas canvas) {
+ final float textHeight = mDrawPaint.getTextSize();
+ final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
+ final int nDays = mNumCells;
+ final int divisor = 2 * nDays;
+
+ mDrawPaint.setTextAlign(Paint.Align.CENTER);
+ mDrawPaint.setTextSize(mDateTextSize);
+
+ int i = 0;
+
+ if (isLayoutRtl()) {
+ for (; i < nDays - 1; i++) {
+ mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
+ : mUnfocusedMonthDateColor);
+ int x = (2 * i + 1) * mWidth / divisor;
+ canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
+ }
+ if (mShowWeekNumber) {
+ mDrawPaint.setColor(mWeekNumberColor);
+ int x = mWidth - mWidth / divisor;
+ canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
+ }
+ } else {
+ if (mShowWeekNumber) {
+ mDrawPaint.setColor(mWeekNumberColor);
+ int x = mWidth / divisor;
+ canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
+ i++;
+ }
+ for (; i < nDays; i++) {
+ mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
+ : mUnfocusedMonthDateColor);
+ int x = (2 * i + 1) * mWidth / divisor;
+ canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
+ }
+ }
+ }
+
+ /**
+ * Draws a horizontal line for separating the weeks.
+ *
+ * @param canvas The canvas to draw on.
+ */
+ private void drawWeekSeparators(Canvas canvas) {
+ // If it is the topmost fully visible child do not draw separator line
+ int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+ if (mListView.getChildAt(0).getTop() < 0) {
+ firstFullyVisiblePosition++;
+ }
+ if (firstFullyVisiblePosition == mWeek) {
+ return;
+ }
+ mDrawPaint.setColor(mWeekSeparatorLineColor);
+ mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
+ float startX;
+ float stopX;
+ if (isLayoutRtl()) {
+ startX = 0;
+ stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+ } else {
+ startX = mShowWeekNumber ? mWidth / mNumCells : 0;
+ stopX = mWidth;
+ }
+ canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
+ }
+
+ /**
+ * Draws the selected date bars if this week has a selected day.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawSelectedDateVerticalBars(Canvas canvas) {
+ if (!mHasSelectedDay) {
+ return;
+ }
+ mSelectedDateVerticalBar.setBounds(
+ mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
+ mWeekSeperatorLineWidth,
+ mSelectedLeft + mSelectedDateVerticalBarWidth / 2,
+ mHeight);
+ mSelectedDateVerticalBar.draw(canvas);
+ mSelectedDateVerticalBar.setBounds(
+ mSelectedRight - mSelectedDateVerticalBarWidth / 2,
+ mWeekSeperatorLineWidth,
+ mSelectedRight + mSelectedDateVerticalBarWidth / 2,
+ mHeight);
+ mSelectedDateVerticalBar.draw(canvas);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ mWidth = w;
+ updateSelectionPositions();
+ }
+
+ /**
+ * This calculates the positions for the selected day lines.
+ */
+ private void updateSelectionPositions() {
+ if (mHasSelectedDay) {
+ final boolean isLayoutRtl = isLayoutRtl();
+ int selectedPosition = mSelectedDay - mFirstDayOfWeek;
+ if (selectedPosition < 0) {
+ selectedPosition += 7;
+ }
+ if (mShowWeekNumber && !isLayoutRtl) {
+ selectedPosition++;
+ }
+ if (isLayoutRtl) {
+ mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
+
+ } else {
+ mSelectedLeft = selectedPosition * mWidth / mNumCells;
+ }
+ mSelectedRight = mSelectedLeft + mWidth / mNumCells;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
+ .getPaddingBottom()) / mShownWeekCount;
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
+ }
+ }
+
+}
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
new file mode 100644
index 0000000..b0f3740
--- /dev/null
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.R;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+import libcore.icu.LocaleData;
+
+class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDelegate {
+ private final DayPickerView mDayPickerView;
+
+ private CalendarView.OnDateChangeListener mOnDateChangeListener;
+
+ public CalendarViewMaterialDelegate(CalendarView delegator, Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(delegator, context);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.CalendarView, defStyleAttr, defStyleRes);
+ final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+ LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+
+ final long minDate = parseDateToMillis(a.getString(
+ R.styleable.CalendarView_minDate), DEFAULT_MIN_DATE);
+ final long maxDate = parseDateToMillis(a.getString(
+ R.styleable.CalendarView_maxDate), DEFAULT_MAX_DATE);
+ if (maxDate < minDate) {
+ throw new IllegalArgumentException("max date cannot be before min date");
+ }
+
+ final long setDate = MathUtils.constrain(System.currentTimeMillis(), minDate, maxDate);
+ final int dateTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance,
+ R.style.TextAppearance_DeviceDefault_Small);
+
+ a.recycle();
+
+ mDayPickerView = new DayPickerView(context);
+ mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
+ mDayPickerView.setCalendarTextAppearance(dateTextAppearanceResId);
+ mDayPickerView.setMinDate(minDate);
+ mDayPickerView.setMaxDate(maxDate);
+ mDayPickerView.setDate(setDate, false, true);
+ mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
+
+ delegator.addView(mDayPickerView);
+ }
+
+ private long parseDateToMillis(String dateStr, String defaultDateStr) {
+ final Calendar tempCalendar = Calendar.getInstance();
+ if (TextUtils.isEmpty(dateStr) || !parseDate(dateStr, tempCalendar)) {
+ parseDate(defaultDateStr, tempCalendar);
+ }
+ return tempCalendar.getTimeInMillis();
+ }
+
+ @Override
+ public void setShownWeekCount(int count) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getShownWeekCount() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedWeekBackgroundColor(int color) {
+ // TODO: Should use a ColorStateList. Deprecate?
+ }
+
+ @Override
+ public int getSelectedWeekBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void setFocusedMonthDateColor(int color) {
+ // TODO: Should use a ColorStateList. Deprecate?
+ }
+
+ @Override
+ public int getFocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setUnfocusedMonthDateColor(int color) {
+ // TODO: Should use a ColorStateList. Deprecate?
+ }
+
+ @Override
+ public int getUnfocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setWeekDayTextAppearance(int resourceId) {
+
+ }
+
+ @Override
+ public int getWeekDayTextAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setDateTextAppearance(int resourceId) {
+
+ }
+
+ @Override
+ public int getDateTextAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setWeekNumberColor(int color) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getWeekNumberColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setWeekSeparatorLineColor(int color) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getWeekSeparatorLineColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(int resourceId) {
+ // Deprecated.
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(Drawable drawable) {
+ // Deprecated.
+ }
+
+ @Override
+ public Drawable getSelectedDateVerticalBar() {
+ // Deprecated.
+ return null;
+ }
+
+ @Override
+ public void setMinDate(long minDate) {
+ mDayPickerView.setMinDate(minDate);
+ }
+
+ @Override
+ public long getMinDate() {
+ return mDayPickerView.getMinDate();
+ }
+
+ @Override
+ public void setMaxDate(long maxDate) {
+ mDayPickerView.setMaxDate(maxDate);
+ }
+
+ @Override
+ public long getMaxDate() {
+ return mDayPickerView.getMaxDate();
+ }
+
+ @Override
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ // Deprecated.
+ }
+
+ @Override
+ public boolean getShowWeekNumber() {
+ // Deprecated.
+ return false;
+ }
+
+ @Override
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
+ }
+
+ @Override
+ public int getFirstDayOfWeek() {
+ return mDayPickerView.getFirstDayOfWeek();
+ }
+
+ @Override
+ public void setDate(long date) {
+ mDayPickerView.setDate(date, true, false);
+ }
+
+ @Override
+ public void setDate(long date, boolean animate, boolean center) {
+ mDayPickerView.setDate(date, animate, center);
+ }
+
+ @Override
+ public long getDate() {
+ return mDayPickerView.getDate();
+ }
+
+ @Override
+ public void setOnDateChangeListener(CalendarView.OnDateChangeListener listener) {
+ mOnDateChangeListener = listener;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Nothing to do here, configuration changes are already propagated
+ // by ViewGroup.
+ }
+
+ private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
+ new DayPickerView.OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerView view, Calendar day) {
+ if (mOnDateChangeListener != null) {
+ final int year = day.get(Calendar.YEAR);
+ final int month = day.get(Calendar.MONTH);
+ final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+ mOnDateChangeListener.onSelectedDayChange(mDelegator, year, month, dayOfMonth);
+ }
+ }
+ };
+}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 3e4eb02..0ca08e1 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -51,12 +51,19 @@ import java.util.TimeZone;
import libcore.icu.ICU;
/**
- * This class is a widget for selecting a date. The date can be selected by a
- * year, month, and day spinners or a {@link CalendarView}. The set of spinners
- * and the calendar view are automatically synchronized. The client can
- * customize whether only the spinners, or only the calendar view, or both to be
- * displayed. Also the minimal and maximal date from which dates to be selected
- * can be customized.
+ * Provides a widget for selecting a date.
+ * <p>
+ * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
+ * set to {@code spinner}, the date can be selected using year, month, and day
+ * spinners or a {@link CalendarView}. The set of spinners and the calendar
+ * view are automatically synchronized. The client can customize whether only
+ * the spinners, or only the calendar view, or both to be displayed.
+ * </p>
+ * <p>
+ * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
+ * set to {@code calendar}, the month and day can be selected using a
+ * calendar-style view while the year can be selected separately using a list.
+ * </p>
* <p>
* See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
* guide.
@@ -80,6 +87,7 @@ import libcore.icu.ICU;
* @attr ref android.R.styleable#DatePicker_yearListItemTextAppearance
* @attr ref android.R.styleable#DatePicker_yearListSelectorColor
* @attr ref android.R.styleable#DatePicker_calendarTextColor
+ * @attr ref android.R.styleable#DatePicker_datePickerMode
*/
@Widget
public class DatePicker extends FrameLayout {
@@ -357,6 +365,10 @@ public class DatePicker extends FrameLayout {
/**
* Gets the {@link CalendarView}.
+ * <p>
+ * This method returns {@code null} when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
* @return The calendar view.
* @see #getCalendarViewShown()
@@ -367,6 +379,10 @@ public class DatePicker extends FrameLayout {
/**
* Sets whether the {@link CalendarView} is shown.
+ * <p>
+ * Calling this method has no effect when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
* @param shown True if the calendar view is to be shown.
*/
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index cf3dbab..820bf78 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -185,8 +185,9 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
mDayPickerView = new DayPickerView(mContext);
mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
- mDayPickerView.setRange(mMinDate, mMaxDate);
- mDayPickerView.setDay(mCurrentDate);
+ mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
+ mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
mYearPickerView = new YearPickerView(mContext);
@@ -336,7 +337,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
switch (viewIndex) {
case MONTH_AND_DAY_VIEW:
- mDayPickerView.setDay(getSelectedDay());
+ mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
if (mCurrentView != viewIndex) {
mMonthAndDayLayout.setSelected(true);
mHeaderYearTextView.setSelected(false);
@@ -414,7 +415,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
updateDisplay(false);
}
mMinDate.setTimeInMillis(minDate);
- mDayPickerView.setRange(mMinDate, mMaxDate);
+ mDayPickerView.setMinDate(minDate);
mYearPickerView.setRange(mMinDate, mMaxDate);
}
@@ -436,7 +437,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
updateDisplay(false);
}
mMaxDate.setTimeInMillis(maxDate);
- mDayPickerView.setRange(mMinDate, mMaxDate);
+ mDayPickerView.setMaxDate(maxDate);
mYearPickerView.setRange(mMinDate, mMaxDate);
}
@@ -616,7 +617,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
listener.onDateChanged();
}
- mDayPickerView.setDay(getSelectedDay());
+ mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
}
@Override
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 443884a..db17df7 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -156,7 +156,7 @@ public class DateTimeView extends TextView {
format = getTimeFormat();
break;
case SHOW_MONTH_DAY_YEAR:
- format = getDateFormat();
+ format = DateFormat.getDateInstance(DateFormat.SHORT);
break;
default:
throw new RuntimeException("unknown display value: " + display);
@@ -196,21 +196,6 @@ public class DateTimeView extends TextView {
return android.text.format.DateFormat.getTimeFormat(getContext());
}
- private DateFormat getDateFormat() {
- String format = Settings.System.getString(getContext().getContentResolver(),
- Settings.System.DATE_FORMAT);
- if (format == null || "".equals(format)) {
- return DateFormat.getDateInstance(DateFormat.SHORT);
- } else {
- try {
- return new SimpleDateFormat(format);
- } catch (IllegalArgumentException e) {
- // If we tried to use a bad format string, fall back to a default.
- return DateFormat.getDateInstance(DateFormat.SHORT);
- }
- }
- }
-
void clearFormatAndUpdate() {
mLastFormat = null;
update();
@@ -283,14 +268,10 @@ public class DateTimeView extends TextView {
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
context.registerReceiver(mReceiver, filter);
-
- final Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT);
- context.getContentResolver().registerContentObserver(uri, true, mObserver);
}
void unregister(Context context) {
context.unregisterReceiver(mReceiver);
- context.getContentResolver().unregisterContentObserver(mObserver);
}
}
}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 6cb1c9d..7db3fb9 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -58,6 +58,8 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
private Calendar mMinDate = Calendar.getInstance();
private Calendar mMaxDate = Calendar.getInstance();
+ private Calendar mTempCalendar;
+
private OnDaySelectedListener mOnDaySelectedListener;
// which month should be displayed/highlighted [0-11]
@@ -77,28 +79,65 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
setDrawSelectorOnTop(false);
setUpListView();
- goTo(mSelectedDay, false, true, true);
+ goTo(mSelectedDay.getTimeInMillis(), false, false, true);
mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener);
}
- public void setDay(Calendar day) {
- goTo(day, false, true, true);
+ /**
+ * Sets the currently selected date to the specified timestamp. Jumps
+ * immediately to the new date. To animate to the new date, use
+ * {@link #setDate(long, boolean, boolean)}.
+ *
+ * @param timeInMillis
+ */
+ public void setDate(long timeInMillis) {
+ setDate(timeInMillis, false, true);
+ }
+
+ public void setDate(long timeInMillis, boolean animate, boolean forceScroll) {
+ goTo(timeInMillis, animate, true, forceScroll);
+ }
+
+ public long getDate() {
+ return mSelectedDay.getTimeInMillis();
}
public void setFirstDayOfWeek(int firstDayOfWeek) {
mAdapter.setFirstDayOfWeek(firstDayOfWeek);
}
- public void setRange(Calendar minDate, Calendar maxDate) {
- mMinDate.setTimeInMillis(minDate.getTimeInMillis());
- mMaxDate.setTimeInMillis(maxDate.getTimeInMillis());
+ public int getFirstDayOfWeek() {
+ return mAdapter.getFirstDayOfWeek();
+ }
+
+ public void setMinDate(long timeInMillis) {
+ mMinDate.setTimeInMillis(timeInMillis);
+ onRangeChanged();
+ }
+
+ public long getMinDate() {
+ return mMinDate.getTimeInMillis();
+ }
+
+ public void setMaxDate(long timeInMillis) {
+ mMaxDate.setTimeInMillis(timeInMillis);
+ onRangeChanged();
+ }
+ public long getMaxDate() {
+ return mMaxDate.getTimeInMillis();
+ }
+
+ /**
+ * Handles changes to date range.
+ */
+ public void onRangeChanged() {
mAdapter.setRange(mMinDate, mMaxDate);
// Changing the min/max date changes the selection position since we
- // don't really have stable IDs.
- goTo(mSelectedDay, false, true, true);
+ // don't really have stable IDs. Jumps immediately to the new position.
+ goTo(mSelectedDay.getTimeInMillis(), false, false, true);
}
/**
@@ -136,12 +175,20 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
return diffMonths;
}
- private int getPositionFromDay(Calendar day) {
+ private int getPositionFromDay(long timeInMillis) {
final int diffMonthMax = getDiffMonths(mMinDate, mMaxDate);
- final int diffMonth = getDiffMonths(mMinDate, day);
+ final int diffMonth = getDiffMonths(mMinDate, getTempCalendarForTime(timeInMillis));
return MathUtils.constrain(diffMonth, 0, diffMonthMax);
}
+ private Calendar getTempCalendarForTime(long timeInMillis) {
+ if (mTempCalendar == null) {
+ mTempCalendar = Calendar.getInstance();
+ }
+ mTempCalendar.setTimeInMillis(timeInMillis);
+ return mTempCalendar;
+ }
+
/**
* This moves to the specified time in the view. If the time is not already
* in range it will move the list so that the first of the month containing
@@ -157,14 +204,14 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
* visible
* @return Whether or not the view animated to the new location
*/
- private boolean goTo(Calendar day, boolean animate, boolean setSelected, boolean forceScroll) {
+ private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) {
// Set the selected day
if (setSelected) {
- mSelectedDay.setTimeInMillis(day.getTimeInMillis());
+ mSelectedDay.setTimeInMillis(day);
}
- mTempDay.setTimeInMillis(day.getTimeInMillis());
+ mTempDay.setTimeInMillis(day);
final int position = getPositionFromDay(day);
View child;
@@ -258,6 +305,10 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
mAdapter.setCalendarTextColor(colors);
}
+ void setCalendarTextAppearance(int resId) {
+ mAdapter.setCalendarTextAppearance(resId);
+ }
+
protected class ScrollStateRunnable implements Runnable {
private int mNewState;
private View mParent;
@@ -415,7 +466,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
}
private String getMonthAndYearString(Calendar day) {
- StringBuffer sbuf = new StringBuffer();
+ final StringBuilder sbuf = new StringBuilder();
sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
sbuf.append(" ");
sbuf.append(mYearFormat.format(day.getTime()));
@@ -429,8 +480,8 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
}
/**
@@ -474,7 +525,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
// Go to that month.
announceForAccessibility(getMonthAndYearString(day));
- goTo(day, true, false, true);
+ goTo(day.getTimeInMillis(), true, false, true);
mPerformingScroll = true;
return true;
}
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 0687905..fe143de 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -106,6 +106,9 @@ class FastScroller {
*/
private final int[] mPreviewResId = new int[2];
+ /** The minimum touch target size in pixels. */
+ private final int mMinimumTouchTarget;
+
/**
* Padding in pixels around the preview text. Applied as layout margins to
* the preview text and padding to the preview image.
@@ -254,6 +257,9 @@ class FastScroller {
mPrimaryText = createPreviewTextView(context);
mSecondaryText = createPreviewTextView(context);
+ mMinimumTouchTarget = listView.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.fast_scroller_minimum_touch_target);
+
setStyle(styleResId);
final ViewGroupOverlay overlay = listView.getOverlay();
@@ -1194,17 +1200,37 @@ class FastScroller {
return MathUtils.constrain((y - offset) / range, 0f, 1f);
}
+ /**
+ * Calculates the thumb position based on the visible items.
+ *
+ * @param firstVisibleItem First visible item, >= 0.
+ * @param visibleItemCount Number of visible items, >= 0.
+ * @param totalItemCount Total number of items, >= 0.
+ * @return
+ */
private float getPosFromItemCount(
int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- if (mSectionIndexer == null || mListAdapter == null) {
+ final SectionIndexer sectionIndexer = mSectionIndexer;
+ if (sectionIndexer == null || mListAdapter == null) {
getSectionsFromIndexer();
}
- final boolean hasSections = mSectionIndexer != null && mSections != null
+ if (visibleItemCount == 0 || totalItemCount == 0) {
+ // No items are visible.
+ return 0;
+ }
+
+ final boolean hasSections = sectionIndexer != null && mSections != null
&& mSections.length > 0;
if (!hasSections || !mMatchDragPosition) {
- return (float) firstVisibleItem / (totalItemCount - visibleItemCount);
+ if (visibleItemCount == totalItemCount) {
+ // All items are visible.
+ return 0;
+ } else {
+ return (float) firstVisibleItem / (totalItemCount - visibleItemCount);
+ }
}
+
// Ignore headers.
firstVisibleItem -= mHeaderCount;
if (firstVisibleItem < 0) {
@@ -1222,14 +1248,14 @@ class FastScroller {
}
// Number of rows in this section.
- final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem);
- final int sectionPos = mSectionIndexer.getPositionForSection(section);
+ final int section = sectionIndexer.getSectionForPosition(firstVisibleItem);
+ final int sectionPos = sectionIndexer.getPositionForSection(section);
final int sectionCount = mSections.length;
final int positionsInSection;
if (section < sectionCount - 1) {
final int nextSectionPos;
if (section + 1 < sectionCount) {
- nextSectionPos = mSectionIndexer.getPositionForSection(section + 1);
+ nextSectionPos = sectionIndexer.getPositionForSection(section + 1);
} else {
nextSectionPos = totalItemCount - 1;
}
@@ -1454,10 +1480,18 @@ class FastScroller {
}
private boolean isPointInsideX(float x) {
+ final float offset = mThumbImage.getTranslationX();
+ final float left = mThumbImage.getLeft() + offset;
+ final float right = mThumbImage.getRight() + offset;
+
+ // Apply the minimum touch target size.
+ final float targetSizeDiff = mMinimumTouchTarget - (right - left);
+ final float adjust = targetSizeDiff > 0 ? targetSizeDiff : 0;
+
if (mLayoutFromRight) {
- return x >= mThumbImage.getLeft();
+ return x >= mThumbImage.getLeft() - adjust;
} else {
- return x <= mThumbImage.getRight();
+ return x <= mThumbImage.getRight() + adjust;
}
}
@@ -1465,7 +1499,12 @@ class FastScroller {
final float offset = mThumbImage.getTranslationY();
final float top = mThumbImage.getTop() + offset;
final float bottom = mThumbImage.getBottom() + offset;
- return y >= top && y <= bottom;
+
+ // Apply the minimum touch target size.
+ final float targetSizeDiff = mMinimumTouchTarget - (bottom - top);
+ final float adjust = targetSizeDiff > 0 ? targetSizeDiff / 2 : 0;
+
+ return y >= (top - adjust) && y <= (bottom + adjust);
}
/**
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index defc26c..161ae7e 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -1613,7 +1613,11 @@ public class GridLayout extends ViewGroup {
equivalent to the single-source shortest paths problem on a digraph, for
which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
*/
- private void solve(Arc[] arcs, int[] locations) {
+ private boolean solve(Arc[] arcs, int[] locations) {
+ return solve(arcs, locations, true);
+ }
+
+ private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
String axisName = horizontal ? "horizontal" : "vertical";
int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
boolean[] originalCulprits = null;
@@ -1631,10 +1635,14 @@ public class GridLayout extends ViewGroup {
if (originalCulprits != null) {
logError(axisName, arcs, originalCulprits);
}
- return;
+ return true;
}
}
+ if (!modifyOnError) {
+ return false; // cannot solve with these constraints
+ }
+
boolean[] culprits = new boolean[arcs.length];
for (int i = 0; i < N; i++) {
for (int j = 0, length = arcs.length; j < length; j++) {
@@ -1658,6 +1666,7 @@ public class GridLayout extends ViewGroup {
}
}
}
+ return true;
}
private void computeMargins(boolean leading) {
@@ -1697,8 +1706,8 @@ public class GridLayout extends ViewGroup {
return trailingMargins;
}
- private void solve(int[] a) {
- solve(getArcs(), a);
+ private boolean solve(int[] a) {
+ return solve(getArcs(), a);
}
private boolean computeHasWeights() {
@@ -1740,28 +1749,18 @@ public class GridLayout extends ViewGroup {
return deltas;
}
- private void shareOutDelta() {
- int totalDelta = 0;
- float totalWeight = 0;
+ private void shareOutDelta(int totalDelta, float totalWeight) {
+ Arrays.fill(deltas, 0);
for (int i = 0, N = getChildCount(); i < N; i++) {
View c = getChildAt(i);
LayoutParams lp = getLayoutParams(c);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
float weight = spec.weight;
if (weight != 0) {
- int delta = getMeasurement(c, horizontal) - getOriginalMeasurements()[i];
- totalDelta += delta;
- totalWeight += weight;
- }
- }
- for (int i = 0, N = getChildCount(); i < N; i++) {
- LayoutParams lp = getLayoutParams(getChildAt(i));
- Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
- float weight = spec.weight;
- if (weight != 0) {
int delta = Math.round((weight * totalDelta / totalWeight));
deltas[i] = delta;
- // the two adjustments below are to counter the above rounding and avoid off-by-ones at the end
+ // the two adjustments below are to counter the above rounding and avoid
+ // off-by-ones at the end
totalDelta -= delta;
totalWeight -= weight;
}
@@ -1771,12 +1770,46 @@ public class GridLayout extends ViewGroup {
private void solveAndDistributeSpace(int[] a) {
Arrays.fill(getDeltas(), 0);
solve(a);
- shareOutDelta();
- arcsValid = false;
- forwardLinksValid = false;
- backwardLinksValid = false;
- groupBoundsValid = false;
- solve(a);
+ int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
+ if (deltaMax < 2) {
+ return; //don't have any delta to distribute
+ }
+ int deltaMin = 0; //inclusive
+
+ float totalWeight = calculateTotalWeight();
+
+ int validDelta = -1; //delta for which a solution exists
+ boolean validSolution = true;
+ // do a binary search to find the max delta that won't conflict with constraints
+ while(deltaMin < deltaMax) {
+ final int delta = (deltaMin + deltaMax) / 2;
+ invalidateValues();
+ shareOutDelta(delta, totalWeight);
+ validSolution = solve(getArcs(), a, false);
+ if (validSolution) {
+ validDelta = delta;
+ deltaMin = delta + 1;
+ } else {
+ deltaMax = delta;
+ }
+ }
+ if (validDelta > 0 && !validSolution) {
+ // last solution was not successful but we have a successful one. Use it.
+ invalidateValues();
+ shareOutDelta(validDelta, totalWeight);
+ solve(a);
+ }
+ }
+
+ private float calculateTotalWeight() {
+ float totalWeight = 0f;
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ View c = getChildAt(i);
+ LayoutParams lp = getLayoutParams(c);
+ Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+ totalWeight += spec.weight;
+ }
+ return totalWeight;
}
private void computeLocations(int[] a) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 396c0b9..5419ab6 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -378,10 +378,10 @@ public class PopupWindow {
}
/**
- * Set the flag on popup to ignore cheek press event; by default this flag
+ * Set the flag on popup to ignore cheek press events; by default this flag
* is set to false
- * which means the pop wont ignore cheek press dispatch events.
- *
+ * which means the popup will not ignore cheek press dispatch events.
+ *
* <p>If the popup is showing, calling this method will take effect only
* the next time the popup is shown or through a manual call to one of
* the {@link #update()} methods.</p>
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 887a93b..de1bbc7 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -227,8 +227,6 @@ public class ProgressBar extends View {
private long mUiThreadId;
private boolean mShouldStartAnimationDrawable;
- private float mAnimationPosition;
-
private boolean mInDrawing;
private boolean mAttached;
private boolean mRefreshIsPosted;
@@ -246,7 +244,7 @@ public class ProgressBar extends View {
public ProgressBar(Context context) {
this(context, null);
}
-
+
public ProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.progressBarStyle);
}
@@ -263,9 +261,9 @@ public class ProgressBar extends View {
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes);
-
+
mNoInvalidate = true;
-
+
final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
if (progressDrawable != null) {
// Calling this method can set mMaxHeight, make sure the corresponding
@@ -284,11 +282,11 @@ public class ProgressBar extends View {
mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);
final int resID = a.getResourceId(
- com.android.internal.R.styleable.ProgressBar_interpolator,
+ com.android.internal.R.styleable.ProgressBar_interpolator,
android.R.anim.linear_interpolator); // default to linear interpolator
if (resID > 0) {
setInterpolator(context, resID);
- }
+ }
setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
@@ -401,12 +399,12 @@ public class ProgressBar extends View {
* traverse layer and state list drawables.
*/
private Drawable tileify(Drawable drawable, boolean clip) {
-
+
if (drawable instanceof LayerDrawable) {
LayerDrawable background = (LayerDrawable) drawable;
final int N = background.getNumberOfLayers();
Drawable[] outDrawables = new Drawable[N];
-
+
for (int i = 0; i < N; i++) {
int id = background.getId(i);
outDrawables[i] = tileify(background.getDrawable(i),
@@ -414,13 +412,13 @@ public class ProgressBar extends View {
}
LayerDrawable newBg = new LayerDrawable(outDrawables);
-
+
for (int i = 0; i < N; i++) {
newBg.setId(i, background.getId(i));
}
-
+
return newBg;
-
+
} else if (drawable instanceof StateListDrawable) {
StateListDrawable in = (StateListDrawable) drawable;
StateListDrawable out = new StateListDrawable();
@@ -429,7 +427,7 @@ public class ProgressBar extends View {
out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
}
return out;
-
+
} else if (drawable instanceof BitmapDrawable) {
final BitmapDrawable bitmap = (BitmapDrawable) drawable;
final Bitmap tileBitmap = bitmap.getBitmap();
@@ -450,7 +448,7 @@ public class ProgressBar extends View {
return clip ? new ClipDrawable(
shapeDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL) : shapeDrawable;
}
-
+
return drawable;
}
@@ -458,7 +456,7 @@ public class ProgressBar extends View {
final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
return new RoundRectShape(roundedCorners, null, null);
}
-
+
/**
* Convert a AnimationDrawable for use as a barberpole animation.
* Each frame of the animation is wrapped in a ClipDrawable and
@@ -470,7 +468,7 @@ public class ProgressBar extends View {
final int N = background.getNumberOfFrames();
AnimationDrawable newBg = new AnimationDrawable();
newBg.setOneShot(background.isOneShot());
-
+
for (int i = 0; i < N; i++) {
Drawable frame = tileify(background.getFrame(i), true);
frame.setLevel(10000);
@@ -481,7 +479,7 @@ public class ProgressBar extends View {
}
return drawable;
}
-
+
/**
* <p>
* Initialize the progress bar's default values:
@@ -522,7 +520,7 @@ public class ProgressBar extends View {
* <p>Change the indeterminate mode for this progress bar. In indeterminate
* mode, the progress is ignored and the progress bar shows an infinite
* animation instead.</p>
- *
+ *
* If this progress bar's style only supports indeterminate mode (such as the circular
* progress bars), then this will be ignored.
*
@@ -701,7 +699,7 @@ public class ProgressBar extends View {
setIndeterminateDrawable(d);
}
-
+
/**
* <p>Get the drawable used to draw the progress bar in
* progress mode.</p>
@@ -1137,7 +1135,7 @@ public class ProgressBar extends View {
setProgressDrawable(d);
}
-
+
/**
* @return The drawable currently used to draw the progress bar
*/
@@ -1188,7 +1186,7 @@ public class ProgressBar extends View {
final int count = mRefreshData.size();
for (int i = 0; i < count; i++) {
final RefreshData rd = mRefreshData.get(i);
- doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate);
+ doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
rd.recycle();
}
mRefreshData.clear();
@@ -1203,12 +1201,10 @@ public class ProgressBar extends View {
new SynchronizedPool<RefreshData>(POOL_MAX);
public int id;
- public float progress;
+ public int progress;
public boolean fromUser;
- public boolean animate;
- public static RefreshData obtain(int id, float progress, boolean fromUser,
- boolean animate) {
+ public static RefreshData obtain(int id, int progress, boolean fromUser) {
RefreshData rd = sPool.acquire();
if (rd == null) {
rd = new RefreshData();
@@ -1216,10 +1212,9 @@ public class ProgressBar extends View {
rd.id = id;
rd.progress = progress;
rd.fromUser = fromUser;
- rd.animate = animate;
return rd;
}
-
+
public void recycle() {
sPool.release(this);
}
@@ -1243,19 +1238,9 @@ public class ProgressBar extends View {
layer.setTintMode(tintMode);
}
- private float getScale(float progress) {
- return mMax > 0 ? progress / (float) mMax : 0;
- }
-
- private synchronized void doRefreshProgress(int id, float progress, boolean fromUser,
+ private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
boolean callBackToApp) {
- doRefreshProgress(id, progress, fromUser, callBackToApp, false);
- }
-
- private synchronized void doRefreshProgress(int id, float progress, boolean fromUser,
- boolean callBackToApp, boolean animate) {
- float scale = getScale(progress);
-
+ float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
final Drawable d = mCurrentDrawable;
if (d != null) {
Drawable progressDrawable = null;
@@ -1272,65 +1257,27 @@ public class ProgressBar extends View {
} else {
invalidate();
}
-
- if (id == R.id.progress) {
- if (animate) {
- onAnimatePosition(scale, fromUser);
- } else if (callBackToApp) {
- onProgressRefresh(scale, fromUser);
- }
+
+ if (callBackToApp && id == R.id.progress) {
+ onProgressRefresh(scale, fromUser);
}
}
- /**
- * Called when a ProgressBar is animating its position.
- *
- * @param scale Current position/progress between 0 and 1.
- * @param fromUser True if the progress change was initiated by the user.
- */
- void onAnimatePosition(float scale, boolean fromUser) {
- }
-
- /**
- * Sets the progress value without going through the entire refresh process.
- *
- * @see #setProgress(int, boolean)
- * @param progress The new progress, between 0 and {@link #getMax()}
- */
- void setProgressValueOnly(int progress) {
- mProgress = progress;
- onProgressRefresh(getScale(progress), true);
- }
-
- void setAnimationPosition(float position) {
- mAnimationPosition = position;
- refreshProgress(R.id.progress, position, true, true);
- }
-
- float getAnimationPosition() {
- return mAnimationPosition;
- }
-
void onProgressRefresh(float scale, boolean fromUser) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
scheduleAccessibilityEventSender();
}
}
- private synchronized void refreshProgress(int id, float progress, boolean fromUser) {
- refreshProgress(id, progress, fromUser, false);
- }
-
- private synchronized void refreshProgress(int id, float progress, boolean fromUser,
- boolean animate) {
+ private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
if (mUiThreadId == Thread.currentThread().getId()) {
- doRefreshProgress(id, progress, fromUser, true, animate);
+ doRefreshProgress(id, progress, fromUser, true);
} else {
if (mRefreshProgressRunnable == null) {
mRefreshProgressRunnable = new RefreshProgressRunnable();
}
- final RefreshData rd = RefreshData.obtain(id, progress, fromUser, animate);
+ final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
mRefreshData.add(rd);
if (mAttached && !mRefreshIsPosted) {
post(mRefreshProgressRunnable);
@@ -1338,7 +1285,7 @@ public class ProgressBar extends View {
}
}
}
-
+
/**
* <p>Set the current progress to the specified value. Does not do anything
* if the progress bar is in indeterminate mode.</p>
@@ -1348,13 +1295,13 @@ public class ProgressBar extends View {
* @see #setIndeterminate(boolean)
* @see #isIndeterminate()
* @see #getProgress()
- * @see #incrementProgressBy(int)
+ * @see #incrementProgressBy(int)
*/
@android.view.RemotableViewMethod
public synchronized void setProgress(int progress) {
setProgress(progress, false);
}
-
+
@android.view.RemotableViewMethod
synchronized void setProgress(int progress, boolean fromUser) {
if (mIndeterminate) {
@@ -1380,7 +1327,7 @@ public class ProgressBar extends View {
* Set the current secondary progress to the specified value. Does not do
* anything if the progress bar is in indeterminate mode.
* </p>
- *
+ *
* @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()}
* @see #setIndeterminate(boolean)
* @see #isIndeterminate()
@@ -1461,8 +1408,8 @@ public class ProgressBar extends View {
* @param max the upper range of this progress bar
*
* @see #getMax()
- * @see #setProgress(int)
- * @see #setSecondaryProgress(int)
+ * @see #setProgress(int)
+ * @see #setSecondaryProgress(int)
*/
@android.view.RemotableViewMethod
public synchronized void setMax(int max) {
@@ -1479,13 +1426,13 @@ public class ProgressBar extends View {
refreshProgress(R.id.progress, mProgress, false);
}
}
-
+
/**
* <p>Increase the progress bar's progress by the specified amount.</p>
*
* @param diff the amount by which the progress must be increased
*
- * @see #setProgress(int)
+ * @see #setProgress(int)
*/
public synchronized final void incrementProgressBy(int diff) {
setProgress(mProgress + diff);
@@ -1496,7 +1443,7 @@ public class ProgressBar extends View {
*
* @param diff the amount by which the secondary progress must be increased
*
- * @see #setSecondaryProgress(int)
+ * @see #setSecondaryProgress(int)
*/
public synchronized final void incrementSecondaryProgressBy(int diff) {
setSecondaryProgress(mSecondaryProgress + diff);
@@ -1519,13 +1466,13 @@ public class ProgressBar extends View {
if (mInterpolator == null) {
mInterpolator = new LinearInterpolator();
}
-
+
if (mTransformation == null) {
mTransformation = new Transformation();
} else {
mTransformation.clear();
}
-
+
if (mAnimation == null) {
mAnimation = new AlphaAnimation(0.0f, 1.0f);
} else {
@@ -1676,7 +1623,7 @@ public class ProgressBar extends View {
}
mIndeterminateDrawable.setBounds(left, top, right, bottom);
}
-
+
if (mProgressDrawable != null) {
mProgressDrawable.setBounds(0, 0, right, bottom);
}
@@ -1746,20 +1693,20 @@ public class ProgressBar extends View {
setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
resolveSizeAndState(dh, heightMeasureSpec, 0));
}
-
+
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
updateDrawableState();
}
-
+
private void updateDrawableState() {
int[] state = getDrawableState();
-
+
if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
mProgressDrawable.setState(state);
}
-
+
if (mIndeterminateDrawable != null && mIndeterminateDrawable.isStateful()) {
mIndeterminateDrawable.setState(state);
}
@@ -1781,14 +1728,14 @@ public class ProgressBar extends View {
static class SavedState extends BaseSavedState {
int progress;
int secondaryProgress;
-
+
/**
* Constructor called from {@link ProgressBar#onSaveInstanceState()}
*/
SavedState(Parcelable superState) {
super(superState);
}
-
+
/**
* Constructor called from {@link #CREATOR}
*/
@@ -1822,10 +1769,10 @@ public class ProgressBar extends View {
// Force our ancestor class to save its state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
-
+
ss.progress = mProgress;
ss.secondaryProgress = mSecondaryProgress;
-
+
return ss;
}
@@ -1833,7 +1780,7 @@ public class ProgressBar extends View {
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
-
+
setProgress(ss.progress);
setSecondaryProgress(ss.secondaryProgress);
}
@@ -1849,7 +1796,7 @@ public class ProgressBar extends View {
final int count = mRefreshData.size();
for (int i = 0; i < count; i++) {
final RefreshData rd = mRefreshData.get(i);
- doRefreshProgress(rd.id, rd.progress, rd.fromUser, rd.animate);
+ doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
rd.recycle();
}
mRefreshData.clear();
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 24fc2bb..8b01dde 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -452,7 +452,10 @@ public class RadialTimePickerView extends View implements View.OnTouchListener {
}
public void initialize(int hour, int minute, boolean is24HourMode) {
- mIs24HourMode = is24HourMode;
+ if (mIs24HourMode != is24HourMode) {
+ mIs24HourMode = is24HourMode;
+ initData();
+ }
setCurrentHourInternal(hour, false, false);
setCurrentMinuteInternal(minute, false);
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index c4a7c0a..82b490e 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -314,10 +314,6 @@ public class RatingBar extends AbsSeekBar {
dispatchRatingChange(true);
}
- @Override
- void animateSetProgress(int progress) {
- }
-
void dispatchRatingChange(boolean fromUser) {
if (mOnRatingBarChangeListener != null) {
mOnRatingBarChangeListener.onRatingChanged(this, getRating(),
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index dfdf606..4ee6418 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -45,7 +45,6 @@ import android.text.TextWatcher;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.TypedValue;
import android.view.CollapsibleActionView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -99,17 +98,21 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
*/
private static final String IME_OPTION_NO_MICROPHONE = "nm";
- private final SearchAutoComplete mQueryTextView;
+ private final SearchAutoComplete mSearchSrcTextView;
private final View mSearchEditFrame;
private final View mSearchPlate;
private final View mSubmitArea;
private final ImageView mSearchButton;
- private final ImageView mSubmitButton;
+ private final ImageView mGoButton;
private final ImageView mCloseButton;
private final ImageView mVoiceButton;
- private final ImageView mSearchHintIcon;
private final View mDropDownAnchor;
- private final int mSearchIconResId;
+
+ /** Icon optionally displayed when the SearchView is collapsed. */
+ private final ImageView mCollapsedIcon;
+
+ /** Drawable used as an EditText hint. */
+ private final Drawable mSearchHintIcon;
// Resources used by SuggestionsAdapter to display suggestions.
private final int mSuggestionRowLayout;
@@ -262,30 +265,38 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
attrs, R.styleable.SearchView, defStyleAttr, defStyleRes);
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- final int layoutResId = a.getResourceId(R.styleable.SearchView_layout, R.layout.search_view);
+ final int layoutResId = a.getResourceId(
+ R.styleable.SearchView_layout, R.layout.search_view);
inflater.inflate(layoutResId, this, true);
- mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
- mQueryTextView.setSearchView(this);
+ mSearchSrcTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
+ mSearchSrcTextView.setSearchView(this);
mSearchEditFrame = findViewById(R.id.search_edit_frame);
mSearchPlate = findViewById(R.id.search_plate);
mSubmitArea = findViewById(R.id.submit_area);
mSearchButton = (ImageView) findViewById(R.id.search_button);
- mSubmitButton = (ImageView) findViewById(R.id.search_go_btn);
+ mGoButton = (ImageView) findViewById(R.id.search_go_btn);
mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
mVoiceButton = (ImageView) findViewById(R.id.search_voice_btn);
- mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon);
+ mCollapsedIcon = (ImageView) findViewById(R.id.search_mag_icon);
// Set up icons and backgrounds.
mSearchPlate.setBackground(a.getDrawable(R.styleable.SearchView_queryBackground));
mSubmitArea.setBackground(a.getDrawable(R.styleable.SearchView_submitBackground));
- mSearchIconResId = a.getResourceId(R.styleable.SearchView_searchIcon, 0);
- mSearchButton.setImageResource(mSearchIconResId);
- mSubmitButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
+ mSearchButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+ mGoButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
mCloseButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_closeIcon));
mVoiceButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_voiceIcon));
- mSearchHintIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+ mCollapsedIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+
+ // Prior to L MR1, the search hint icon defaulted to searchIcon. If the
+ // style does not have an explicit value set, fall back to that.
+ if (a.hasValueOrEmpty(R.styleable.SearchView_searchHintIcon)) {
+ mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchHintIcon);
+ } else {
+ mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchIcon);
+ }
// Extract dropdown layout resource IDs for later use.
mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout,
@@ -294,18 +305,18 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
mSearchButton.setOnClickListener(mOnClickListener);
mCloseButton.setOnClickListener(mOnClickListener);
- mSubmitButton.setOnClickListener(mOnClickListener);
+ mGoButton.setOnClickListener(mOnClickListener);
mVoiceButton.setOnClickListener(mOnClickListener);
- mQueryTextView.setOnClickListener(mOnClickListener);
+ mSearchSrcTextView.setOnClickListener(mOnClickListener);
- mQueryTextView.addTextChangedListener(mTextWatcher);
- mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
- mQueryTextView.setOnItemClickListener(mOnItemClickListener);
- mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
- mQueryTextView.setOnKeyListener(mTextKeyListener);
+ mSearchSrcTextView.addTextChangedListener(mTextWatcher);
+ mSearchSrcTextView.setOnEditorActionListener(mOnEditorActionListener);
+ mSearchSrcTextView.setOnItemClickListener(mOnItemClickListener);
+ mSearchSrcTextView.setOnItemSelectedListener(mOnItemSelectedListener);
+ mSearchSrcTextView.setOnKeyListener(mTextKeyListener);
// Inform any listener of focus changes
- mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
+ mSearchSrcTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (mOnQueryTextFocusChangeListener != null) {
@@ -350,7 +361,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor());
+ mDropDownAnchor = findViewById(mSearchSrcTextView.getDropDownAnchor());
if (mDropDownAnchor != null) {
mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
@@ -393,7 +404,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
if (mVoiceButtonEnabled) {
// Disable the microphone on the keyboard, as a mic is displayed near the text box
// TODO: use imeOptions to disable voice input when the new API will be available
- mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
+ mSearchSrcTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
}
updateViewsVisibility(isIconified());
}
@@ -416,7 +427,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* @attr ref android.R.styleable#SearchView_imeOptions
*/
public void setImeOptions(int imeOptions) {
- mQueryTextView.setImeOptions(imeOptions);
+ mSearchSrcTextView.setImeOptions(imeOptions);
}
/**
@@ -427,7 +438,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* @attr ref android.R.styleable#SearchView_imeOptions
*/
public int getImeOptions() {
- return mQueryTextView.getImeOptions();
+ return mSearchSrcTextView.getImeOptions();
}
/**
@@ -439,7 +450,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* @attr ref android.R.styleable#SearchView_inputType
*/
public void setInputType(int inputType) {
- mQueryTextView.setInputType(inputType);
+ mSearchSrcTextView.setInputType(inputType);
}
/**
@@ -449,7 +460,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* @attr ref android.R.styleable#SearchView_inputType
*/
public int getInputType() {
- return mQueryTextView.getInputType();
+ return mSearchSrcTextView.getInputType();
}
/** @hide */
@@ -461,7 +472,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
if (!isFocusable()) return false;
// If it is not iconified, then give the focus to the text field
if (!isIconified()) {
- boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+ boolean result = mSearchSrcTextView.requestFocus(direction, previouslyFocusedRect);
if (result) {
updateViewsVisibility(false);
}
@@ -477,7 +488,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
mClearingFocus = true;
setImeVisibility(false);
super.clearFocus();
- mQueryTextView.clearFocus();
+ mSearchSrcTextView.clearFocus();
mClearingFocus = false;
}
@@ -536,7 +547,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* @return the query string
*/
public CharSequence getQuery() {
- return mQueryTextView.getText();
+ return mSearchSrcTextView.getText();
}
/**
@@ -548,9 +559,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* text field.
*/
public void setQuery(CharSequence query, boolean submit) {
- mQueryTextView.setText(query);
+ mSearchSrcTextView.setText(query);
if (query != null) {
- mQueryTextView.setSelection(mQueryTextView.length());
+ mSearchSrcTextView.setSelection(mSearchSrcTextView.length());
mUserQuery = query;
}
@@ -711,7 +722,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
public void setSuggestionsAdapter(CursorAdapter adapter) {
mSuggestionsAdapter = adapter;
- mQueryTextView.setAdapter(mSuggestionsAdapter);
+ mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
}
/**
@@ -789,12 +800,12 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
// Visibility of views that are visible when collapsed
final int visCollapsed = collapsed ? VISIBLE : GONE;
// Is there text in the query
- final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+ final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
mSearchButton.setVisibility(visCollapsed);
updateSubmitButton(hasText);
mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
- mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
+ mCollapsedIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
updateCloseButton();
updateVoiceButton(!hasText);
updateSubmitArea();
@@ -827,13 +838,13 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
&& (hasText || !mVoiceButtonEnabled)) {
visibility = VISIBLE;
}
- mSubmitButton.setVisibility(visibility);
+ mGoButton.setVisibility(visibility);
}
private void updateSubmitArea() {
int visibility = GONE;
if (isSubmitAreaEnabled()
- && (mSubmitButton.getVisibility() == VISIBLE
+ && (mGoButton.getVisibility() == VISIBLE
|| mVoiceButton.getVisibility() == VISIBLE)) {
visibility = VISIBLE;
}
@@ -841,12 +852,15 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
private void updateCloseButton() {
- final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+ final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
// Should we show the close button? It is not shown if there's no focus,
// field is not iconified by default and there is no text in it.
final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView);
mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
- mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+ final Drawable closeButtonImg = mCloseButton.getDrawable();
+ if (closeButtonImg != null){
+ closeButtonImg.setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+ }
}
private void postUpdateFocusedState() {
@@ -854,9 +868,16 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
private void updateFocusedState() {
- boolean focused = mQueryTextView.hasFocus();
- mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
- mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+ final boolean focused = mSearchSrcTextView.hasFocus();
+ final int[] stateSet = focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET;
+ final Drawable searchPlateBg = mSearchPlate.getBackground();
+ if (searchPlateBg != null) {
+ searchPlateBg.setState(stateSet);
+ }
+ final Drawable submitAreaBg = mSubmitArea.getBackground();
+ if (submitAreaBg != null) {
+ submitAreaBg.setState(stateSet);
+ }
invalidate();
}
@@ -896,11 +917,11 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
onSearchClicked();
} else if (v == mCloseButton) {
onCloseClicked();
- } else if (v == mSubmitButton) {
+ } else if (v == mGoButton) {
onSubmitQuery();
} else if (v == mVoiceButton) {
onVoiceClicked();
- } else if (v == mQueryTextView) {
+ } else if (v == mSearchSrcTextView) {
forceSuggestionQuery();
}
}
@@ -925,7 +946,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
// entered query with the action key
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
- launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
+ launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mSearchSrcTextView.getText()
.toString());
return true;
}
@@ -947,25 +968,25 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
if (DBG) {
Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: "
- + mQueryTextView.getListSelection());
+ + mSearchSrcTextView.getListSelection());
}
// If a suggestion is selected, handle enter, search key, and action keys
// as presses on the selected suggestion
- if (mQueryTextView.isPopupShowing()
- && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) {
+ if (mSearchSrcTextView.isPopupShowing()
+ && mSearchSrcTextView.getListSelection() != ListView.INVALID_POSITION) {
return onSuggestionsKey(v, keyCode, event);
}
// If there is text in the query box, handle enter, and action keys
// The search key is handled by the dialog's onKeyDown().
- if (!mQueryTextView.isEmpty() && event.hasNoModifiers()) {
+ if (!mSearchSrcTextView.isEmpty() && event.hasNoModifiers()) {
if (event.getAction() == KeyEvent.ACTION_UP) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
v.cancelLongPress();
// Launch as a regular search.
- launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText()
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mSearchSrcTextView.getText()
.toString());
return true;
}
@@ -973,7 +994,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
- launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView
+ launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mSearchSrcTextView
.getText().toString());
return true;
}
@@ -1001,7 +1022,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
// "click")
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH
|| keyCode == KeyEvent.KEYCODE_TAB) {
- int position = mQueryTextView.getListSelection();
+ int position = mSearchSrcTextView.getListSelection();
return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
}
@@ -1012,18 +1033,18 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
// left key, at end if right key
// TODO: Reverse left/right for right-to-left languages, e.g.
// Arabic
- int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView
+ int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mSearchSrcTextView
.length();
- mQueryTextView.setSelection(selPoint);
- mQueryTextView.setListSelection(0);
- mQueryTextView.clearListSelection();
- mQueryTextView.ensureImeVisible(true);
+ mSearchSrcTextView.setSelection(selPoint);
+ mSearchSrcTextView.setListSelection(0);
+ mSearchSrcTextView.clearListSelection();
+ mSearchSrcTextView.ensureImeVisible(true);
return true;
}
// Next, check for an "up and out" move
- if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mSearchSrcTextView.getListSelection()) {
// TODO: restoreUserQuery();
// let ACTV complete the move
return false;
@@ -1035,7 +1056,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
&& ((actionKey.getSuggestActionMsg() != null) || (actionKey
.getSuggestActionMsgColumn() != null))) {
// launch suggestion using action key column
- int position = mQueryTextView.getListSelection();
+ int position = mSearchSrcTextView.getListSelection();
if (position != ListView.INVALID_POSITION) {
Cursor c = mSuggestionsAdapter.getCursor();
if (c.moveToPosition(position)) {
@@ -1078,24 +1099,24 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
private CharSequence getDecoratedHint(CharSequence hintText) {
- // If the field is always expanded, then don't add the search icon to the hint
- if (!mIconifiedByDefault) {
+ // If the field is always expanded or we don't have a search hint icon,
+ // then don't add the search icon to the hint.
+ if (!mIconifiedByDefault || mSearchHintIcon == null) {
return hintText;
}
- final Drawable searchIcon = getContext().getDrawable(mSearchIconResId);
- final int textSize = (int) (mQueryTextView.getTextSize() * 1.25);
- searchIcon.setBounds(0, 0, textSize, textSize);
+ final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
+ mSearchHintIcon.setBounds(0, 0, textSize, textSize);
- final SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon
+ final SpannableStringBuilder ssb = new SpannableStringBuilder(" ");
+ ssb.setSpan(new ImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(hintText);
- ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return ssb;
}
private void updateQueryHint() {
if (mQueryHint != null) {
- mQueryTextView.setHint(getDecoratedHint(mQueryHint));
+ mSearchSrcTextView.setHint(getDecoratedHint(mQueryHint));
} else if (mSearchable != null) {
CharSequence hint = null;
int hintId = mSearchable.getHintId();
@@ -1103,10 +1124,10 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
hint = getContext().getString(hintId);
}
if (hint != null) {
- mQueryTextView.setHint(getDecoratedHint(hint));
+ mSearchSrcTextView.setHint(getDecoratedHint(hint));
}
} else {
- mQueryTextView.setHint(getDecoratedHint(""));
+ mSearchSrcTextView.setHint(getDecoratedHint(""));
}
}
@@ -1114,9 +1135,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* Updates the auto-complete text view.
*/
private void updateSearchAutoComplete() {
- mQueryTextView.setDropDownAnimationStyle(0); // no animation
- mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
- mQueryTextView.setImeOptions(mSearchable.getImeOptions());
+ mSearchSrcTextView.setDropDownAnimationStyle(0); // no animation
+ mSearchSrcTextView.setThreshold(mSearchable.getSuggestThreshold());
+ mSearchSrcTextView.setImeOptions(mSearchable.getImeOptions());
int inputType = mSearchable.getInputType();
// We only touch this if the input type is set up for text (which it almost certainly
// should be, in the case of search!)
@@ -1135,7 +1156,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
}
}
- mQueryTextView.setInputType(inputType);
+ mSearchSrcTextView.setInputType(inputType);
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.changeCursor(null);
}
@@ -1144,7 +1165,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
if (mSearchable.getSuggestAuthority() != null) {
mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
this, mSearchable, mOutsideDrawablesCache);
- mQueryTextView.setAdapter(mSuggestionsAdapter);
+ mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
: SuggestionsAdapter.REFINE_BY_ENTRY);
@@ -1161,7 +1182,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
int visibility = GONE;
if (mVoiceButtonEnabled && !isIconified() && empty) {
visibility = VISIBLE;
- mSubmitButton.setVisibility(GONE);
+ mGoButton.setVisibility(GONE);
}
mVoiceButton.setVisibility(visibility);
}
@@ -1178,7 +1199,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
};
private void onTextChanged(CharSequence newText) {
- CharSequence text = mQueryTextView.getText();
+ CharSequence text = mSearchSrcTextView.getText();
mUserQuery = text;
boolean hasText = !TextUtils.isEmpty(text);
updateSubmitButton(hasText);
@@ -1192,7 +1213,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
private void onSubmitQuery() {
- CharSequence query = mQueryTextView.getText();
+ CharSequence query = mSearchSrcTextView.getText();
if (query != null && TextUtils.getTrimmedLength(query) > 0) {
if (mOnQueryChangeListener == null
|| !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
@@ -1206,11 +1227,11 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
private void dismissSuggestions() {
- mQueryTextView.dismissDropDown();
+ mSearchSrcTextView.dismissDropDown();
}
private void onCloseClicked() {
- CharSequence text = mQueryTextView.getText();
+ CharSequence text = mSearchSrcTextView.getText();
if (TextUtils.isEmpty(text)) {
if (mIconifiedByDefault) {
// If the app doesn't override the close behavior
@@ -1222,8 +1243,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
}
} else {
- mQueryTextView.setText("");
- mQueryTextView.requestFocus();
+ mSearchSrcTextView.setText("");
+ mSearchSrcTextView.requestFocus();
setImeVisibility(true);
}
@@ -1231,7 +1252,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
private void onSearchClicked() {
updateViewsVisibility(false);
- mQueryTextView.requestFocus();
+ mSearchSrcTextView.requestFocus();
setImeVisibility(true);
if (mOnSearchClickListener != null) {
mOnSearchClickListener.onClick(this);
@@ -1266,7 +1287,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
// Delayed update to make sure that the focus has settled down and window focus changes
// don't affect it. A synchronous update was not working.
postUpdateFocusedState();
- if (mQueryTextView.hasFocus()) {
+ if (mSearchSrcTextView.hasFocus()) {
forceSuggestionQuery();
}
}
@@ -1286,7 +1307,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
setQuery("", false);
clearFocus();
updateViewsVisibility(true);
- mQueryTextView.setImeOptions(mCollapsedImeOptions);
+ mSearchSrcTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
@@ -1298,9 +1319,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
if (mExpandedInActionView) return;
mExpandedInActionView = true;
- mCollapsedImeOptions = mQueryTextView.getImeOptions();
- mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
- mQueryTextView.setText("");
+ mCollapsedImeOptions = mSearchSrcTextView.getImeOptions();
+ mSearchSrcTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+ mSearchSrcTextView.setText("");
setIconified(false);
}
@@ -1326,17 +1347,17 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
? res.getDimensionPixelSize(R.dimen.dropdownitem_icon_width)
+ res.getDimensionPixelSize(R.dimen.dropdownitem_text_padding_left)
: 0;
- mQueryTextView.getDropDownBackground().getPadding(dropDownPadding);
+ mSearchSrcTextView.getDropDownBackground().getPadding(dropDownPadding);
int offset;
if (isLayoutRtl) {
offset = - dropDownPadding.left;
} else {
offset = anchorPadding - (dropDownPadding.left + iconOffset);
}
- mQueryTextView.setDropDownHorizontalOffset(offset);
+ mSearchSrcTextView.setDropDownHorizontalOffset(offset);
final int width = mDropDownAnchor.getWidth() + dropDownPadding.left
+ dropDownPadding.right + iconOffset - anchorPadding;
- mQueryTextView.setDropDownWidth(width);
+ mSearchSrcTextView.setDropDownWidth(width);
}
}
@@ -1394,7 +1415,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* Query rewriting.
*/
private void rewriteQueryFromSuggestion(int position) {
- CharSequence oldQuery = mQueryTextView.getText();
+ CharSequence oldQuery = mSearchSrcTextView.getText();
Cursor c = mSuggestionsAdapter.getCursor();
if (c == null) {
return;
@@ -1460,9 +1481,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
* Sets the text in the query box, without updating the suggestions.
*/
private void setQuery(CharSequence query) {
- mQueryTextView.setText(query, true);
+ mSearchSrcTextView.setText(query, true);
// Move the cursor to the end
- mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
+ mSearchSrcTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
}
private void launchQuerySearch(int actionKey, String actionMsg, String query) {
@@ -1648,8 +1669,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
private void forceSuggestionQuery() {
- mQueryTextView.doBeforeTextChanged();
- mQueryTextView.doAfterTextChanged();
+ mSearchSrcTextView.doBeforeTextChanged();
+ mSearchSrcTextView.doAfterTextChanged();
}
static boolean isLandscapeMode(Context context) {
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
index ecd2912..24ebb2c 100644
--- a/core/java/android/widget/SimpleMonthAdapter.java
+++ b/core/java/android/widget/SimpleMonthAdapter.java
@@ -16,8 +16,12 @@
package android.widget;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SimpleMonthView.OnDayClickListener;
@@ -33,15 +37,14 @@ class SimpleMonthAdapter extends BaseAdapter {
private final Context mContext;
- private Calendar mSelectedDay;
- private ColorStateList mCalendarTextColors;
+ private Calendar mSelectedDay = Calendar.getInstance();
+ private ColorStateList mCalendarTextColors = ColorStateList.valueOf(Color.BLACK);
private OnDaySelectedListener mOnDaySelectedListener;
private int mFirstDayOfWeek;
public SimpleMonthAdapter(Context context) {
mContext = context;
- mSelectedDay = Calendar.getInstance();
}
public void setRange(Calendar min, Calendar max) {
@@ -57,6 +60,10 @@ class SimpleMonthAdapter extends BaseAdapter {
notifyDataSetInvalidated();
}
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
/**
* Updates the selected day and related parameters.
*
@@ -81,6 +88,24 @@ class SimpleMonthAdapter extends BaseAdapter {
mCalendarTextColors = colors;
}
+ /**
+ * Sets the text color, size, style, hint color, and highlight color from
+ * the specified TextAppearance resource. This is mostly copied from
+ * {@link TextView#setTextAppearance(Context, int)}.
+ */
+ void setCalendarTextAppearance(int resId) {
+ final TypedArray a = mContext.obtainStyledAttributes(resId, R.styleable.TextAppearance);
+
+ final ColorStateList textColor = a.getColorStateList(R.styleable.TextAppearance_textColor);
+ if (textColor != null) {
+ mCalendarTextColors = textColor;
+ }
+
+ // TODO: Support font size, etc.
+
+ a.recycle();
+ }
+
@Override
public int getCount() {
final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 1a86809..e2acaac 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,6 +27,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.AttributeSet;
@@ -127,6 +129,8 @@ public class TextClock extends TextView {
private Calendar mTime;
private String mTimeZone;
+ private boolean mShowCurrentUserTime;
+
private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
@@ -342,6 +346,22 @@ public class TextClock extends TextView {
}
/**
+ * Sets whether this clock should always track the current user and not the user of the
+ * current process. This is used for single instance processes like the systemUI who need
+ * to display time for different users.
+ *
+ * @hide
+ */
+ public void setShowCurrentUserTime(boolean showCurrentUserTime) {
+ mShowCurrentUserTime = showCurrentUserTime;
+
+ chooseFormat();
+ onTimeChanged();
+ unregisterObserver();
+ registerObserver();
+ }
+
+ /**
* Indicates whether the system is currently using the 24-hour mode.
*
* When the system is in 24-hour mode, this view will use the pattern
@@ -360,7 +380,11 @@ public class TextClock extends TextView {
* @see #getFormat24Hour()
*/
public boolean is24HourModeEnabled() {
- return DateFormat.is24HourFormat(getContext());
+ if (mShowCurrentUserTime) {
+ return DateFormat.is24HourFormat(getContext(), ActivityManager.getCurrentUser());
+ } else {
+ return DateFormat.is24HourFormat(getContext());
+ }
}
/**
@@ -500,7 +524,13 @@ public class TextClock extends TextView {
private void registerObserver() {
final ContentResolver resolver = getContext().getContentResolver();
- resolver.registerContentObserver(Settings.System.CONTENT_URI, true, mFormatChangeObserver);
+ if (mShowCurrentUserTime) {
+ resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+ mFormatChangeObserver, UserHandle.USER_ALL);
+ } else {
+ resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+ mFormatChangeObserver);
+ }
}
private void unregisterReceiver() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 44ccde0..7d4a2fb 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -42,6 +42,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.BoringLayout;
import android.text.DynamicLayout;
@@ -286,9 +287,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mCurTextColor;
private int mCurHintTextColor;
private boolean mFreezesText;
- private boolean mTemporaryDetach;
private boolean mDispatchTemporaryDetach;
+ /** Whether this view is temporarily detached from the parent view. */
+ boolean mTemporaryDetach;
+
private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
@@ -3631,9 +3634,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (mHintTextColor != null) {
color = mHintTextColor.getColorForState(getDrawableState(), 0);
- if (color != mCurHintTextColor && mText.length() == 0) {
+ if (color != mCurHintTextColor) {
mCurHintTextColor = color;
- inval = true;
+ if (mText.length() == 0) {
+ inval = true;
+ }
}
}
if (inval) {
@@ -3809,7 +3814,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Display the error later, after the first layout pass
post(new Runnable() {
public void run() {
- setError(error);
+ if (mEditor == null || !mEditor.mErrorWasChanged) {
+ setError(error);
+ }
}
});
}
@@ -5367,9 +5374,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
final int maxScrollY = mLayout.getHeight() - vspace;
+ // Add sufficient space for cursor and tone marks
+ int cursorWidth = 2 + (int)mTextPaint.density; // adequate for Material cursors
+ int fudgedPaddingRight = Math.max(0, compoundPaddingRight - (cursorWidth - 1));
+
float clipLeft = compoundPaddingLeft + scrollX;
float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
- float clipRight = right - left - compoundPaddingRight + scrollX;
+ float clipRight = right - left - fudgedPaddingRight + scrollX;
float clipBottom = bottom - top + scrollY -
((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
@@ -8381,8 +8392,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* to speak passwords.
*/
private boolean shouldSpeakPasswordsForAccessibility() {
- return (Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) == 1);
+ return (Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, UserHandle.USER_CURRENT) == 1);
}
@Override
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 1b25486..70fb510 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1079,7 +1079,9 @@ public final class ProcessStats implements Parcelable {
ProcessDataCollection totals = new ProcessDataCollection(screenStates,
memStates, procStates);
computeProcessData(proc, totals, now);
- if (totals.totalTime != 0 || totals.numPss != 0) {
+ double percentage = (double) totals.totalTime / (double) totalTime * 100;
+ // We don't print percentages < .01, so just drop those.
+ if (percentage >= 0.005 || totals.numPss != 0) {
if (prefix != null) {
pw.print(prefix);
}
@@ -2470,7 +2472,7 @@ public final class ProcessStats implements Parcelable {
totalMem.totalTime, totalPss, totalMem.sysMemSamples);
totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight,
totalMem.totalTime, totalPss, totalMem.sysMemSamples);
- totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
+ totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
totalMem.totalTime, totalPss, totalMem.sysMemSamples);
pw.print(" TOTAL : ");
printSizeValue(pw, totalPss);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 376db6e..2db466a 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -840,7 +840,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
if (N > 1) {
Comparator<ResolveInfo> rComparator =
- new ResolverComparator(ResolverActivity.this);
+ new ResolverComparator(ResolverActivity.this, mIntent);
Collections.sort(currentResolveList, rComparator);
}
// First put the initial items at the top.
@@ -1093,11 +1093,20 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
}
+ static final boolean isSpecificUriMatch(int match) {
+ match = match&IntentFilter.MATCH_CATEGORY_MASK;
+ return match >= IntentFilter.MATCH_CATEGORY_HOST
+ && match <= IntentFilter.MATCH_CATEGORY_PATH;
+ }
+
class ResolverComparator implements Comparator<ResolveInfo> {
private final Collator mCollator;
+ private final boolean mHttp;
- public ResolverComparator(Context context) {
+ public ResolverComparator(Context context, Intent intent) {
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
+ String scheme = intent.getScheme();
+ mHttp = "http".equals(scheme) || "https".equals(scheme);
}
@Override
@@ -1107,6 +1116,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return 1;
}
+ if (mHttp) {
+ // Special case: we want filters that match URI paths/schemes to be
+ // ordered before others. This is for the case when opening URIs,
+ // to make native apps go above browsers.
+ final boolean lhsSpecific = isSpecificUriMatch(lhs.match);
+ final boolean rhsSpecific = isSpecificUriMatch(rhs.match);
+ if (lhsSpecific != rhsSpecific) {
+ return lhsSpecific ? -1 : 1;
+ }
+ }
+
if (mStats != null) {
final long timeDiff =
getPackageTimeSpent(rhs.activityInfo.packageName) -
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index d8dffe0..044383e 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -35,6 +35,8 @@ import android.util.Log;
import com.android.org.bouncycastle.util.encoders.Base64;
+import libcore.io.IoUtils;
+
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -559,11 +561,7 @@ public class LocalTransport extends BackupTransport {
// Full restore handling
private void resetFullRestoreState() {
- try {
- mCurFullRestoreStream.close();
- } catch (IOException e) {
- Log.w(TAG, "Unable to close full restore input stream");
- }
+ IoUtils.closeQuietly(mCurFullRestoreStream);
mCurFullRestoreStream = null;
mFullRestoreSocketStream = null;
mFullRestoreBuffer = null;
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 3d016be..c5d9db4 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -25,6 +25,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.LinkAddress;
+import android.net.Network;
import android.net.RouteInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -99,6 +100,7 @@ public class VpnConfig implements Parcelable {
public boolean allowBypass;
public boolean allowIPv4;
public boolean allowIPv6;
+ public Network[] underlyingNetworks;
public void updateAllowedFamilies(InetAddress address) {
if (address instanceof Inet4Address) {
@@ -162,6 +164,7 @@ public class VpnConfig implements Parcelable {
out.writeInt(allowBypass ? 1 : 0);
out.writeInt(allowIPv4 ? 1 : 0);
out.writeInt(allowIPv6 ? 1 : 0);
+ out.writeTypedArray(underlyingNetworks, flags);
}
public static final Parcelable.Creator<VpnConfig> CREATOR =
@@ -186,6 +189,7 @@ public class VpnConfig implements Parcelable {
config.allowBypass = in.readInt() != 0;
config.allowIPv4 = in.readInt() != 0;
config.allowIPv6 = in.readInt() != 0;
+ config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
return config;
}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 17685fd..99286cb 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -49,6 +49,10 @@ public class HandlerCaller {
mCallback = callback;
}
+ public Handler getHandler() {
+ return mH;
+ }
+
public void executeOrSendMessage(Message msg) {
// If we are calling this from the main thread, then we can call
// right through. Otherwise, we need to send the message to the
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 46850da..62088fa 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -45,6 +45,8 @@ import libcore.io.IoUtils;
import java.io.BufferedReader;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -91,9 +93,9 @@ public class ZygoteInit {
private static Resources mResources;
/**
- * The name of a resource file that contains classes to preload.
+ * The path of a file that contains classes to preload.
*/
- private static final String PRELOADED_CLASSES = "preloaded-classes";
+ private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
/** Controls whether we should preload resources during zygote init. */
private static final boolean PRELOAD_RESOURCES = true;
@@ -278,74 +280,76 @@ public class ZygoteInit {
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
- InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
- PRELOADED_CLASSES);
- if (is == null) {
+ InputStream is;
+ try {
+ is = new FileInputStream(PRELOADED_CLASSES);
+ } catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
- } else {
- Log.i(TAG, "Preloading classes...");
- long startTime = SystemClock.uptimeMillis();
+ return;
+ }
- // Drop root perms while running static initializers.
- setEffectiveGroup(UNPRIVILEGED_GID);
- setEffectiveUser(UNPRIVILEGED_UID);
+ Log.i(TAG, "Preloading classes...");
+ long startTime = SystemClock.uptimeMillis();
- // Alter the target heap utilization. With explicit GCs this
- // is not likely to have any effect.
- float defaultUtilization = runtime.getTargetHeapUtilization();
- runtime.setTargetHeapUtilization(0.8f);
+ // Drop root perms while running static initializers.
+ setEffectiveGroup(UNPRIVILEGED_GID);
+ setEffectiveUser(UNPRIVILEGED_UID);
- try {
- BufferedReader br
- = new BufferedReader(new InputStreamReader(is), 256);
-
- int count = 0;
- String line;
- while ((line = br.readLine()) != null) {
- // Skip comments and blank lines.
- line = line.trim();
- if (line.startsWith("#") || line.equals("")) {
- continue;
- }
+ // Alter the target heap utilization. With explicit GCs this
+ // is not likely to have any effect.
+ float defaultUtilization = runtime.getTargetHeapUtilization();
+ runtime.setTargetHeapUtilization(0.8f);
- try {
- if (false) {
- Log.v(TAG, "Preloading " + line + "...");
- }
- Class.forName(line);
- count++;
- } catch (ClassNotFoundException e) {
- Log.w(TAG, "Class not found for preloading: " + line);
- } catch (UnsatisfiedLinkError e) {
- Log.w(TAG, "Problem preloading " + line + ": " + e);
- } catch (Throwable t) {
- Log.e(TAG, "Error preloading " + line + ".", t);
- if (t instanceof Error) {
- throw (Error) t;
- }
- if (t instanceof RuntimeException) {
- throw (RuntimeException) t;
- }
- throw new RuntimeException(t);
- }
+ try {
+ BufferedReader br
+ = new BufferedReader(new InputStreamReader(is), 256);
+
+ int count = 0;
+ String line;
+ while ((line = br.readLine()) != null) {
+ // Skip comments and blank lines.
+ line = line.trim();
+ if (line.startsWith("#") || line.equals("")) {
+ continue;
}
- Log.i(TAG, "...preloaded " + count + " classes in "
- + (SystemClock.uptimeMillis()-startTime) + "ms.");
- } catch (IOException e) {
- Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
- } finally {
- IoUtils.closeQuietly(is);
- // Restore default.
- runtime.setTargetHeapUtilization(defaultUtilization);
-
- // Fill in dex caches with classes, fields, and methods brought in by preloading.
- runtime.preloadDexCaches();
-
- // Bring back root. We'll need it later.
- setEffectiveUser(ROOT_UID);
- setEffectiveGroup(ROOT_GID);
+ try {
+ if (false) {
+ Log.v(TAG, "Preloading " + line + "...");
+ }
+ Class.forName(line);
+ count++;
+ } catch (ClassNotFoundException e) {
+ Log.w(TAG, "Class not found for preloading: " + line);
+ } catch (UnsatisfiedLinkError e) {
+ Log.w(TAG, "Problem preloading " + line + ": " + e);
+ } catch (Throwable t) {
+ Log.e(TAG, "Error preloading " + line + ".", t);
+ if (t instanceof Error) {
+ throw (Error) t;
+ }
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ }
+ throw new RuntimeException(t);
+ }
}
+
+ Log.i(TAG, "...preloaded " + count + " classes in "
+ + (SystemClock.uptimeMillis()-startTime) + "ms.");
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
+ } finally {
+ IoUtils.closeQuietly(is);
+ // Restore default.
+ runtime.setTargetHeapUtilization(defaultUtilization);
+
+ // Fill in dex caches with classes, fields, and methods brought in by preloading.
+ runtime.preloadDexCaches();
+
+ // Bring back root. We'll need it later.
+ setEffectiveUser(ROOT_UID);
+ setEffectiveGroup(ROOT_GID);
}
}
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 64f3bea..f93b1a1 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -15,47 +15,34 @@
*/
package com.android.internal.policy;
-import android.view.MotionEvent;
-
import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.policy.IKeyguardStateCallback;
import com.android.internal.policy.IKeyguardExitCallback;
import android.os.Bundle;
-interface IKeyguardService {
- boolean isShowing();
- boolean isSecure();
- boolean isShowingAndNotOccluded();
- boolean isInputRestricted();
- boolean isDismissable();
- oneway void verifyUnlock(IKeyguardExitCallback callback);
- oneway void keyguardDone(boolean authenticated, boolean wakeup);
-
+oneway interface IKeyguardService {
/**
* Sets the Keyguard as occluded when a window dismisses the Keyguard with flag
* FLAG_SHOW_ON_LOCK_SCREEN.
*
* @param isOccluded Whether the Keyguard is occluded by another window.
- * @return See IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_*. This is needed because
- * PhoneWindowManager needs to set these flags immediately and can't wait for the
- * Keyguard thread to pick it up. In the hidden case, PhoneWindowManager is solely
- * responsible to make sure that the flags are unset.
*/
- int setOccluded(boolean isOccluded);
-
- oneway void dismiss();
- oneway void onDreamingStarted();
- oneway void onDreamingStopped();
- oneway void onScreenTurnedOff(int reason);
- oneway void onScreenTurnedOn(IKeyguardShowCallback callback);
- oneway void setKeyguardEnabled(boolean enabled);
- oneway void onSystemReady();
- oneway void doKeyguardTimeout(in Bundle options);
- oneway void setCurrentUser(int userId);
- oneway void showAssistant();
- oneway void dispatch(in MotionEvent event);
- oneway void launchCamera();
- oneway void onBootCompleted();
+ void setOccluded(boolean isOccluded);
+
+ void addStateMonitorCallback(IKeyguardStateCallback callback);
+ void verifyUnlock(IKeyguardExitCallback callback);
+ void keyguardDone(boolean authenticated, boolean wakeup);
+ void dismiss();
+ void onDreamingStarted();
+ void onDreamingStopped();
+ void onScreenTurnedOff(int reason);
+ void onScreenTurnedOn(IKeyguardShowCallback callback);
+ void setKeyguardEnabled(boolean enabled);
+ void onSystemReady();
+ void doKeyguardTimeout(in Bundle options);
+ void setCurrentUser(int userId);
+ void onBootCompleted();
/**
* Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper
@@ -64,11 +51,11 @@ interface IKeyguardService {
* @param startTime the start time of the animation in uptime milliseconds
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- oneway void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+ void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
/**
* Notifies the Keyguard that the activity that was starting has now been drawn and it's safe
* to start the keyguard dismiss sequence.
*/
- oneway void onActivityDrawn();
+ void onActivityDrawn();
}
diff --git a/core/java/com/android/internal/policy/IKeyguardServiceConstants.java b/core/java/com/android/internal/policy/IKeyguardServiceConstants.java
deleted file mode 100644
index b88174a..0000000
--- a/core/java/com/android/internal/policy/IKeyguardServiceConstants.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.internal.policy;
-
-/**
- * @hide
- */
-public class IKeyguardServiceConstants {
-
- /**
- * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}:
- * Don't change the keyguard window flags.
- */
- public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_NONE = 0;
-
- /**
- * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}:
- * Set the keyguard window flags to FLAG_SHOW_WALLPAPER and PRIVATE_FLAG_KEYGUARD.
- */
- public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_SET_FLAGS = 1;
-
- /**
- * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}:
- * Unset the keyguard window flags to FLAG_SHOW_WALLPAPER and PRIVATE_FLAG_KEYGUARD.
- */
- public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_UNSET_FLAGS = 2;
-}
diff --git a/core/java/android/net/http/Timer.java b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl
index cc15a30..db3b40b 100644
--- a/core/java/android/net/http/Timer.java
+++ b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -13,29 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.policy;
-package android.net.http;
-
-import android.os.SystemClock;
-
-/**
- * {@hide}
- * Debugging tool
- */
-class Timer {
-
- private long mStart;
- private long mLast;
-
- public Timer() {
- mStart = mLast = SystemClock.uptimeMillis();
- }
-
- public void mark(String message) {
- long now = SystemClock.uptimeMillis();
- if (HttpLog.LOGV) {
- HttpLog.v(message + " " + (now - mLast) + " total " + (now - mStart));
- }
- mLast = now;
- }
-}
+interface IKeyguardStateCallback {
+ void onShowingStateChanged(boolean showing);
+ void onSimSecureStateChanged(boolean simSecure);
+ void onInputRestrictedStateChanged(boolean inputRestricted);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3ccced5..f6c42af 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -191,9 +191,6 @@ public class LockPatternUtils {
return trust;
}
- /**
- * @param contentResolver Used to look up and save settings.
- */
public LockPatternUtils(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
@@ -384,8 +381,16 @@ public class LockPatternUtils {
* @return Whether a saved pattern exists.
*/
public boolean savedPatternExists() {
+ return savedPatternExists(getCurrentOrCallingUserId());
+ }
+
+ /**
+ * Check to see if the user has stored a lock pattern.
+ * @return Whether a saved pattern exists.
+ */
+ public boolean savedPatternExists(int userId) {
try {
- return getLockSettings().havePattern(getCurrentOrCallingUserId());
+ return getLockSettings().havePattern(userId);
} catch (RemoteException re) {
return false;
}
@@ -396,8 +401,16 @@ public class LockPatternUtils {
* @return Whether a saved pattern exists.
*/
public boolean savedPasswordExists() {
+ return savedPasswordExists(getCurrentOrCallingUserId());
+ }
+
+ /**
+ * Check to see if the user has stored a lock pattern.
+ * @return Whether a saved pattern exists.
+ */
+ public boolean savedPasswordExists(int userId) {
try {
- return getLockSettings().havePassword(getCurrentOrCallingUserId());
+ return getLockSettings().havePassword(userId);
} catch (RemoteException re) {
return false;
}
@@ -474,17 +487,23 @@ public class LockPatternUtils {
return activePasswordQuality;
}
+ public void clearLock(boolean isFallback) {
+ clearLock(isFallback, getCurrentOrCallingUserId());
+ }
+
/**
* Clear any lock pattern or password.
*/
- public void clearLock(boolean isFallback) {
- if(!isFallback) deleteGallery();
- saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
- setLockPatternEnabled(false);
- saveLockPattern(null);
- setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
- setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
- onAfterChangingPassword();
+ public void clearLock(boolean isFallback, int userHandle) {
+ if(!isFallback) deleteGallery(userHandle);
+ saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, isFallback,
+ userHandle);
+ setLockPatternEnabled(false, userHandle);
+ saveLockPattern(null, isFallback, userHandle);
+ setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+ setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+ userHandle);
+ onAfterChangingPassword(userHandle);
}
/**
@@ -531,11 +550,11 @@ public class LockPatternUtils {
/**
* Calls back SetupFaceLock to delete the gallery file when the lock type is changed
*/
- void deleteGallery() {
- if(usingBiometricWeak()) {
+ void deleteGallery(int userId) {
+ if(usingBiometricWeak(userId)) {
Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY");
intent.putExtra("deleteGallery", true);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
}
}
@@ -550,11 +569,20 @@ public class LockPatternUtils {
/**
* Save a lock pattern.
* @param pattern The new pattern to save.
- * @param isFallback Specifies if this is a fallback to biometric weak
*/
public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
+ this.saveLockPattern(pattern, isFallback, getCurrentOrCallingUserId());
+ }
+
+ /**
+ * Save a lock pattern.
+ * @param pattern The new pattern to save.
+ * @param isFallback Specifies if this is a fallback to biometric weak
+ * @param userId the user whose pattern is to be saved.
+ */
+ public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback,
+ int userId) {
try {
- int userId = getCurrentOrCallingUserId();
getLockSettings().setLockPattern(patternToString(pattern), userId);
DevicePolicyManager dpm = getDevicePolicyManager();
if (pattern != null) {
@@ -570,17 +598,17 @@ public class LockPatternUtils {
}
}
- setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
+ setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
if (!isFallback) {
- deleteGallery();
- setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+ deleteGallery(userId);
+ setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
pattern.size(), 0, 0, 0, 0, 0, 0, userId);
} else {
- setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
+ setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, userId);
setLong(PASSWORD_TYPE_ALTERNATE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
- finishBiometricWeak();
+ DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
+ finishBiometricWeak(userId);
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
0, 0, 0, 0, 0, 0, 0, userId);
}
@@ -588,7 +616,7 @@ public class LockPatternUtils {
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
0, 0, 0, 0, 0, userId);
}
- onAfterChangingPassword();
+ onAfterChangingPassword(userId);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't save lock pattern " + re);
}
@@ -806,7 +834,7 @@ public class LockPatternUtils {
}
if (!isFallback) {
- deleteGallery();
+ deleteGallery(userHandle);
setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
int letters = 0;
@@ -846,7 +874,7 @@ public class LockPatternUtils {
userHandle);
setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality),
userHandle);
- finishBiometricWeak();
+ finishBiometricWeak(userHandle);
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
0, 0, 0, 0, 0, 0, 0, userHandle);
}
@@ -854,7 +882,7 @@ public class LockPatternUtils {
// password hashes have the same length for simplicity of implementation.
String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
if (passwordHistory == null) {
- passwordHistory = new String();
+ passwordHistory = "";
}
int passwordHistoryLength = getRequestedPasswordHistoryLength();
if (passwordHistoryLength == 0) {
@@ -881,7 +909,7 @@ public class LockPatternUtils {
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
userHandle);
}
- onAfterChangingPassword();
+ onAfterChangingPassword(userHandle);
} catch (RemoteException re) {
// Cant do much
Log.e(TAG, "Unable to save lock password " + re);
@@ -955,8 +983,15 @@ public class LockPatternUtils {
* @return true if the lockscreen method is set to biometric weak
*/
public boolean usingBiometricWeak() {
- int quality =
- (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ return usingBiometricWeak(getCurrentOrCallingUserId());
+ }
+
+ /**
+ * @return true if the lockscreen method is set to biometric weak
+ */
+ public boolean usingBiometricWeak(int userId) {
+ int quality = (int) getLong(
+ PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
}
@@ -1096,15 +1131,22 @@ public class LockPatternUtils {
* @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
*/
public boolean isLockPatternEnabled() {
+ return isLockPatternEnabled(getCurrentOrCallingUserId());
+ }
+
+ /**
+ * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
+ */
+ public boolean isLockPatternEnabled(int userId) {
final boolean backupEnabled =
getLong(PASSWORD_TYPE_ALTERNATE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId)
== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
- return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false)
- && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
- == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
- (usingBiometricWeak() && backupEnabled));
+ return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false, userId)
+ && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+ userId) == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+ || (usingBiometricWeak(userId) && backupEnabled));
}
/**
@@ -1160,7 +1202,14 @@ public class LockPatternUtils {
* Set whether the lock pattern is enabled.
*/
public void setLockPatternEnabled(boolean enabled) {
- setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
+ setLockPatternEnabled(enabled, getCurrentOrCallingUserId());
+ }
+
+ /**
+ * Set whether the lock pattern is enabled.
+ */
+ public void setLockPatternEnabled(boolean enabled, int userHandle) {
+ setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled, userHandle);
}
/**
@@ -1485,15 +1534,20 @@ public class LockPatternUtils {
}
public boolean isSecure() {
- long mode = getKeyguardStoredPasswordQuality();
+ return isSecure(getCurrentOrCallingUserId());
+ }
+
+ public boolean isSecure(int userId) {
+ long mode = getKeyguardStoredPasswordQuality(userId);
final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
|| mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
|| mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
|| mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
|| mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
- final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
- || isPassword && savedPasswordExists();
+ final boolean secure =
+ isPattern && isLockPatternEnabled(userId) && savedPatternExists(userId)
+ || isPassword && savedPasswordExists(userId);
return secure;
}
@@ -1549,15 +1603,15 @@ public class LockPatternUtils {
return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
}
- private void finishBiometricWeak() {
- setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
+ private void finishBiometricWeak(int userId) {
+ setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true, userId);
// Launch intent to show final screen, this also
// moves the temporary gallery to the actual gallery
Intent intent = new Intent();
intent.setClassName("com.android.facelock",
"com.android.facelock.SetupEndScreen");
- mContext.startActivity(intent);
+ mContext.startActivityAsUser(intent, new UserHandle(userId));
}
public void setPowerButtonInstantlyLocks(boolean enabled) {
@@ -1651,8 +1705,8 @@ public class LockPatternUtils {
getTrustManager().reportRequireCredentialEntry(userId);
}
- private void onAfterChangingPassword() {
- getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
+ private void onAfterChangingPassword(int userHandle) {
+ getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
}
public boolean isCredentialRequiredToDecrypt(boolean defaultValue) {
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 99b1bae..d617c05 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -26,6 +26,7 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
@@ -76,6 +77,19 @@ public class SwipeDismissLayout extends FrameLayout {
private OnDismissedListener mDismissedListener;
private OnSwipeProgressChangedListener mProgressListener;
+ private ViewTreeObserver.OnEnterAnimationCompleteListener mOnEnterAnimationCompleteListener =
+ new ViewTreeObserver.OnEnterAnimationCompleteListener() {
+ @Override
+ public void onEnterAnimationComplete() {
+ // SwipeDismissLayout assumes that the host Activity is translucent
+ // and temporarily disables translucency when it is fully visible.
+ // As soon as the user starts swiping, we will re-enable
+ // translucency.
+ if (getContext() instanceof Activity) {
+ ((Activity) getContext()).convertFromTranslucent();
+ }
+ }
+ };
private float mLastX;
@@ -103,13 +117,6 @@ public class SwipeDismissLayout extends FrameLayout {
android.R.integer.config_shortAnimTime);
mCancelInterpolator = new DecelerateInterpolator(1.5f);
mDismissInterpolator = new AccelerateInterpolator(1.5f);
- // SwipeDismissLayout assumes that the host Activity is translucent
- // and temporarily disables translucency when it is fully visible.
- // As soon as the user starts swiping, we will re-enable
- // translucency.
- if (context instanceof Activity) {
- ((Activity) context).convertFromTranslucent();
- }
}
public void setOnDismissedListener(OnDismissedListener listener) {
@@ -121,6 +128,24 @@ public class SwipeDismissLayout extends FrameLayout {
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (getContext() instanceof Activity) {
+ getViewTreeObserver().addOnEnterAnimationCompleteListener(
+ mOnEnterAnimationCompleteListener);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (getContext() instanceof Activity) {
+ getViewTreeObserver().removeOnEnterAnimationCompleteListener(
+ mOnEnterAnimationCompleteListener);
+ }
+ }
+
+ @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// offset because the view is translated during swipe
ev.offsetLocation(mTranslationX, 0);
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 26e2e2a..ed7ce63 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -23,6 +23,7 @@ import android.app.backup.BackupDataOutput;
import android.app.backup.BackupAgentHelper;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
+import android.app.backup.RecentsBackupHelper;
import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
import android.os.Environment;
@@ -83,6 +84,8 @@ public class SystemBackupAgent extends BackupAgentHelper {
}
}
addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys));
+ addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
+
super.onBackup(oldState, data, newState);
}
@@ -113,6 +116,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this,
new String[] { WALLPAPER_IMAGE },
new String[] { WALLPAPER_IMAGE_KEY} ));
+ addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
try {
super.onRestore(data, appVersionCode, newState);