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/animation/TimeAnimator.java25
-rw-r--r--core/java/android/animation/ValueAnimator.java42
-rw-r--r--core/java/android/app/ActivityManagerNative.java38
-rw-r--r--core/java/android/app/ActivityThread.java5
-rw-r--r--core/java/android/app/ContextImpl.java29
-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.java8
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java13
-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/content/ContentProvider.java75
-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.java14
-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.java2
-rw-r--r--core/java/android/net/VpnService.java27
-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/BatteryStats.java71
-rw-r--r--core/java/android/os/UserManager.java4
-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.java59
-rw-r--r--core/java/android/service/carriermessaging/MessagePdu.java13
-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/text/StaticLayout.java6
-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/view/ThreadedRenderer.java2
-rw-r--r--core/java/android/view/View.java296
-rw-r--r--core/java/android/view/ViewGroup.java13
-rw-r--r--core/java/android/view/WindowManagerGlobal.java29
-rw-r--r--core/java/android/view/WindowManagerImpl.java36
-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/DatePickerCalendarDelegate.java13
-rw-r--r--core/java/android/widget/DayPickerView.java85
-rw-r--r--core/java/android/widget/FastScroller.java32
-rw-r--r--core/java/android/widget/RadialTimePickerView.java5
-rw-r--r--core/java/android/widget/SearchView.java213
-rw-r--r--core/java/android/widget/SimpleMonthAdapter.java31
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java8
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java59
53 files changed, 2948 insertions, 2076 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/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/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 e3de44e..98a096c 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) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7fafc38..1de9b47 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1863,6 +1863,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) {
@@ -1951,7 +1966,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..07e8dc5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -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 ad1cf44..57d53aa 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -182,8 +182,8 @@ public class DevicePolicyManager {
* <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_DONT_DISABLE_SYSTEM_APPS =
- "android.app.extra.PROVISIONING_DONT_DISABLE_SYSTEM_APPS";
+ 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
@@ -3421,12 +3421,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/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/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4c82efd..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;
}
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..a73ba74 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;
@@ -2864,10 +2865,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 interacting with the status bar.
*
* @see #getSystemService
- * @see android.app.UsageStatsManager
+ * @see android.app.usage.UsageStatsManager
* @hide
*/
public static final String USAGE_STATS_SERVICE = "usagestats";
@@ -2921,6 +2922,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 +3114,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..a13a2ea 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
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index c848993..d469487 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;
@@ -164,6 +165,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.
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/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/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/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 270d786..4135e8b 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 1f45f0a..3c12e06 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.java b/core/java/android/service/carriermessaging/CarrierMessagingService.java
index 101f69b..7aea590 100644
--- a/core/java/android/service/carriermessaging/CarrierMessagingService.java
+++ b/core/java/android/service/carriermessaging/CarrierMessagingService.java
@@ -16,6 +16,7 @@
package android.service.carriermessaging;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.app.Service;
@@ -93,7 +94,7 @@ public abstract class CarrierMessagingService extends Service {
* @return True to keep an inbound SMS message and delivered to SMS apps. False to
* drop the message.
*/
- public boolean onFilterSms(MessagePdu pdu, String format, int destPort) {
+ public boolean onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort) {
// optional
return true;
}
@@ -105,9 +106,11 @@ public abstract class CarrierMessagingService extends Service {
* @param format the format of the response PDU, typically "3gpp" or "3gpp2"
* @param destAddress phone number of the recipient of the message
*
- * @return a {@link SendSmsResponse}.
+ * @return a possibly {code null} {@link SendSmsResponse}. Upon returning {@code null}, the SMS
+ * is sent using the carrier network.
*/
- public SendSmsResponse onSendTextSms(String text, String format, String destAddress) {
+ public @Nullable SendSmsResponse onSendTextSms(
+ @NonNull String text, @NonNull String format, @NonNull String destAddress) {
// optional
return null;
}
@@ -120,10 +123,11 @@ public abstract class CarrierMessagingService extends Service {
* @param destAddress phone number of the recipient of the message
* @param destPort the destination port
*
- * @return a {@link SendSmsResponse}
+ * @return a possibly {code null} {@link SendSmsResponse}. Upon returning {@code null}, the SMS
+ * is sent using the carrier network.
*/
- public SendSmsResponse onSendDataSms(byte[] data, String format, String destAddress,
- int destPort) {
+ public @Nullable SendSmsResponse onSendDataSms(@NonNull byte[] data, @NonNull String format,
+ @NonNull String destAddress, int destPort) {
// optional
return null;
}
@@ -135,10 +139,11 @@ public abstract class CarrierMessagingService extends Service {
* @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 {@link List} of {@link SendSmsResponse}, one for each message part.
+ * @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 List<SendSmsResponse> onSendMultipartTextSms(List<String> parts, String format,
- String destAddress) {
+ public @Nullable List<SendSmsResponse> onSendMultipartTextSms(@NonNull List<String> parts,
+ @NonNull String format, @NonNull String destAddress) {
// optional
return null;
}
@@ -150,9 +155,10 @@ public abstract class CarrierMessagingService extends Service {
* @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 {@link SendMmsResult}.
+ * @return a possibly {@code null} {@link SendMmsResult}. Upon returning {@code null}, the
+ * MMS is sent using the carrier network.
*/
- public SendMmsResult onSendMms(Uri pduUri, @Nullable String locationUrl) {
+ public @Nullable SendMmsResult onSendMms(@NonNull Uri pduUri, @Nullable String locationUrl) {
// optional
return null;
}
@@ -165,13 +171,13 @@ public abstract class CarrierMessagingService extends Service {
*
* @return a {@link SendMmsResult}.
*/
- public int onDownloadMms(Uri contentUri, String locationUrl) {
+ public int onDownloadMms(@NonNull Uri contentUri, @NonNull String locationUrl) {
// optional
return DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK;
}
@Override
- public IBinder onBind(Intent intent) {
+ public @Nullable IBinder onBind(@NonNull Intent intent) {
if (!SERVICE_INTERFACE.equals(intent.getAction())) {
return null;
}
@@ -185,12 +191,24 @@ public abstract class CarrierMessagingService extends Service {
private int mResult;
private byte[] mSendConfPdu;
- public SendMmsResult(int result, byte[] sendConfPdu) {
+ /**
+ * 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}
*/
@@ -199,9 +217,11 @@ public abstract class CarrierMessagingService extends Service {
}
/**
- * @return the SendConf PDU, which confirms that the message was sent.
+ * Returns the SendConf PDU, which confirms that the message was sent.
+ *
+ * @return the SendConf PDU
*/
- public byte[] getSendConfPdu() {
+ public @Nullable byte[] getSendConfPdu() {
return mSendConfPdu;
}
}
@@ -219,12 +239,15 @@ public abstract class CarrierMessagingService extends Service {
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, byte[] ackPdu, int errorCode) {
+ public SendSmsResponse(int messageRef, @NonNull byte[] ackPdu, int errorCode) {
mMessageRef = messageRef;
mAckPdu = ackPdu;
mErrorCode = errorCode;
@@ -244,7 +267,7 @@ public abstract class CarrierMessagingService extends Service {
*
* @return the ackPdu
*/
- public byte[] getAckPdu() {
+ public @NonNull byte[] getAckPdu() {
return mAckPdu;
}
diff --git a/core/java/android/service/carriermessaging/MessagePdu.java b/core/java/android/service/carriermessaging/MessagePdu.java
index b81719f..3c78568 100644
--- a/core/java/android/service/carriermessaging/MessagePdu.java
+++ b/core/java/android/service/carriermessaging/MessagePdu.java
@@ -16,6 +16,7 @@
package android.service.carriermessaging;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,9 +32,14 @@ public final class MessagePdu implements Parcelable {
private final List<byte[]> mPduList;
/**
+ * Constructs a MessagePdu with the list of message PDUs.
+ *
* @param pduList the list of message PDUs
*/
- public MessagePdu(List<byte[]> pduList) {
+ 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;
}
@@ -42,7 +48,7 @@ public final class MessagePdu implements Parcelable {
*
* @return the list of PDUs
*/
- public List<byte[]> getPdus() {
+ public @NonNull List<byte[]> getPdus() {
return mPduList;
}
@@ -51,9 +57,6 @@ public final class MessagePdu implements Parcelable {
return 0;
}
- /**
- * Writes the PDU into a {@link Parcel}.
- */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (mPduList == null) {
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/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index e82057c..02297e3 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -383,7 +383,6 @@ public class StaticLayout extends Layout {
okBottom = fitBottom;
}
} else {
- final boolean moreChars;
int endPos;
int above, below, top, bottom;
float currentTextWidth;
@@ -395,7 +394,6 @@ public class StaticLayout extends Layout {
top = okTop;
bottom = okBottom;
currentTextWidth = okWidth;
- moreChars = (j + 1 < spanEnd);
} else if (fit != here) {
endPos = fit;
above = fitAscent;
@@ -403,7 +401,6 @@ public class StaticLayout extends Layout {
top = fitTop;
bottom = fitBottom;
currentTextWidth = fitWidth;
- moreChars = (j + 1 < spanEnd);
} else {
// must make progress, so take next character
endPos = here + 1;
@@ -417,7 +414,6 @@ public class StaticLayout extends Layout {
top = fmTop;
bottom = fmBottom;
currentTextWidth = widths[here - paraStart];
- moreChars = (endPos < spanEnd);
}
v = out(source, here, endPos,
@@ -425,7 +421,7 @@ public class StaticLayout extends Layout {
v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, hasTabOrEmoji,
needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
chs, widths, paraStart, ellipsize, ellipsizedWidth,
- currentTextWidth, paint, moreChars);
+ currentTextWidth, paint, true);
here = endPos;
j = here - 1; // restart j-span loop from here, compensating for the j++
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/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 5579c13..2c8a499 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()) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 77c1d7b..b54d462 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;
@@ -11277,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);
@@ -14411,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);
+ }
- 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;
+
+ 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();
- canvas.translate(-mScrollX, -mScrollY);
+ if (autoScale && scalingRequired) {
+ final float scale = attachInfo.mApplicationScale;
+ canvas.scale(scale, scale);
+ }
- mPrivateFlags |= PFLAG_DRAWN;
- if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
- mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
- }
+ canvas.translate(-mScrollX, -mScrollY);
- // 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);
+ 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);
+ }
- 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;
}
}
@@ -14774,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);
}
/**
@@ -14873,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(
@@ -15478,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.
@@ -15486,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;
}
}
@@ -15505,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);
}
/**
@@ -15861,7 +15856,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
- mPrivateFlags3 |= PFLAG3_OUTLINE_INVALID;
+ rebuildOutline();
}
/**
@@ -15897,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();
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index bae0b12..1a5ff26 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
*
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/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/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/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..06bb32c 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -1194,17 +1194,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 +1242,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;
}
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/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/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/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3ccced5..8d3db5b 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -384,8 +384,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 +404,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;
}
@@ -955,8 +971,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 +1119,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));
}
/**
@@ -1485,15 +1515,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;
}