Merge changes Ia0f54124,I1e085f5e,Ic0a3ff0a,I141847df,I8ae4efa2 into lmp-preview-dev
* changes:
camera2: Update native/managed key mappings.
Camera2: switch API interface to java classses
Camera2: Use ColorSpaceTransform and RggbChannelVector, replace enum byte[]
camera2: Add new metadata keys, change types for existing range keys
Camera2: Replace int[] by MeteringRectangle
diff --git a/Android.mk b/Android.mk
index 62d5446..8603d99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,6 +78,7 @@
core/java/android/app/IServiceConnection.aidl \
core/java/android/app/IStopUserCallback.aidl \
core/java/android/app/task/ITaskCallback.aidl \
+ core/java/android/app/task/ITaskManager.aidl \
core/java/android/app/task/ITaskService.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
@@ -328,6 +329,7 @@
telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
+ telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/api/current.txt b/api/current.txt
index 441ad26..cddbbf8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4689,6 +4689,7 @@
method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
field public static final int SIZE_DEFAULT = 0; // 0x0
+ field public static final int SIZE_FULL_SCREEN = 5; // 0x5
field public static final int SIZE_LARGE = 4; // 0x4
field public static final int SIZE_MEDIUM = 3; // 0x3
field public static final int SIZE_SMALL = 2; // 0x2
@@ -5350,6 +5351,57 @@
package android.app.task {
+ public class Task implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getBackoffPolicy();
+ method public android.os.Bundle getExtras();
+ method public long getInitialBackoffMillis();
+ method public long getIntervalMillis();
+ method public long getMaxExecutionDelayMillis();
+ method public long getMinLatencyMillis();
+ method public int getNetworkCapabilities();
+ method public android.content.ComponentName getService();
+ method public int getTaskId();
+ method public boolean isPeriodic();
+ method public boolean isRequireCharging();
+ method public boolean isRequireDeviceIdle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static abstract interface Task.BackoffPolicy {
+ field public static final int EXPONENTIAL = 1; // 0x1
+ field public static final int LINEAR = 0; // 0x0
+ }
+
+ public final class Task.Builder {
+ ctor public Task.Builder(int, android.content.ComponentName);
+ method public android.app.task.Task build();
+ method public android.app.task.Task.Builder setBackoffCriteria(long, int);
+ method public android.app.task.Task.Builder setExtras(android.os.Bundle);
+ method public android.app.task.Task.Builder setMinimumLatency(long);
+ method public android.app.task.Task.Builder setOverrideDeadline(long);
+ method public android.app.task.Task.Builder setPeriodic(long);
+ method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int);
+ method public android.app.task.Task.Builder setRequiresCharging(boolean);
+ method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean);
+ }
+
+ public static abstract interface Task.NetworkType {
+ field public static final int ANY = 0; // 0x0
+ field public static final int UNMETERED = 1; // 0x1
+ }
+
+ public abstract class TaskManager {
+ ctor public TaskManager();
+ method public abstract void cancel(int);
+ method public abstract void cancelAll();
+ method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
+ method public abstract int schedule(android.app.task.Task);
+ field public static final int RESULT_INVALID_PARAMETERS = -1; // 0xffffffff
+ field public static final int RESULT_OVER_QUOTA = -2; // 0xfffffffe
+ }
+
public class TaskParams implements android.os.Parcelable {
method public int describeContents();
method public android.os.Bundle getExtras();
@@ -6973,6 +7025,7 @@
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String TASK_SERVICE = "task";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -7911,55 +7964,6 @@
method public abstract void onStatusChanged(int);
}
- public class Task implements android.os.Parcelable {
- method public int describeContents();
- method public int getBackoffPolicy();
- method public android.os.Bundle getExtras();
- method public long getInitialBackoffMillis();
- method public long getIntervalMillis();
- method public long getMaxExecutionDelayMillis();
- method public long getMinLatencyMillis();
- method public int getNetworkCapabilities();
- method public android.content.ComponentName getService();
- method public int getTaskId();
- method public boolean isPeriodic();
- method public boolean isRequireCharging();
- method public boolean isRequireDeviceIdle();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public static abstract interface Task.BackoffPolicy {
- field public static final int EXPONENTIAL = 1; // 0x1
- field public static final int LINEAR = 0; // 0x0
- }
-
- public final class Task.Builder {
- ctor public Task.Builder(int, android.content.ComponentName);
- method public android.content.Task build();
- method public android.content.Task.Builder setBackoffCriteria(long, int);
- method public android.content.Task.Builder setExtras(android.os.Bundle);
- method public android.content.Task.Builder setMinimumLatency(long);
- method public android.content.Task.Builder setOverrideDeadline(long);
- method public android.content.Task.Builder setPeriodic(long);
- method public android.content.Task.Builder setRequiredNetworkCapabilities(int);
- method public android.content.Task.Builder setRequiresCharging(boolean);
- method public android.content.Task.Builder setRequiresDeviceIdle(boolean);
- }
-
- public static abstract interface Task.NetworkType {
- field public static final int ANY = 0; // 0x0
- field public static final int UNMETERED = 1; // 0x1
- }
-
- public abstract class TaskManager {
- ctor public TaskManager();
- method public abstract void cancel(int);
- method public abstract void cancelAll();
- method public abstract java.util.List<android.content.Task> getAllPendingTasks();
- method public abstract int schedule(android.content.Task);
- }
-
public class UriMatcher {
ctor public UriMatcher(int);
method public void addURI(java.lang.String, java.lang.String, int);
@@ -13712,45 +13716,6 @@
method public void stop();
}
- public final class AudioAttributes {
- method public int getContentType();
- method public int getFlags();
- method public java.util.Set<java.lang.String> getTags();
- method public int getUsage();
- field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
- field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
- field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
- field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
- field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
- field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
- field public static final int USAGE_ALARM = 4; // 0x4
- field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
- field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
- field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
- field public static final int USAGE_GAME = 14; // 0xe
- field public static final int USAGE_MEDIA = 1; // 0x1
- field public static final int USAGE_NOTIFICATION = 5; // 0x5
- field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
- field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
- field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
- field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
- field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6
- field public static final int USAGE_UNKNOWN = 0; // 0x0
- field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
- field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
- }
-
- public static class AudioAttributes.Builder {
- ctor public AudioAttributes.Builder();
- ctor public AudioAttributes.Builder(android.media.AudioAttributes);
- method public android.media.AudioAttributes.Builder addTag(java.lang.String);
- method public android.media.AudioAttributes build();
- method public android.media.AudioAttributes.Builder setContentType(int);
- method public android.media.AudioAttributes.Builder setFlags(int);
- method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
- method public android.media.AudioAttributes.Builder setUsage(int);
- }
-
public class AudioFormat {
ctor public AudioFormat();
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 07de85c..b5281ff 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4194,7 +4194,11 @@
*/
public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
int requestCode) {
- startActivityFromFragment(fragment, intent, requestCode, null);
+ Bundle options = null;
+ if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+ options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
+ }
+ startActivityFromFragment(fragment, intent, requestCode, options);
}
/**
@@ -4219,6 +4223,9 @@
*/
public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
int requestCode, @Nullable Bundle options) {
+ if (options != null) {
+ mActivityTransitionState.startExitOutTransition(this, options);
+ }
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, fragment,
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 7617886..d08978bd 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -189,15 +189,17 @@
final protected SharedElementListener mListener;
protected ResultReceiver mResultReceiver;
final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
+ final protected boolean mIsReturning;
public ActivityTransitionCoordinator(Window window,
ArrayList<String> allSharedElementNames,
ArrayList<String> accepted, ArrayList<String> localNames,
- SharedElementListener listener) {
+ SharedElementListener listener, boolean isReturning) {
super(new Handler());
mWindow = window;
mListener = listener;
mAllSharedElementNames = allSharedElementNames;
+ mIsReturning = isReturning;
setSharedElements(accepted, localNames);
if (getViewsTransition() != null) {
getDecor().captureTransitioningViews(mTransitioningViews);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1634d11..ff8688d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -133,10 +133,12 @@
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.app.admin.DevicePolicyManager;
+import android.app.task.ITaskManager;
import android.app.trust.TrustManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.appwidget.IAppWidgetService.Stub;
import com.android.internal.os.IDropBoxManagerService;
import java.io.File;
@@ -693,6 +695,12 @@
public Object createService(ContextImpl ctx) {
return new UsageStatsManager(ctx.getOuterContext());
}});
+
+ registerService(TASK_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(TASK_SERVICE);
+ return new TaskManagerImpl(ITaskManager.Stub.asInterface(b));
+ }});
}
static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 4cca355..bc97852 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -55,16 +55,14 @@
private boolean mHasStopped;
private Handler mHandler;
private boolean mIsCanceled;
- private boolean mIsReturning;
private ObjectAnimator mBackgroundAnimator;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames,
ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
- getListener(activity, acceptedNames));
+ getListener(activity, acceptedNames), acceptedNames != null);
mActivity = activity;
- mIsReturning = acceptedNames != null;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index f36c36a..93eb53e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -58,16 +58,14 @@
private Handler mHandler;
- private boolean mIsReturning;
-
private ObjectAnimator mBackgroundAnimator;
private boolean mIsHidden;
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
- super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning));
- mIsReturning = isReturning;
+ super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
+ isReturning);
mIsBackgroundReady = !isReturning;
mActivity = activity;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 70f270f..25f24b1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3355,6 +3355,14 @@
*/
public static final int SIZE_LARGE = 4;
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * full screen.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_FULL_SCREEN = 5;
+
/** Notification extra which contains wearable extensions */
private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java
new file mode 100644
index 0000000..f42839e
--- /dev/null
+++ b/core/java/android/app/TaskManagerImpl.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+// in android.app so ContextImpl has package access
+package android.app;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
+import android.app.task.TaskManager;
+
+import java.util.List;
+
+
+/**
+ * Concrete implementation of the TaskManager interface
+ * @hide
+ */
+public class TaskManagerImpl extends TaskManager {
+ ITaskManager mBinder;
+
+ /* package */ TaskManagerImpl(ITaskManager binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int schedule(Task task) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void cancel(int taskId) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void cancelAll() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public List<Task> getAllPendingTasks() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl
new file mode 100644
index 0000000..b56c78a
--- /dev/null
+++ b/core/java/android/app/task/ITaskManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * 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.app.task;
+
+import android.app.task.Task;
+
+ /**
+ * IPC interface that supports the app-facing {@link #TaskManager} api.
+ * {@hide}
+ */
+interface ITaskManager {
+ int schedule(in Task task);
+ void cancel(int taskId);
+ void cancelAll();
+ List<Task> getAllPendingTasks();
+}
diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl
new file mode 100644
index 0000000..1f25439
--- /dev/null
+++ b/core/java/android/app/task/Task.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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.app.task;
+
+parcelable Task;
+
\ No newline at end of file
diff --git a/core/java/android/content/Task.java b/core/java/android/app/task/Task.java
similarity index 96%
rename from core/java/android/content/Task.java
rename to core/java/android/app/task/Task.java
index 407880f..dd184a5 100644
--- a/core/java/android/content/Task.java
+++ b/core/java/android/app/task/Task.java
@@ -14,15 +14,15 @@
* limitations under the License
*/
-package android.content;
+package android.app.task;
-import android.app.task.TaskService;
+import android.content.ComponentName;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the
+ * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the
* parameters required to schedule work against the calling application. These are constructed
* using the {@link Task.Builder}.
*/
@@ -92,7 +92,7 @@
}
/**
- * See {@link android.content.Task.NetworkType} for a description of this value.
+ * See {@link android.app.task.Task.NetworkType} for a description of this value.
*/
public int getNetworkCapabilities() {
return networkCapabilities;
@@ -139,7 +139,7 @@
}
/**
- * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field
+ * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field
* can take. This defaults to exponential.
*/
public int getBackoffPolicy() {
@@ -255,7 +255,7 @@
/**
* Set some description of the kind of network capabilities you would like to have. This
- * will be a parameter defined in {@link android.content.Task.NetworkType}.
+ * will be a parameter defined in {@link android.app.task.Task.NetworkType}.
* Not calling this function means the network is not necessary.
* Bear in mind that calling this function defines network as a strict requirement for your
* task if the network requested is not available your task will never run. See
@@ -314,7 +314,7 @@
* Specify that this task should be delayed by the provided amount of time.
* Because it doesn't make sense setting this property on a periodic task, doing so will
* throw an {@link java.lang.IllegalArgumentException} when
- * {@link android.content.Task.Builder#build()} is called.
+ * {@link android.app.task.Task.Builder#build()} is called.
* @param minLatencyMillis Milliseconds before which this task will not be considered for
* execution.
*/
@@ -328,7 +328,7 @@
* deadline even if other requirements are not met. Because it doesn't make sense setting
* this property on a periodic task, doing so will throw an
* {@link java.lang.IllegalArgumentException} when
- * {@link android.content.Task.Builder#build()} is called.
+ * {@link android.app.task.Task.Builder#build()} is called.
*/
public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
mMaxExecutionDelayMillis = maxExecutionDelayMillis;
diff --git a/core/java/android/content/TaskManager.java b/core/java/android/app/task/TaskManager.java
similarity index 75%
rename from core/java/android/content/TaskManager.java
rename to core/java/android/app/task/TaskManager.java
index d28d78a..0fbe37d 100644
--- a/core/java/android/content/TaskManager.java
+++ b/core/java/android/app/task/TaskManager.java
@@ -14,14 +14,19 @@
* limitations under the License
*/
-package android.content;
+package android.app.task;
import java.util.List;
+import android.content.Context;
+
/**
* Class for scheduling various types of tasks with the scheduling framework on the device.
*
- * Get an instance of this class through {@link Context#getSystemService(String)}.
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TASK_SERVICE)}.
*/
public abstract class TaskManager {
/*
@@ -29,18 +34,20 @@
* if the run-time for your task is too short, or perhaps the system can't resolve the
* requisite {@link TaskService} in your package.
*/
- static final int RESULT_INVALID_PARAMETERS = -1;
+ public static final int RESULT_INVALID_PARAMETERS = -1;
+
/**
* Returned from {@link #schedule(Task)} if this application has made too many requests for
* work over too short a time.
*/
// TODO: Determine if this is necessary.
- static final int RESULT_OVER_QUOTA = -2;
+ public static final int RESULT_OVER_QUOTA = -2;
- /*
- * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on
- * the sorts of tasks you can schedule.
- * @return If >0, this int corresponds to the taskId of the successfully scheduled task.
+ /**
+ * @param task The task you wish scheduled. See
+ * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks
+ * you can schedule.
+ * @return If >0, this int returns the taskId of the successfully scheduled task.
* Otherwise you have to compare the return value to the error codes defined in this class.
*/
public abstract int schedule(Task task);
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
index 0351082..dacb3480 100644
--- a/core/java/android/app/task/TaskParams.java
+++ b/core/java/android/app/task/TaskParams.java
@@ -47,7 +47,7 @@
/**
* @return The extras you passed in when constructing this task with
- * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will
+ * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will
* never be null. If you did not set any extras this will be an empty bundle.
*/
public Bundle getExtras() {
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
index ab1a565..8ce4484 100644
--- a/core/java/android/app/task/TaskService.java
+++ b/core/java/android/app/task/TaskService.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.GuardedBy;
/**
- * <p>Entry point for the callback from the {@link android.content.TaskManager}.</p>
+ * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p>
* <p>This is the base class that handles asynchronous requests that were previously scheduled. You
* are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where
* you will implement your task logic.</p>
@@ -215,9 +215,9 @@
*
* <p>This will happen if the requirements specified at schedule time are no longer met. For
* example you may have requested WiFi with
- * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+ * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
* task was executing the user toggled WiFi. Another example is if you had specified
- * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+ * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
* idle maintenance window. You are solely responsible for the behaviour of your application
* upon receipt of this message; your app will likely start to misbehave if you ignore it. One
* immediate repercussion is that the system will cease holding a wakelock for you.</p>
@@ -237,7 +237,7 @@
* You can specify post-execution behaviour to the scheduler here with
* <code>needsReschedule </code>. This will apply a back-off timer to your task based on
* the default, or what was set with
- * {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original
+ * {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original
* requirements are always honoured even for a backed-off task. Note that a task running in
* idle mode will not be backed-off. Instead what will happen is the task will be re-added
* to the queue and re-executed within a future idle maintenance window.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ae006c..b0673b5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2023,6 +2023,7 @@
PRINT_SERVICE,
MEDIA_SESSION_SERVICE,
BATTERY_SERVICE,
+ TASK_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -2079,6 +2080,8 @@
* <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
* <dt> {@link #BATTERY_SERVICE} ("batterymanager")
* <dd> A {@link android.os.BatteryManager} for managing battery state
+ * <dt> {@link #TASK_SERVICE} ("taskmanager")
+ * <dd> A {@link android.app.task.TaskManager} for managing scheduled tasks
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -2134,6 +2137,8 @@
* @see android.app.DownloadManager
* @see #BATTERY_SERVICE
* @see android.os.BatteryManager
+ * @see #TASK_SERVICE
+ * @see android.app.task.TaskManager
*/
public abstract Object getSystemService(@ServiceName @NonNull String name);
@@ -2728,6 +2733,15 @@
public static final String USAGE_STATS_SERVICE = "usagestats";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.app.task.TaskManager} instance for managing occasional
+ * background tasks.
+ * @see #getSystemService
+ * @see android.app.task.TaskManager
+ */
+ public static final String TASK_SERVICE = "task";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
index 50462cc..217e4b7 100644
--- a/core/java/android/tv/TvInputInfo.java
+++ b/core/java/android/tv/TvInputInfo.java
@@ -39,7 +39,7 @@
public TvInputInfo(ResolveInfo service) {
mService = service;
ServiceInfo si = service.serviceInfo;
- mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name));
+ mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
}
/**
@@ -134,7 +134,7 @@
* @return the generated input id for the given {@code name}.
* @hide
*/
- public static final String generateInputIdForComponenetName(ComponentName name) {
+ public static final String generateInputIdForComponentName(ComponentName name) {
return name.flattenToShortString();
}
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index eeb738d..cb0142f 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -69,7 +69,7 @@
@Override
public void onCreate() {
super.onCreate();
- mId = TvInputInfo.generateInputIdForComponenetName(
+ mId = TvInputInfo.generateInputIdForComponentName(
new ComponentName(getPackageName(), getClass().getName()));
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 8417887..9c9a939 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -33,8 +33,6 @@
/**
* Hardware renderer that proxies the rendering to a render thread. Most calls
* are currently synchronous.
- * TODO: Make draw() async.
- * TODO: Figure out how to share the DisplayList between two threads (global lock?)
*
* The UI thread can block on the RenderThread, but RenderThread must never
* block on the UI thread.
@@ -117,7 +115,7 @@
@Override
void destroyHardwareResources(View view) {
destroyResources(view);
- // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+ nFlushCaches(mNativeProxy, GLES20Canvas.FLUSH_CACHES_LAYERS);
}
private static void destroyResources(View view) {
@@ -368,6 +366,8 @@
private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
private static native void nDestroyLayer(long nativeProxy, long layer);
+ private static native void nFlushCaches(long nativeProxy, int flushMode);
+
private static native void nFence(long nativeProxy);
private static native void nNotifyFramePending(long nativeProxy);
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 3104862..b92b983 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -253,9 +253,10 @@
ViewPropertyAnimator(View view) {
mView = view;
view.ensureTransformationInfo();
- if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
- mRTBackend = new ViewPropertyAnimatorRT(view);
- }
+ // TODO: Disabled because of b/15287046
+ //if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+ // mRTBackend = new ViewPropertyAnimatorRT(view);
+ //}
}
/**
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5bc0f62..6f256f0 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -293,6 +293,12 @@
proxy->destroyLayer(layer);
}
+static void android_view_ThreadedRenderer_flushCaches(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jint flushMode) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ proxy->flushCaches(static_cast<Caches::FlushMode>(flushMode));
+}
+
static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz,
jlong proxyPtr) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -334,6 +340,7 @@
{ "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
{ "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
{ "nDestroyLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_destroyLayer },
+ { "nFlushCaches", "(JI)V", (void*) android_view_ThreadedRenderer_flushCaches },
{ "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
#endif
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index a759a79..210ea86b 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -31,7 +31,6 @@
public FontFamily() {
mNativePtr = nCreateFamily();
- mNativePtr = nCreateFamily();
if (mNativePtr == 0) {
throw new RuntimeException();
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 64451c4..b7613fb 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -66,6 +66,9 @@
static Map<String, Typeface> sSystemFontMap;
static FontFamily[] sFallbackFonts;
+ static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
+ static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
+
/**
* @hide
*/
@@ -249,10 +252,16 @@
return fontFamily;
}
- static {
+ /*
+ * (non-Javadoc)
+ *
+ * This should only be called once, from the static class initializer block.
+ */
+ private static void init() {
// Load font config and initialize Minikin state
- String systemConfigFilename = "/system/etc/system_fonts.xml";
- String configFilename = "/system/etc/fallback_fonts.xml";
+ File systemFontConfigLocation = getSystemFontConfigLocation();
+ File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
+ File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
try {
// TODO: throws an exception non-Minikin builds, to fail early;
// remove when Minikin-only
@@ -301,7 +310,10 @@
} catch (XmlPullParserException e) {
Log.e(TAG, "XML parse exception for " + configFilename);
}
+ }
+ static {
+ init();
// Set up defaults and typefaces exposed in public API
DEFAULT = create((String) null, 0);
DEFAULT_BOLD = create((String) null, Typeface.BOLD);
@@ -315,6 +327,11 @@
create((String) null, Typeface.ITALIC),
create((String) null, Typeface.BOLD_ITALIC),
};
+
+ }
+
+ private static File getSystemFontConfigLocation() {
+ return new File("/system/etc/");
}
@Override
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b6b3428..160fbea 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -556,6 +556,13 @@
return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
}
+void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
+ if (mGlobalContext->hasContext()) {
+ requireGlContext();
+ Caches::getInstance().flush(flushMode);
+ }
+}
+
void CanvasContext::runWithGlContext(RenderTask* task) {
requireGlContext();
task->run();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a54b33e..da85d448 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -63,6 +63,8 @@
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+ void flushCaches(Caches::FlushMode flushMode);
+
void invokeFunctor(Functor* functor);
void runWithGlContext(RenderTask* task);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 2e103d8..8e772f2 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -282,6 +282,18 @@
post(task);
}
+CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) {
+ args->context->flushCaches(args->flushMode);
+ return NULL;
+}
+
+void RenderProxy::flushCaches(Caches::FlushMode flushMode) {
+ SETUP_TASK(flushCaches);
+ args->context = mContext;
+ args->flushMode = flushMode;
+ post(task);
+}
+
CREATE_BRIDGE0(fence) {
// Intentionally empty
return NULL;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 8aeb264..22d4e22 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -29,6 +29,7 @@
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
+#include "../Caches.h"
#include "DrawFrameTask.h"
namespace android {
@@ -81,6 +82,8 @@
ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
+ ANDROID_API void flushCaches(Caches::FlushMode flushMode);
+
ANDROID_API void fence();
ANDROID_API void notifyFramePending();
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bb23a36..5a3aaab 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -26,6 +26,7 @@
import java.util.Set;
/**
+ * @hide
* A class to encapsulate a collection of attributes describing information about an audio
* player or recorder.
*/
diff --git a/packages/Keyguard/res/layout/keyguard_pin_view.xml b/packages/Keyguard/res/layout/keyguard_pin_view.xml
index a804c8c..a8e330b 100644
--- a/packages/Keyguard/res/layout/keyguard_pin_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_pin_view.xml
@@ -41,28 +41,16 @@
android:layout_weight="1"
android:layoutDirection="ltr"
>
- <LinearLayout
+ <RelativeLayout
+ android:id="@+id/row0"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:orientation="horizontal"
android:layout_weight="1"
>
- <TextView android:id="@+id/pinEntry"
- android:editable="true"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
- android:singleLine="true"
- android:cursorVisible="false"
- android:background="@null"
- android:textAppearance="@style/TextAppearance.NumPadKey"
- android:imeOptions="flagForceAscii|actionDone"
- />
- <ImageButton android:id="@+id/delete_button"
+ <ImageButton android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
android:gravity="center_vertical"
android:src="@drawable/ic_input_delete"
android:clickable="true"
@@ -73,13 +61,30 @@
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/keyboardview_keycode_delete"
/>
- </LinearLayout>
- <View
- android:layout_width="wrap_content"
- android:layout_height="1dp"
- android:background="#55FFFFFF"
- />
+ <TextView android:id="@+id/pinEntry"
+ android:editable="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_toStartOf="@+id/delete_button"
+ android:layout_alignParentStart="true"
+ android:gravity="center"
+ android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+ android:singleLine="true"
+ android:cursorVisible="false"
+ android:background="@null"
+ android:textAppearance="@style/TextAppearance.NumPadKey"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_alignParentBottom="true"
+ android:background="#55FFFFFF"
+ />
+ </RelativeLayout>
<LinearLayout
+ android:id="@+id/row1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
@@ -114,6 +119,7 @@
/>
</LinearLayout>
<LinearLayout
+ android:id="@+id/row2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
@@ -148,6 +154,7 @@
/>
</LinearLayout>
<LinearLayout
+ android:id="@+id/row3"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
@@ -182,6 +189,7 @@
/>
</LinearLayout>
<LinearLayout
+ android:id="@+id/row4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index 3830df7..01d9ab3 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -161,4 +161,6 @@
<dimen name="widget_big_font_size">68dp</dimen>
<dimen name="big_font_size">120dp</dimen>
+ <!-- The y translation to apply at the start in appear animations. -->
+ <dimen name="appear_y_translation_start">24dp</dimen>
</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
new file mode 100644
index 0000000..ea896d5
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+/**
+ * A class to make nice appear transitions for views in a tabular layout.
+ */
+public class AppearAnimationUtils {
+
+ public static final long APPEAR_DURATION = 220;
+
+ private final Interpolator mLinearOutSlowIn;
+ private final float mStartTranslation;
+
+ public AppearAnimationUtils(Context ctx) {
+ mLinearOutSlowIn = AnimationUtils.loadInterpolator(
+ ctx, android.R.interpolator.linear_out_slow_in);
+ mStartTranslation =
+ ctx.getResources().getDimensionPixelOffset(R.dimen.appear_y_translation_start);
+ }
+
+ public void startAppearAnimation(View[][] views, final Runnable finishListener) {
+ long maxDelay = 0;
+ ViewPropertyAnimator maxDelayAnimator = null;
+ for (int row = 0; row < views.length; row++) {
+ View[] columns = views[row];
+ for (int col = 0; col < columns.length; col++) {
+ long delay = calculateDelay(row, col);
+ ViewPropertyAnimator animator = startAppearAnimation(columns[col], delay);
+ if (animator != null && delay > maxDelay) {
+ maxDelay = delay;
+ maxDelayAnimator = animator;
+ }
+ }
+ }
+ if (maxDelayAnimator != null) {
+ maxDelayAnimator.setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishListener.run();
+ }
+ });
+ } else {
+ finishListener.run();
+ }
+ }
+
+ private ViewPropertyAnimator startAppearAnimation(View view, long delay) {
+ if (view == null) return null;
+ view.setAlpha(0f);
+ view.setTranslationY(mStartTranslation);
+ view.animate()
+ .alpha(1f)
+ .translationY(0)
+ .setInterpolator(mLinearOutSlowIn)
+ .setDuration(APPEAR_DURATION)
+ .setStartDelay(delay)
+ .setListener(null);
+ if (view.hasOverlappingRendering()) {
+ view.animate().withLayer();
+ }
+ return view.animate();
+ }
+
+ private long calculateDelay(int row, int col) {
+ return (long) (row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20);
+ }
+
+ public TimeInterpolator getInterpolator() {
+ return mLinearOutSlowIn;
+ }
+
+ public float getStartTranslation() {
+ return mStartTranslation;
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index 4dfda91..1f3c176 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -22,6 +22,7 @@
import android.text.method.DigitsKeyListener;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.TextView.OnEditorActionListener;
/**
@@ -30,12 +31,21 @@
public class KeyguardPINView extends KeyguardAbsKeyInputView
implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+ private final AppearAnimationUtils mAppearAnimationUtils;
+ private ViewGroup mKeyguardBouncerFrame;
+ private ViewGroup mRow0;
+ private ViewGroup mRow1;
+ private ViewGroup mRow2;
+ private ViewGroup mRow3;
+ private View mDivider;
+
public KeyguardPINView(Context context) {
this(context, null);
}
public KeyguardPINView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mAppearAnimationUtils = new AppearAnimationUtils(context);
}
protected void resetState() {
@@ -56,6 +66,12 @@
protected void onFinishInflate() {
super.onFinishInflate();
+ mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame);
+ mRow0 = (ViewGroup) findViewById(R.id.row0);
+ mRow1 = (ViewGroup) findViewById(R.id.row1);
+ mRow2 = (ViewGroup) findViewById(R.id.row2);
+ mRow3 = (ViewGroup) findViewById(R.id.row3);
+ mDivider = findViewById(R.id.divider);
final View ok = findViewById(R.id.key_enter);
if (ok != null) {
ok.setOnClickListener(new View.OnClickListener() {
@@ -117,8 +133,45 @@
@Override
public void startAppearAnimation() {
- // TODO: Fancy animation.
- setAlpha(0);
- animate().alpha(1).withLayer().setDuration(200);
+ enableClipping(false);
+ setTranslationY(mAppearAnimationUtils.getStartTranslation());
+ animate()
+ .setDuration(500)
+ .setInterpolator(mAppearAnimationUtils.getInterpolator())
+ .translationY(0);
+ mAppearAnimationUtils.startAppearAnimation(new View[][] {
+ new View[] {
+ mRow0, null, null
+ },
+ new View[] {
+ findViewById(R.id.key1), findViewById(R.id.key2), findViewById(R.id.key3)
+ },
+ new View[] {
+ findViewById(R.id.key4), findViewById(R.id.key5), findViewById(R.id.key6)
+ },
+ new View[] {
+ findViewById(R.id.key7), findViewById(R.id.key8), findViewById(R.id.key9)
+ },
+ new View[] {
+ null, findViewById(R.id.key0), findViewById(R.id.key_enter)
+ },
+ new View[] {
+ null, mEcaView, null
+ }},
+ new Runnable() {
+ @Override
+ public void run() {
+ enableClipping(true);
+ }
+ });
+ }
+
+ private void enableClipping(boolean enable) {
+ mKeyguardBouncerFrame.setClipToPadding(enable);
+ mKeyguardBouncerFrame.setClipChildren(enable);
+ mRow1.setClipToPadding(enable);
+ mRow2.setClipToPadding(enable);
+ mRow3.setClipToPadding(enable);
+ setClipChildren(enable);
}
}
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 936f73b..9bf42b2 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -22,7 +22,7 @@
android:layout_height="match_parent"
android:layout_width="match_parent"
>
- <com.android.systemui.statusbar.phone.SwipeAffordanceView
+ <com.android.systemui.statusbar.AlphaImageView
android:id="@+id/camera_button"
android:layout_height="64dp"
android:layout_width="64dp"
@@ -30,10 +30,9 @@
android:tint="#ffffffff"
android:src="@drawable/ic_camera_alt_24dp"
android:scaleType="center"
- android:contentDescription="@string/accessibility_camera_button"
- systemui:swipeDirection="start"/>
+ android:contentDescription="@string/accessibility_camera_button" />
- <com.android.systemui.statusbar.phone.SwipeAffordanceView
+ <com.android.systemui.statusbar.AlphaImageView
android:id="@+id/phone_button"
android:layout_height="64dp"
android:layout_width="64dp"
@@ -41,8 +40,7 @@
android:tint="#ffffffff"
android:src="@drawable/ic_phone_24dp"
android:scaleType="center"
- android:contentDescription="@string/accessibility_phone_button"
- systemui:swipeDirection="end"/>
+ android:contentDescription="@string/accessibility_phone_button" />
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
android:id="@+id/keyguard_indication_text"
@@ -54,15 +52,13 @@
android:textColor="#ffffff"
android:textAppearance="?android:attr/textAppearanceSmall"/>
- <ImageView
+ <com.android.systemui.statusbar.AlphaImageView
android:id="@+id/lock_icon"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_lock_24dp"
android:scaleType="center"
- android:alpha="0.7"
- android:layerType="hardware"
- android:tint="#ffffffff"/>
+ android:tint="#ffffffff" />
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3549689..c453618 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -43,12 +43,6 @@
<declare-styleable name="BatteryMeterView">
<attr name="frameColor" format="color" />
</declare-styleable>
- <declare-styleable name="SwipeAffordanceView">
- <attr name="swipeDirection" format="enum">
- <enum name="start" value="0" />
- <enum name="end" value="1" />
- </attr>
- </declare-styleable>
<declare-styleable name="Clock">
<attr name="amPmStyle" format="enum">
<enum name="normal" value="0" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6e35230..610b376 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -294,6 +294,9 @@
<dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
<dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+ <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
+ <dimen name="keyguard_min_swipe_amount">75dp</dimen>
+
<!-- Volume panel dialog y offset -->
<dimen name="volume_panel_top">16dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
new file mode 100644
index 0000000..06dc4e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * An ImageView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaImageView extends ImageView {
+ public AlphaImageView(Context context) {
+ super(context);
+ }
+
+ public AlphaImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f4db625..6246e05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -48,6 +48,7 @@
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -77,11 +78,12 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.Locale;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -194,7 +196,7 @@
mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
if (provisioned != mDeviceProvisioned) {
mDeviceProvisioned = provisioned;
- updateNotificationIcons();
+ updateNotifications();
}
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
@@ -209,7 +211,7 @@
// so we just dump our cache ...
mUsersAllowingPrivateNotifications.clear();
// ... and refresh all the notifications
- updateNotificationIcons();
+ updateNotifications();
}
};
@@ -280,11 +282,12 @@
public void onListenerConnected() {
if (DEBUG) Log.d(TAG, "onListenerConnected");
final StatusBarNotification[] notifications = getActiveNotifications();
+ final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
for (StatusBarNotification sbn : notifications) {
- addNotificationInternal(sbn);
+ addNotificationInternal(sbn, currentRanking);
}
}
});
@@ -293,13 +296,14 @@
@Override
public void onNotificationPosted(final StatusBarNotification sbn) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+ final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
if (mNotificationData.findByKey(sbn.getKey()) != null) {
- updateNotificationInternal(sbn);
+ updateNotificationInternal(sbn, currentRanking);
} else {
- addNotificationInternal(sbn);
+ addNotificationInternal(sbn, currentRanking);
}
}
});
@@ -308,10 +312,24 @@
@Override
public void onNotificationRemoved(final StatusBarNotification sbn) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+ final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
- removeNotificationInternal(sbn.getKey());
+ removeNotificationInternal(sbn.getKey(), currentRanking);
+ }
+ });
+ }
+
+ @Override
+ public void onNotificationRankingUpdate() {
+ if (DEBUG) Log.d(TAG, "onRankingUpdate");
+ final Ranking currentRanking = getCurrentRanking();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mNotificationData.updateRanking(currentRanking);
+ updateNotifications();
}
});
}
@@ -1113,19 +1131,13 @@
}
}
- protected StatusBarNotification removeNotificationViews(String key) {
- NotificationData.Entry entry = mNotificationData.remove(key);
+ protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) {
+ NotificationData.Entry entry = mNotificationData.remove(key, ranking);
if (entry == null) {
Log.w(TAG, "removeNotification for unknown key: " + key);
return null;
}
- // Remove the expanded view.
- ViewGroup rowParent = (ViewGroup)entry.row.getParent();
- if (rowParent != null) rowParent.removeView(entry.row);
- updateRowStates();
- updateNotificationIcons();
- updateSpeedBump();
-
+ updateNotifications();
return entry.notification;
}
@@ -1159,35 +1171,17 @@
return entry;
}
- protected void addNotificationViews(NotificationData.Entry entry) {
+ protected void addNotificationViews(Entry entry, Ranking ranking) {
if (entry == null) {
return;
}
// Add the expanded view and icon.
- int pos = mNotificationData.add(entry);
- if (DEBUG) {
- Log.d(TAG, "addNotificationViews: added at " + pos);
- }
- updateRowStates();
- updateNotificationIcons();
- updateSpeedBump();
+ mNotificationData.add(entry, ranking);
+ updateNotifications();
}
- protected void updateSpeedBump() {
- int n = mNotificationData.size();
- int speedBumpIndex = -1;
- for (int i = n-1; i >= 0; i--) {
- NotificationData.Entry entry = mNotificationData.get(i);
- if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
- && entry.row.isBelowSpeedBump() ) {
- speedBumpIndex = n - 1 - i;
- }
- }
- mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
- }
-
- private void addNotificationViews(StatusBarNotification notification) {
- addNotificationViews(createNotificationViews(notification));
+ private void addNotificationViews(StatusBarNotification notification, Ranking ranking) {
+ addNotificationViews(createNotificationViews(notification), ranking);
}
/**
@@ -1201,17 +1195,17 @@
protected void updateRowStates() {
int maxKeyguardNotifications = getMaxKeyguardNotifications();
mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
- int n = mNotificationData.size();
+ final int N = mNotificationData.size();
int visibleNotifications = 0;
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
- for (int i = n-1; i >= 0; i--) {
+ for (int i = 0; i < N; i++) {
NotificationData.Entry entry = mNotificationData.get(i);
if (onKeyguard) {
entry.row.setExpansionDisabled(true);
} else {
entry.row.setExpansionDisabled(false);
if (!entry.row.isUserLocked()) {
- boolean top = (i == n-1);
+ boolean top = (i == 0);
entry.row.setSystemExpanded(top);
}
}
@@ -1238,6 +1232,9 @@
} else {
mKeyguardIconOverflowContainer.setVisibility(View.GONE);
}
+ // Move overflow container to last position.
+ mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
+ mStackScroller.getChildCount() - 1);
}
private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1247,46 +1244,42 @@
protected void setZenMode(int mode) {
if (!isDeviceProvisioned()) return;
mZenMode = mode;
- updateNotificationIcons();
+ updateNotifications();
}
protected abstract void haltTicker();
protected abstract void setAreThereNotifications();
- protected abstract void updateNotificationIcons();
+ protected abstract void updateNotifications();
protected abstract void tick(StatusBarNotification n, boolean firstTime);
protected abstract void updateExpandedViewPos(int expandedPosition);
protected abstract boolean shouldDisableNavbarGestures();
- protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
- return parent != null && parent.indexOfChild(entry.row) == 0;
- }
-
-
@Override
public void addNotification(StatusBarNotification notification) {
if (!USE_NOTIFICATION_LISTENER) {
- addNotificationInternal(notification);
+ addNotificationInternal(notification, null);
}
}
- public abstract void addNotificationInternal(StatusBarNotification notification);
+ public abstract void addNotificationInternal(StatusBarNotification notification,
+ Ranking ranking);
@Override
public void removeNotification(String key) {
if (!USE_NOTIFICATION_LISTENER) {
- removeNotificationInternal(key);
+ removeNotificationInternal(key, null);
}
}
- protected abstract void removeNotificationInternal(String key);
+ protected abstract void removeNotificationInternal(String key, Ranking ranking);
public void updateNotification(StatusBarNotification notification) {
if (!USE_NOTIFICATION_LISTENER) {
- updateNotificationInternal(notification);
+ updateNotificationInternal(notification, null);
}
}
- public void updateNotificationInternal(StatusBarNotification notification) {
+ public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
@@ -1358,18 +1351,12 @@
&& oldPublicContentView.getPackage().equals(publicContentView.getPackage())
&& oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
- ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
- boolean orderUnchanged =
- notification.getNotification().when == oldNotification.getNotification().when
- && notification.getScore() == oldNotification.getScore();
- // score now encompasses/supersedes isOngoing()
boolean updateTicker = notification.getNotification().tickerText != null
&& !TextUtils.equals(notification.getNotification().tickerText,
oldEntry.notification.getNotification().tickerText);
- boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
- if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
- && (orderUnchanged || isTopAnyway)) {
+ if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
+ && publicUnchanged) {
if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
oldEntry.notification = notification;
try {
@@ -1397,22 +1384,20 @@
handleNotificationError(notification, "Couldn't update icon: " + ic);
return;
}
- updateRowStates();
- updateSpeedBump();
+ mNotificationData.updateRanking(ranking);
+ updateNotifications();
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
- removeNotificationViews(notification.getKey());
- addNotificationViews(notification);
+ removeNotificationViews(notification.getKey(), ranking);
+ addNotificationViews(notification, ranking);
}
} else {
if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey());
if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
- if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
- if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
- removeNotificationViews(notification.getKey());
- addNotificationViews(notification); // will also replace the heads up
+ removeNotificationViews(notification.getKey(), ranking);
+ addNotificationViews(notification, ranking); // will also replace the heads up
final NotificationData.Entry newEntry = mNotificationData.findByKey(
notification.getKey());
final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
@@ -1559,5 +1544,12 @@
mWindowManager.removeViewImmediate(mSearchPanelView);
}
mContext.unregisterReceiver(mBroadcastReceiver);
+ if (USE_NOTIFICATION_LISTENER) {
+ try {
+ mNotificationListener.unregisterAsSystemService();
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 7d576cb..5f1325b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -18,6 +18,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
+import android.view.ViewPropertyAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -46,6 +47,8 @@
private float mMaxLengthSeconds;
private float mHighVelocityPxPerSecond;
+ private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
+
public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
mMaxLengthSeconds = maxLengthSeconds;
mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
@@ -80,18 +83,59 @@
* @param currValue the current value
* @param endValue the end value of the animator
* @param velocity the current velocity of the motion
+ */
+ public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+ float velocity) {
+ apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
* @param maxDistance the maximum distance for this interaction; the maximum animation length
* gets multiplied by the ratio between the actual distance and this value
*/
public void apply(ValueAnimator animator, float currValue, float endValue, float velocity,
float maxDistance) {
+ AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+ maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length
+ * gets multiplied by the ratio between the actual distance and this value
+ */
+ public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+ float velocity, float maxDistance) {
+ AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+ maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ private AnimatorProperties getProperties(float currValue,
+ float endValue, float velocity, float maxDistance) {
float maxLengthSeconds = (float) (mMaxLengthSeconds
* Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
float diff = Math.abs(endValue - currValue);
float velAbs = Math.abs(velocity);
float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
if (durationSeconds <= maxLengthSeconds) {
- animator.setInterpolator(mLinearOutSlowIn);
+ mAnimatorProperties.interpolator = mLinearOutSlowIn;
} else if (velAbs >= mMinVelocityPxPerSecond) {
// Cross fade between fast-out-slow-in and linear interpolator with current velocity.
@@ -100,14 +144,15 @@
= new VelocityInterpolator(durationSeconds, velAbs, diff);
InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
- animator.setInterpolator(superInterpolator);
+ mAnimatorProperties.interpolator = superInterpolator;
} else {
// Just use a normal interpolator which doesn't take the velocity into account.
durationSeconds = maxLengthSeconds;
- animator.setInterpolator(mFastOutSlowIn);
+ mAnimatorProperties.interpolator = mFastOutSlowIn;
}
- animator.setDuration((long) (durationSeconds * 1000));
+ mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+ return mAnimatorProperties;
}
/**
@@ -124,6 +169,34 @@
*/
public void applyDismissing(ValueAnimator animator, float currValue, float endValue,
float velocity, float maxDistance) {
+ AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+ maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion for the case when the animation is making something
+ * disappear.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length
+ * gets multiplied by the ratio between the actual distance and this value
+ */
+ public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
+ float velocity, float maxDistance) {
+ AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+ maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ private AnimatorProperties getDismissingProperties(float currValue, float endValue,
+ float velocity, float maxDistance) {
float maxLengthSeconds = (float) (mMaxLengthSeconds
* Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
float diff = Math.abs(endValue - currValue);
@@ -135,7 +208,7 @@
Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, 1, y2);
float durationSeconds = startGradient * diff / velAbs;
if (durationSeconds <= maxLengthSeconds) {
- animator.setInterpolator(mLinearOutFasterIn);
+ mAnimatorProperties.interpolator = mLinearOutFasterIn;
} else if (velAbs >= mMinVelocityPxPerSecond) {
// Cross fade between linear-out-faster-in and linear interpolator with current
@@ -145,14 +218,15 @@
= new VelocityInterpolator(durationSeconds, velAbs, diff);
InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
velocityInterpolator, mLinearOutFasterIn, mLinearOutSlowIn);
- animator.setInterpolator(superInterpolator);
+ mAnimatorProperties.interpolator = superInterpolator;
} else {
// Just use a normal interpolator which doesn't take the velocity into account.
durationSeconds = maxLengthSeconds;
- animator.setInterpolator(mFastOutLinearIn);
+ mAnimatorProperties.interpolator = mFastOutLinearIn;
}
- animator.setDuration((long) (durationSeconds * 1000));
+ mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+ return mAnimatorProperties;
}
/**
@@ -221,4 +295,10 @@
return time * mVelocity / mDiff;
}
}
+
+ private static class AnimatorProperties {
+ Interpolator interpolator;
+ long duration;
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 0555879..993bb92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -50,7 +50,7 @@
for (int i = 0; i < n; i++) {
final StatusBarNotification sbn = mIntercepted.valueAt(i);
sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
- mBar.addNotificationInternal(sbn);
+ mBar.addNotificationInternal(sbn, null);
}
mIntercepted.clear();
updateSyntheticNotification();
@@ -88,7 +88,7 @@
private void updateSyntheticNotification() {
if (mIntercepted.isEmpty()) {
if (mSynKey != null) {
- mBar.removeNotificationInternal(mSynKey);
+ mBar.removeNotificationInternal(mSynKey, null);
mSynKey = null;
}
return;
@@ -107,9 +107,9 @@
mBar.getCurrentUserHandle());
if (mSynKey == null) {
mSynKey = sbn.getKey();
- mBar.addNotificationInternal(sbn);
+ mBar.addNotificationInternal(sbn, null);
} else {
- mBar.updateNotificationInternal(sbn);
+ mBar.updateNotificationInternal(sbn, null);
}
final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
entry.row.setOnClickListener(mSynClickListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 5696246..d829ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,15 +16,19 @@
package com.android.systemui.statusbar;
+import android.app.Notification;
+import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.view.View;
-import android.widget.ImageView;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
/**
* The list of currently displaying notifications.
+ *
+ * TODO: Rename to NotificationList.
*/
public class NotificationData {
public static final class Entry {
@@ -34,7 +38,6 @@
public ExpandableNotificationRow row; // the outer expanded view
public View expanded; // the inflated RemoteViews
public View expandedPublic; // for insecure lockscreens
- public ImageView largeIcon;
public View expandedBig;
private boolean interruption;
public Entry() {}
@@ -64,18 +67,23 @@
}
private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
- private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
- // sort first by score, then by when
+ private Ranking mRanking;
+ private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
+ @Override
public int compare(Entry a, Entry b) {
+ if (mRanking != null) {
+ return mRanking.getRank(a.key) - mRanking.getRank(b.key);
+ }
+
final StatusBarNotification na = a.notification;
final StatusBarNotification nb = b.notification;
- int d = na.getScore() - nb.getScore();
+ int d = nb.getScore() - na.getScore();
if (a.interruption != b.interruption) {
- return a.interruption ? 1 : -1;
+ return a.interruption ? -1 : 1;
} else if (d != 0) {
return d;
} else {
- return (int) (na.getNotification().when - nb.getNotification().when);
+ return (int) (nb.getNotification().when - na.getNotification().when);
}
}
};
@@ -97,26 +105,47 @@
return null;
}
- public int add(Entry entry) {
- int i;
- int N = mEntries.size();
- for (i = 0; i < N; i++) {
- if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
- break;
- }
- }
- mEntries.add(i, entry);
- return i;
+ public void add(Entry entry, Ranking ranking) {
+ mEntries.add(entry);
+ updateRankingAndSort(ranking);
}
- public Entry remove(String key) {
+ public Entry remove(String key, Ranking ranking) {
Entry e = findByKey(key);
- if (e != null) {
- mEntries.remove(e);
+ if (e == null) {
+ return null;
}
+ mEntries.remove(e);
+ updateRankingAndSort(ranking);
return e;
}
+ public void updateRanking(Ranking ranking) {
+ updateRankingAndSort(ranking);
+ }
+
+ public boolean isAmbient(String key) {
+ // TODO: Remove when switching to NotificationListener.
+ if (mRanking == null) {
+ for (Entry entry : mEntries) {
+ if (key.equals(entry.key)) {
+ return entry.notification.getNotification().priority ==
+ Notification.PRIORITY_MIN;
+ }
+ }
+ } else {
+ return mRanking.isAmbient(key);
+ }
+ return false;
+ }
+
+ private void updateRankingAndSort(Ranking ranking) {
+ if (ranking != null) {
+ mRanking = ranking;
+ }
+ Collections.sort(mEntries, mRankingComparator);
+ }
+
/**
* Return whether there are any visible items (i.e. items without an error).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 714ad06..994b329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
@@ -33,26 +32,23 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
-
import com.android.systemui.R;
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
* text.
*/
-public class KeyguardBottomAreaView extends FrameLayout
- implements SwipeAffordanceView.AffordanceListener,
+public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
UnlockMethodCache.OnUnlockMethodChangedListener {
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
- private SwipeAffordanceView mCameraButton;
- private SwipeAffordanceView mPhoneButton;
+ private ImageView mCameraImageView;
+ private ImageView mPhoneImageView;
private ImageView mLockIcon;
- private PowerManager mPowerManager;
private ActivityStarter mActivityStarter;
private UnlockMethodCache mUnlockMethodCache;
@@ -76,11 +72,9 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mCameraButton = (SwipeAffordanceView) findViewById(R.id.camera_button);
- mPhoneButton = (SwipeAffordanceView) findViewById(R.id.phone_button);
+ mCameraImageView = (ImageView) findViewById(R.id.camera_button);
+ mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
mLockIcon = (ImageView) findViewById(R.id.lock_icon);
- mCameraButton.setAffordanceListener(this);
- mPhoneButton.setAffordanceListener(this);
watchForDevicePolicyChanges();
watchForAccessibilityChanges();
updateCameraVisibility();
@@ -88,7 +82,6 @@
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
updateTrust();
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
public void setActivityStarter(ActivityStarter activityStarter) {
@@ -97,12 +90,12 @@
private void updateCameraVisibility() {
boolean visible = !isCameraDisabledByDpm();
- mCameraButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
private void updatePhoneVisibility() {
boolean visible = isPhoneVisible();
- mPhoneButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
private boolean isPhoneVisible() {
@@ -162,33 +155,31 @@
}
private void enableAccessibility(boolean touchExplorationEnabled) {
- mCameraButton.enableAccessibility(touchExplorationEnabled);
- mPhoneButton.enableAccessibility(touchExplorationEnabled);
+ mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+ mCameraImageView.setClickable(touchExplorationEnabled);
+ mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+ mPhoneImageView.setClickable(touchExplorationEnabled);
}
- private void launchCamera() {
+ @Override
+ public void onClick(View v) {
+ if (v == mCameraImageView) {
+ launchCamera();
+ } else if (v == mPhoneImageView) {
+ launchPhone();
+ }
+ }
+
+ public void launchCamera() {
mContext.startActivityAsUser(
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
UserHandle.CURRENT);
}
- private void launchPhone() {
+ public void launchPhone() {
mActivityStarter.startActivity(PHONE_INTENT);
}
- @Override
- public void onUserActivity(long when) {
- mPowerManager.userActivity(when, false);
- }
-
- @Override
- public void onActionPerformed(SwipeAffordanceView view) {
- if (view == mCameraButton) {
- launchCamera();
- } else if (view == mPhoneButton) {
- launchPhone();
- }
- }
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
@@ -208,6 +199,18 @@
mLockIcon.setImageResource(iconRes);
}
+ public ImageView getPhoneImageView() {
+ return mPhoneImageView;
+ }
+
+ public ImageView getCameraImageView() {
+ return mCameraImageView;
+ }
+
+ public ImageView getLockIcon() {
+ return mLockIcon;
+ }
+
@Override
public void onMethodSecureChanged(boolean methodSecure) {
updateTrust();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
new file mode 100644
index 0000000..b4f4865
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.os.PowerManager;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A touch handler of the Keyguard which is responsible for swiping the content left or right.
+ */
+public class KeyguardPageSwipeHelper {
+
+ private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
+ private static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.7f;
+ private final Context mContext;
+
+ private FlingAnimationUtils mFlingAnimationUtils;
+ private Callback mCallback;
+ private int mTrackingPointer;
+ private VelocityTracker mVelocityTracker;
+ private boolean mSwipingInProgress;
+ private float mInitialTouchX;
+ private float mInitialTouchY;
+ private float mTranslation;
+ private float mTranslationOnDown;
+ private int mTouchSlop;
+ private int mMinTranslationAmount;
+ private int mMinFlingVelocity;
+ private PowerManager mPowerManager;
+ private final View mLeftIcon;
+ private final View mCenterIcon;
+ private final View mRightIcon;
+ private Interpolator mFastOutSlowIn;
+ private Animator mSwipeAnimator;
+ private boolean mCallbackCalled;
+
+ KeyguardPageSwipeHelper(Callback callback, Context context) {
+ mContext = context;
+ mCallback = callback;
+ mLeftIcon = mCallback.getLeftIcon();
+ mCenterIcon = mCallback.getCenterIcon();
+ mRightIcon = mCallback.getRightIcon();
+ updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ initDimens();
+ }
+
+ private void initDimens() {
+ final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_min_swipe_amount);
+ mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
+ mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_slow_in);
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float y = event.getY(pointerIndex);
+ final float x = event.getX(pointerIndex);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mSwipingInProgress) {
+ cancelAnimations();
+ }
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mTranslationOnDown = mTranslation;
+ initVelocityTracker();
+ trackMovement(event);
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ final float newY = event.getY(newIndex);
+ final float newX = event.getX(newIndex);
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialTouchY = newY;
+ mInitialTouchX = newX;
+ mTranslationOnDown = mTranslation;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float w = x - mInitialTouchX;
+ trackMovement(event);
+ if (((leftSwipePossible() && w > mTouchSlop)
+ || (rightSwipePossible() && w < -mTouchSlop))
+ && Math.abs(w) > Math.abs(y - mInitialTouchY)
+ && !mSwipingInProgress) {
+ cancelAnimations();
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mTranslationOnDown = mTranslation;
+ mSwipingInProgress = true;
+ }
+ if (mSwipingInProgress) {
+ setTranslation(mTranslationOnDown + x - mInitialTouchX, false);
+ onUserActivity(event.getEventTime());
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mTrackingPointer = -1;
+ trackMovement(event);
+ if (mSwipingInProgress) {
+ flingWithCurrentVelocity();
+ }
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ break;
+ }
+ return true;
+ }
+
+ private boolean rightSwipePossible() {
+ return mRightIcon.getVisibility() == View.VISIBLE;
+ }
+
+ private boolean leftSwipePossible() {
+ return mLeftIcon.getVisibility() == View.VISIBLE;
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ private void onUserActivity(long when) {
+ mPowerManager.userActivity(when, false);
+ }
+
+ private void cancelAnimations() {
+ ArrayList<View> targetViews = mCallback.getTranslationViews();
+ for (View target : targetViews) {
+ target.animate().cancel();
+ }
+ View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+ targetView.animate().cancel();
+ if (mSwipeAnimator != null) {
+ mSwipeAnimator.removeAllListeners();
+ mSwipeAnimator.cancel();
+ hideInactiveIcons(true);
+ }
+ }
+
+ private void flingWithCurrentVelocity() {
+ float vel = getCurrentVelocity();
+
+ // We snap back if the current translation is not far enough
+ boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount;
+
+ // or if the velocity is in the opposite direction.
+ boolean velIsInWrongDirection = vel * mTranslation < 0;
+ snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
+ vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
+ fling(vel, snapBack);
+ }
+
+ private void fling(float vel, final boolean snapBack) {
+ float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
+ target = snapBack ? 0 : target;
+
+ // translation Animation
+ startTranslationAnimations(vel, target);
+
+ // animate left / right icon
+ startIconAnimation(vel, snapBack, target);
+
+ ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
+ mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mTranslation = (float) animation.getAnimatedValue();
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeAnimator = null;
+ mSwipingInProgress = false;
+ if (!snapBack && !mCallbackCalled) {
+
+ // ensure that the callback is called eventually
+ mCallback.onAnimationToSideStarted(mTranslation < 0);
+ mCallbackCalled = true;
+ }
+ }
+ });
+ if (!snapBack) {
+ mCallbackCalled = false;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ int frameNumber;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ if (frameNumber == 2 && !mCallbackCalled) {
+
+ // we have to wait for the second frame for this call,
+ // until the render thread has definitely kicked in, to avoid a lag.
+ mCallback.onAnimationToSideStarted(mTranslation < 0);
+ mCallbackCalled = true;
+ }
+ frameNumber++;
+ }
+ });
+ } else {
+ showAllIcons(true);
+ }
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ private void startTranslationAnimations(float vel, float target) {
+ ArrayList<View> targetViews = mCallback.getTranslationViews();
+ for (View targetView : targetViews) {
+ ViewPropertyAnimator animator = targetView.animate();
+ mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+ animator.translationX(target);
+ }
+ }
+
+ private void startIconAnimation(float vel, boolean snapBack, float target) {
+ float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT;
+ float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f;
+ View targetView = mTranslation > 0
+ ? mLeftIcon
+ : mRightIcon;
+ if (targetView.getVisibility() == View.VISIBLE) {
+ ViewPropertyAnimator iconAnimator = targetView.animate();
+ mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel);
+ iconAnimator.scaleX(scale);
+ iconAnimator.scaleY(scale);
+ iconAnimator.alpha(alpha);
+ }
+ }
+
+ private void setTranslation(float translation, boolean isReset) {
+ translation = rightSwipePossible() ? translation : Math.max(0, translation);
+ translation = leftSwipePossible() ? translation : Math.min(0, translation);
+ if (translation != mTranslation) {
+ ArrayList<View> translatedViews = mCallback.getTranslationViews();
+ for (View view : translatedViews) {
+ view.setTranslationX(translation);
+ }
+ if (translation == 0.0f) {
+ boolean animate = !isReset;
+ showAllIcons(animate);
+ } else {
+ View targetView = translation > 0 ? mLeftIcon : mRightIcon;
+ float progress = Math.abs(translation) / mCallback.getPageWidth();
+ progress = Math.min(progress, 1.0f);
+ float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress;
+ float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT;
+ updateIcon(targetView, scale, alpha, false);
+ View otherView = translation < 0 ? mLeftIcon : mRightIcon;
+ if (mTranslation * translation <= 0) {
+ // The sign of the translation has changed so we need to hide the other icons
+ updateIcon(otherView, 0, 0, true);
+ updateIcon(mCenterIcon, 0, 0, true);
+ }
+ }
+ mTranslation = translation;
+ }
+ }
+
+ private void showAllIcons(boolean animate) {
+ float scale = 1.0f;
+ float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
+ updateIcon(mRightIcon, scale, alpha, animate);
+ updateIcon(mCenterIcon, scale, alpha, animate);
+ updateIcon(mLeftIcon, scale, alpha, animate);
+ }
+
+ private void hideInactiveIcons(boolean animate){
+ View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
+ updateIcon(otherView, 0, 0, animate);
+ updateIcon(mCenterIcon, 0, 0, animate);
+ }
+
+ private void updateIcon(View view, float scale, float alpha, boolean animate) {
+ if (view.getVisibility() != View.VISIBLE) {
+ return;
+ }
+ if (!animate) {
+ view.setAlpha(alpha);
+ view.setScaleX(scale);
+ view.setScaleY(scale);
+ // TODO: remove this invalidate once the property setters invalidate it properly
+ view.invalidate();
+ } else {
+ if (view.getAlpha() != alpha || view.getScaleX() != scale) {
+ view.animate()
+ .setInterpolator(mFastOutSlowIn)
+ .alpha(alpha)
+ .scaleX(scale)
+ .scaleY(scale);
+ }
+ }
+ }
+
+ private void trackMovement(MotionEvent event) {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(event);
+ }
+ }
+
+ private void initVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ }
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+
+ private float getCurrentVelocity() {
+ if (mVelocityTracker == null) {
+ return 0;
+ }
+ mVelocityTracker.computeCurrentVelocity(1000);
+ return mVelocityTracker.getXVelocity();
+ }
+
+ public void onConfigurationChanged() {
+ initDimens();
+ }
+
+ public void reset() {
+ setTranslation(0.0f, true);
+ mSwipingInProgress = false;
+ }
+
+ public boolean isSwipingInProgress() {
+ return mSwipingInProgress;
+ }
+
+ public interface Callback {
+
+ /**
+ * Notifies the callback when an animation to a side page was started.
+ *
+ * @param rightPage Is the page animated to the right page?
+ */
+ void onAnimationToSideStarted(boolean rightPage);
+
+ float getPageWidth();
+
+ ArrayList<View> getTranslationViews();
+
+ View getLeftIcon();
+
+ View getCenterIcon();
+
+ View getRightIcon();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 52688df..838e5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -40,10 +41,13 @@
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
+import java.util.ArrayList;
+
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
- View.OnClickListener {
+ View.OnClickListener, KeyguardPageSwipeHelper.Callback {
+ private KeyguardPageSwipeHelper mPageSwiper;
PhoneStatusBar mStatusBar;
private StatusBarHeaderView mHeader;
private View mQsContainer;
@@ -58,7 +62,7 @@
private int mTrackingPointer;
private VelocityTracker mVelocityTracker;
- private boolean mTracking;
+ private boolean mQsTracking;
/**
* Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
@@ -92,6 +96,11 @@
new KeyguardClockPositionAlgorithm();
private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
new KeyguardClockPositionAlgorithm.Result();
+ private boolean mIsSwipedHorizontally;
+ private boolean mIsExpanding;
+ private KeyguardBottomAreaView mKeyguardBottomArea;
+ private boolean mBlockTouches;
+ private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -129,6 +138,10 @@
mNotificationStackScroller.setOnHeightChangedListener(this);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.fast_out_slow_in);
+ mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
+ mSwipeTranslationViews.add(mNotificationStackScroller);
+ mSwipeTranslationViews.add(mKeyguardStatusView);
+ mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
}
@Override
@@ -247,6 +260,12 @@
mQsExpansionEnabled = qsExpansionEnabled;
}
+ public void resetViews() {
+ mBlockTouches = false;
+ mPageSwiper.reset();
+ closeQs();
+ }
+
public void closeQs() {
cancelAnimation();
setQsExpansion(mQsMinExpansionHeight);
@@ -263,9 +282,7 @@
public void fling(float vel, boolean always) {
GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
if (gr != null) {
- gr.tag(
- "fling " + ((vel > 0) ? "open" : "closed"),
- "notifications,v=" + vel);
+ gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
}
super.fling(vel, always);
}
@@ -283,6 +300,9 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mBlockTouches) {
+ return false;
+ }
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
@@ -298,7 +318,7 @@
mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
- if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+ if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
@@ -316,7 +336,7 @@
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
trackMovement(event);
- if (mTracking) {
+ if (mQsTracking) {
// Already tracking because onOverscrolled was called. We need to update here
// so we don't stop for a frame until the next touch event gets handled in
@@ -327,12 +347,12 @@
return true;
}
if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
- && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
+ && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = y;
mInitialTouchX = x;
- mTracking = true;
+ mQsTracking = true;
mIntercepting = false;
return true;
}
@@ -341,9 +361,9 @@
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
trackMovement(event);
- if (mTracking) {
- flingWithCurrentVelocity();
- mTracking = false;
+ if (mQsTracking) {
+ flingQsWithCurrentVelocity();
+ mQsTracking = false;
}
mIntercepting = false;
break;
@@ -362,7 +382,7 @@
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
- private void flingWithCurrentVelocity() {
+ private void flingQsWithCurrentVelocity() {
float vel = getCurrentVelocity();
// TODO: Better logic whether we should expand or not.
@@ -371,65 +391,83 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (mBlockTouches) {
+ return false;
+ }
// TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
// implementation.
- if (mTracking) {
- int pointerIndex = event.findPointerIndex(mTrackingPointer);
- if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
+ if (!mIsExpanding && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) {
+ mPageSwiper.onTouchEvent(event);
+ if (mPageSwiper.isSwipingInProgress()) {
+ return true;
}
- final float y = event.getY(pointerIndex);
- final float x = event.getX(pointerIndex);
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mTracking = true;
- mInitialTouchY = y;
- mInitialTouchX = x;
- onQsExpansionStarted();
- mInitialHeightOnTouch = mQsExpansionHeight;
- initVelocityTracker();
- trackMovement(event);
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- final int upPointer = event.getPointerId(event.getActionIndex());
- if (mTrackingPointer == upPointer) {
- // gesture is ongoing, find a new pointer to track
- final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
- final float newY = event.getY(newIndex);
- final float newX = event.getX(newIndex);
- mTrackingPointer = event.getPointerId(newIndex);
- mInitialHeightOnTouch = mQsExpansionHeight;
- mInitialTouchY = newY;
- mInitialTouchX = newX;
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- final float h = y - mInitialTouchY;
- setQsExpansion(h + mInitialHeightOnTouch);
- trackMovement(event);
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mTracking = false;
- mTrackingPointer = -1;
- trackMovement(event);
- flingWithCurrentVelocity();
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- break;
- }
- return true;
+ }
+ if (mQsTracking || mQsExpanded) {
+ return onQsTouch(event);
}
- // Consume touch events when QS are expanded.
- return mQsExpanded || super.onTouchEvent(event);
+ super.onTouchEvent(event);
+ return true;
+ }
+
+ @Override
+ protected boolean hasConflictingGestures() {
+ return mStatusBar.getBarState() != StatusBarState.SHADE;
+ }
+
+ private boolean onQsTouch(MotionEvent event) {
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float y = event.getY(pointerIndex);
+ final float x = event.getX(pointerIndex);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mQsTracking = true;
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ onQsExpansionStarted();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ initVelocityTracker();
+ trackMovement(event);
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ final float newY = event.getY(newIndex);
+ final float newX = event.getX(newIndex);
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = newY;
+ mInitialTouchX = newX;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float h = y - mInitialTouchY;
+ setQsExpansion(h + mInitialHeightOnTouch);
+ trackMovement(event);
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mQsTracking = false;
+ mTrackingPointer = -1;
+ trackMovement(event);
+ flingQsWithCurrentVelocity();
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ break;
+ }
+ return true;
}
@Override
@@ -439,7 +477,7 @@
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = mLastTouchY;
mInitialTouchX = mLastTouchX;
- mTracking = true;
+ mQsTracking = true;
}
}
@@ -569,7 +607,7 @@
/**
* @return Whether we should intercept a gesture to open Quick Settings.
*/
- private boolean shouldIntercept(float x, float y, float yDiff) {
+ private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
if (!mQsExpansionEnabled) {
return false;
}
@@ -647,12 +685,14 @@
protected void onExpandingStarted() {
super.onExpandingStarted();
mNotificationStackScroller.onExpansionStarted();
+ mIsExpanding = true;
}
@Override
protected void onExpandingFinished() {
super.onExpandingFinished();
mNotificationStackScroller.onExpansionStopped();
+ mIsExpanding = false;
}
@Override
@@ -686,6 +726,12 @@
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mPageSwiper.onConfigurationChanged();
+ }
+
+ @Override
public void onClick(View v) {
if (v == mHeader.getBackgroundView()) {
onQsExpansionStarted();
@@ -696,4 +742,40 @@
}
}
}
+
+ @Override
+ public void onAnimationToSideStarted(boolean rightPage) {
+ if (rightPage) {
+ mKeyguardBottomArea.launchCamera();
+ } else {
+ mKeyguardBottomArea.launchPhone();
+ }
+ mBlockTouches = true;
+ }
+
+
+ @Override
+ public float getPageWidth() {
+ return getWidth();
+ }
+
+ @Override
+ public ArrayList<View> getTranslationViews() {
+ return mSwipeTranslationViews;
+ }
+
+ @Override
+ public View getLeftIcon() {
+ return mKeyguardBottomArea.getPhoneImageView();
+ }
+
+ @Override
+ public View getCenterIcon() {
+ return mKeyguardBottomArea.getLockIcon();
+ }
+
+ @Override
+ public View getRightIcon() {
+ return mKeyguardBottomArea.getCameraImageView();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 7500c10..f6db8a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -35,7 +35,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public class PanelView extends FrameLayout {
+public abstract class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
protected float mOverExpansion;
@@ -130,21 +130,24 @@
final float y = event.getY(pointerIndex);
final float x = event.getX(pointerIndex);
+ boolean waitForTouchSlop = hasConflictingGestures();
+
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- mTracking = true;
mInitialTouchY = y;
mInitialTouchX = x;
+ mInitialOffsetOnTouch = mExpandedHeight;
if (mVelocityTracker == null) {
initVelocityTracker();
}
trackMovement(event);
- if (mHeightAnimator != null) {
- mHeightAnimator.cancel(); // end any outstanding animations
+ if (!waitForTouchSlop || mHeightAnimator != null) {
+ if (mHeightAnimator != null) {
+ mHeightAnimator.cancel(); // end any outstanding animations
+ }
+ onTrackingStarted();
}
- onTrackingStarted();
- mInitialOffsetOnTouch = mExpandedHeight;
if (mExpandedHeight == 0) {
mJustPeeked = true;
runPeekAnimation();
@@ -166,15 +169,27 @@
break;
case MotionEvent.ACTION_MOVE:
- final float h = y - mInitialTouchY + mInitialOffsetOnTouch;
- if (h > mPeekHeight) {
+ float h = y - mInitialTouchY;
+ if (waitForTouchSlop && !mTracking && Math.abs(h) > mTouchSlop
+ && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+ mInitialOffsetOnTouch = mExpandedHeight;
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ if (mHeightAnimator != null) {
+ mHeightAnimator.cancel(); // end any outstanding animations
+ }
+ onTrackingStarted();
+ h = 0;
+ }
+ final float newHeight = h + mInitialOffsetOnTouch;
+ if (newHeight > mPeekHeight) {
if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
mPeekAnimator.cancel();
}
mJustPeeked = false;
}
- if (!mJustPeeked) {
- setExpandedHeightInternal(h);
+ if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
+ setExpandedHeightInternal(newHeight);
mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
}
@@ -183,7 +198,6 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mTracking = false;
mTrackingPointer = -1;
trackMovement(event);
boolean expand = flingWithCurrentVelocity();
@@ -194,14 +208,18 @@
}
break;
}
- return true;
+ return !waitForTouchSlop || mTracking;
}
+ protected abstract boolean hasConflictingGestures();
+
protected void onTrackingStopped(boolean expand) {
+ mTracking = false;
mBar.onTrackingStopped(PanelView.this, expand);
}
protected void onTrackingStarted() {
+ mTracking = true;
mBar.onTrackingStarted(PanelView.this);
onExpandingStarted();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 15ad709..5dcd61c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -64,6 +64,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.DisplayMetrics;
@@ -1037,13 +1038,15 @@
}
@Override
- public void addNotificationInternal(StatusBarNotification notification) {
+ public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
Entry shadeEntry = createNotificationViews(notification);
if (shadeEntry == null) {
return;
}
if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
+ // Forward the ranking so we can sort the new notification.
+ mNotificationData.updateRanking(ranking);
return;
}
if (mUseHeadsUp && shouldInterrupt(notification)) {
@@ -1083,7 +1086,7 @@
tick(notification, true);
}
}
- addNotificationViews(shadeEntry);
+ addNotificationViews(shadeEntry, ranking);
// Recalculate the position of the sliding windows and the titles.
setAreThereNotifications();
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
@@ -1099,14 +1102,14 @@
}
@Override
- public void updateNotification(StatusBarNotification notification) {
- super.updateNotification(notification);
+ public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+ super.updateNotificationInternal(notification, ranking);
mIntercepted.update(notification);
}
@Override
- public void removeNotificationInternal(String key) {
- StatusBarNotification old = removeNotificationViews(key);
+ public void removeNotificationInternal(String key, Ranking ranking) {
+ StatusBarNotification old = removeNotificationViews(key, ranking);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
if (old != null) {
@@ -1143,7 +1146,7 @@
R.integer.config_show_search_delay);
}
- private void loadNotificationShade() {
+ private void updateNotificationShade() {
if (mStackScroller == null) return;
int N = mNotificationData.size();
@@ -1153,7 +1156,7 @@
final boolean provisioned = isDeviceProvisioned();
// If the device hasn't been through Setup, we only show system notifications
for (int i=0; i<N; i++) {
- Entry ent = mNotificationData.get(N-i-1);
+ Entry ent = mNotificationData.get(i);
if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
// TODO How do we want to badge notifcations from profiles.
@@ -1184,26 +1187,75 @@
for (int i=0; i<toShow.size(); i++) {
View v = toShow.get(i);
if (v.getParent() == null) {
- mStackScroller.addView(v, i);
+ mStackScroller.addView(v);
}
}
+ // So after all this work notifications still aren't sorted correctly.
+ // Let's do that now by advancing through toShow and mStackScroller in
+ // lock-step, making sure mStackScroller matches what we see in toShow.
+ int j = 0;
+ for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+ View child = mStackScroller.getChildAt(i);
+ if (!(child instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ if (child == toShow.get(j)) {
+ // Everything is well, advance both lists.
+ j++;
+ continue;
+ }
+
+ // Oops, wrong notification at this position. Put the right one
+ // here and advance both lists.
+ mStackScroller.changeViewPosition(toShow.get(j), i);
+ j++;
+ }
+ updateRowStates();
+ updateSpeedbump();
mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
}
+ private void updateSpeedbump() {
+ int speedbumpIndex = -1;
+ int currentIndex = 0;
+ for (int i = 0; i < mNotificationData.size(); i++) {
+ Entry entry = mNotificationData.get(i);
+ if (entry.row.getParent() == null) {
+ // This view isn't even added, so the stack scroller doesn't
+ // know about it. Ignore completely.
+ continue;
+ }
+ if (entry.row.getVisibility() != View.GONE &&
+ mNotificationData.isAmbient(entry.key)) {
+ speedbumpIndex = currentIndex;
+ break;
+ }
+ currentIndex++;
+ }
+ mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
+ }
+
@Override
- protected void updateNotificationIcons() {
+ protected void updateNotifications() {
+ // TODO: Move this into updateNotificationIcons()?
if (mNotificationIcons == null) return;
- loadNotificationShade();
+ updateNotificationShade();
+ updateNotificationIcons();
+ }
+ private void updateNotificationIcons() {
final LinearLayout.LayoutParams params
= new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
int N = mNotificationData.size();
if (DEBUG) {
- Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
+ Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" +
+ mNotificationIcons);
}
ArrayList<View> toShow = new ArrayList<View>();
@@ -1211,7 +1263,7 @@
final boolean provisioned = isDeviceProvisioned();
// If the device hasn't been through Setup, we only show system notifications
for (int i=0; i<N; i++) {
- Entry ent = mNotificationData.get(N-i-1);
+ Entry ent = mNotificationData.get(i);
if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
|| showNotificationEvenIfUnprovisioned(ent.notification))) continue;
if (!notificationIsForCurrentProfiles(ent.notification)) continue;
@@ -2388,7 +2440,7 @@
public void userSwitched(int newUserId) {
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
animateCollapsePanels();
- updateNotificationIcons();
+ updateNotifications();
resetUserSetupObserver();
}
@@ -2774,7 +2826,7 @@
mKeyguardStatusView.setVisibility(View.VISIBLE);
mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
- mNotificationPanel.closeQs();
+ mNotificationPanel.resetViews();
} else {
mKeyguardStatusView.setVisibility(View.GONE);
mKeyguardIndicationTextView.setVisibility(View.GONE);
@@ -2793,10 +2845,8 @@
updateStackScrollerState();
updatePublicMode();
- updateRowStates();
- updateSpeedBump();
+ updateNotifications();
checkBarModes();
- updateNotificationIcons();
updateCarrierLabelVisibility(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
deleted file mode 100644
index 049c5fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.Button;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.KeyButtonView;
-
-/**
- * A swipeable button for affordances on the lockscreen. This is used for the camera and phone
- * affordance.
- */
-public class SwipeAffordanceView extends KeyButtonView {
-
- private static final int SWIPE_DIRECTION_START = 0;
- private static final int SWIPE_DIRECTION_END = 1;
-
- private static final int SWIPE_DIRECTION_LEFT = 0;
- private static final int SWIPE_DIRECTION_RIGHT = 1;
-
- private AffordanceListener mListener;
- private int mScaledTouchSlop;
- private float mDragDistance;
- private int mResolvedSwipeDirection;
- private int mSwipeDirection;
-
- public SwipeAffordanceView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SwipeAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- TypedArray a = context.getTheme().obtainStyledAttributes(
- attrs,
- R.styleable.SwipeAffordanceView,
- 0, 0);
- try {
- mSwipeDirection = a.getInt(R.styleable.SwipeAffordanceView_swipeDirection, 0);
- } finally {
- a.recycle();
- }
- }
-
- @Override
- public void onRtlPropertiesChanged(int layoutDirection) {
- super.onRtlPropertiesChanged(layoutDirection);
- if (!isLayoutRtl()) {
- mResolvedSwipeDirection = mSwipeDirection;
- } else {
- mResolvedSwipeDirection = mSwipeDirection == SWIPE_DIRECTION_START
- ? SWIPE_DIRECTION_RIGHT
- : SWIPE_DIRECTION_LEFT;
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mDragDistance = getResources().getDimension(R.dimen.affordance_drag_distance);
- mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- }
-
- public void enableAccessibility(boolean touchExplorationEnabled) {
-
- // Add a touch handler or accessibility click listener for camera button.
- if (touchExplorationEnabled) {
- setOnTouchListener(null);
- setOnClickListener(mClickListener);
- } else {
- setOnTouchListener(mTouchListener);
- setOnClickListener(null);
- }
- }
-
- public void setAffordanceListener(AffordanceListener listener) {
- mListener = listener;
- }
-
- private void onActionPerformed() {
- if (mListener != null) {
- mListener.onActionPerformed(this);
- }
- }
-
- private void onUserActivity(long when) {
- if (mListener != null) {
- mListener.onUserActivity(when);
- }
- }
-
- private final OnClickListener mClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- onActionPerformed();
- }
- };
-
- private final OnTouchListener mTouchListener = new OnTouchListener() {
- private float mStartX;
- private boolean mTouchSlopReached;
- private boolean mSkipCancelAnimation;
-
- @Override
- public boolean onTouch(final View view, MotionEvent event) {
- float realX = event.getRawX();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mStartX = realX;
- mTouchSlopReached = false;
- mSkipCancelAnimation = false;
- break;
- case MotionEvent.ACTION_MOVE:
- if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? realX > mStartX
- : realX < mStartX) {
- realX = mStartX;
- }
- if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? realX < mStartX - mDragDistance
- : realX > mStartX + mDragDistance) {
- view.setPressed(true);
- onUserActivity(event.getEventTime());
- } else {
- view.setPressed(false);
- }
- if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? realX < mStartX - mScaledTouchSlop
- : realX > mStartX + mScaledTouchSlop) {
- mTouchSlopReached = true;
- }
- view.setTranslationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? Math.max(realX - mStartX, -mDragDistance)
- : Math.min(realX - mStartX, mDragDistance));
- break;
- case MotionEvent.ACTION_UP:
- if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? realX < mStartX - mDragDistance
- : realX > mStartX + mDragDistance) {
- onActionPerformed();
- view.animate().x(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? -view.getWidth()
- : ((View) view.getParent()).getWidth() + view.getWidth())
- .setInterpolator(new AccelerateInterpolator(2f)).withEndAction(
- new Runnable() {
- @Override
- public void run() {
- view.setTranslationX(0);
- }
- });
- mSkipCancelAnimation = true;
- }
- if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? realX < mStartX - mScaledTouchSlop
- : realX > mStartX + mScaledTouchSlop) {
- mTouchSlopReached = true;
- }
- if (!mTouchSlopReached) {
- mSkipCancelAnimation = true;
- view.animate().translationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
- ? -mDragDistance / 2
- : mDragDistance / 2).
- setInterpolator(new DecelerateInterpolator()).withEndAction(
- new Runnable() {
- @Override
- public void run() {
- view.animate().translationX(0).
- setInterpolator(new AccelerateInterpolator());
- }
- });
- }
- case MotionEvent.ACTION_CANCEL:
- view.setPressed(false);
- if (!mSkipCancelAnimation) {
- view.animate().translationX(0)
- .setInterpolator(new AccelerateInterpolator(2f));
- }
- break;
- }
- return true;
- }
- };
-
- public interface AffordanceListener {
-
- /**
- * Called when the view would like to report user activity.
- *
- * @param when The timestamp of the user activity in {@link SystemClock#uptimeMillis} time
- * base.
- */
- void onUserActivity(long when);
-
- /**
- * Called when the action of the affordance has been performed.
- */
- void onActionPerformed(SwipeAffordanceView view);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 5e2d06b..cf56fa57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -27,6 +27,7 @@
boolean animateZ;
boolean animateScale;
boolean animateHeight;
+ boolean animateTopInset;
boolean animateDimmed;
boolean hasDelays;
@@ -60,6 +61,11 @@
return this;
}
+ public AnimationFilter animateTopInset() {
+ animateTopInset = true;
+ return this;
+ }
+
public AnimationFilter animateDimmed() {
animateDimmed = true;
return this;
@@ -84,6 +90,7 @@
animateZ |= filter.animateZ;
animateScale |= filter.animateScale;
animateHeight |= filter.animateHeight;
+ animateTopInset |= filter.animateTopInset;
animateDimmed |= filter.animateDimmed;
hasDelays |= filter.hasDelays;
}
@@ -94,6 +101,7 @@
animateZ = false;
animateScale = false;
animateHeight = false;
+ animateTopInset = false;
animateDimmed = false;
hasDelays = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 079b184..58176b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1599,6 +1599,7 @@
new AnimationFilter()
.animateAlpha()
.animateHeight()
+ .animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
@@ -1607,6 +1608,7 @@
new AnimationFilter()
.animateAlpha()
.animateHeight()
+ .animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
@@ -1615,6 +1617,7 @@
new AnimationFilter()
.animateAlpha()
.animateHeight()
+ .animateTopInset()
.animateY()
.animateZ()
.hasDelays(),
@@ -1623,6 +1626,7 @@
new AnimationFilter()
.animateAlpha()
.animateHeight()
+ .animateTopInset()
.animateY()
.animateDimmed()
.animateScale()
@@ -1651,6 +1655,7 @@
new AnimationFilter()
.animateAlpha()
.animateHeight()
+ .animateTopInset()
.animateY()
.animateZ()
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index bd2541a..2b52c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -49,6 +49,7 @@
private int mBottomStackPeekSize;
private int mZDistanceBetweenElements;
private int mZBasicHeight;
+ private int mRoundedRectCornerRadius;
private StackIndentationFunctor mTopStackIndentationFunctor;
private StackIndentationFunctor mBottomStackIndentationFunctor;
@@ -111,6 +112,8 @@
mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
mBottomStackSlowDownLength = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
+ mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
}
@@ -146,6 +149,67 @@
handleDraggedViews(ambientState, resultState, algorithmState);
updateDimmedActivated(ambientState, resultState, algorithmState);
+ updateClipping(resultState, algorithmState);
+ }
+
+ private void updateClipping(StackScrollState resultState,
+ StackScrollAlgorithmState algorithmState) {
+ float previousNotificationEnd = 0;
+ float previousNotificationStart = 0;
+ boolean previousNotificationIsSwiped = false;
+ int childCount = algorithmState.visibleChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ StackScrollState.ViewState state = resultState.getViewStateForView(child);
+ float newYTranslation = state.yTranslation;
+ int newHeight = state.height;
+ // apply clipping and shadow
+ float newNotificationEnd = newYTranslation + newHeight;
+
+ // In the unlocked shade we have to clip a little bit higher because of the rounded
+ // corners of the notifications.
+ float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
+
+ // When the previous notification is swiped, we don't clip the content to the
+ // bottom of it.
+ float clipHeight = previousNotificationIsSwiped
+ ? newHeight
+ : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
+
+ updateChildClippingAndBackground(state, newHeight, clipHeight,
+ (int) (newHeight - (previousNotificationStart - newYTranslation)));
+
+ if (!child.isTransparent()) {
+ // Only update the previous values if we are not transparent,
+ // otherwise we would clip to a transparent view.
+ previousNotificationStart = newYTranslation + child.getClipTopAmount();
+ previousNotificationEnd = newNotificationEnd;
+ previousNotificationIsSwiped = child.getTranslationX() != 0;
+ }
+ }
+ }
+
+ /**
+ * Updates the shadow outline and the clipping for a view.
+ *
+ * @param state the viewState to update
+ * @param realHeight the currently applied height of the view
+ * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
+ * @param backgroundHeight the desired background height. The shadows of the view will be
+ * based on this height and the content will be clipped from the top
+ */
+ private void updateChildClippingAndBackground(StackScrollState.ViewState state, int realHeight,
+ float clipHeight, int backgroundHeight) {
+ if (realHeight > clipHeight) {
+ state.topOverLap = (int) (realHeight - clipHeight);
+ } else {
+ state.topOverLap = 0;
+ }
+ if (realHeight > backgroundHeight) {
+ state.clipTopAmount = (realHeight - backgroundHeight);
+ } else {
+ state.clipTopAmount = 0;
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 44e10be..94cb16d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.stack;
-import android.graphics.Outline;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
@@ -37,15 +36,12 @@
private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
private final ViewGroup mHostView;
- private final int mRoundedRectCornerRadius;
private Map<ExpandableView, ViewState> mStateMap;
private final Rect mClipRect = new Rect();
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
mStateMap = new HashMap<ExpandableView, ViewState>();
- mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
}
public ViewGroup getHostView() {
@@ -83,9 +79,6 @@
*/
public void apply() {
int numChildren = mHostView.getChildCount();
- float previousNotificationEnd = 0;
- float previousNotificationStart = 0;
- boolean previousNotificationIsSwiped = false;
for (int i = 0; i < numChildren; i++) {
ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ViewState state = mStateMap.get(child);
@@ -155,39 +148,41 @@
// apply dimming
child.setDimmed(state.dimmed, false /* animate */);
- // apply clipping and shadow
- float newNotificationEnd = newYTranslation + newHeight;
+ float oldClipTopAmount = child.getClipTopAmount();
+ if (oldClipTopAmount != state.clipTopAmount) {
+ child.setClipTopAmount(state.clipTopAmount);
+ }
- // In the unlocked shade we have to clip a little bit higher because of the rounded
- // corners of the notifications.
- float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
-
- // When the previous notification is swiped, we don't clip the content to the
- // bottom of it.
- float clipHeight = previousNotificationIsSwiped
- ? newHeight
- : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
-
- updateChildClippingAndBackground(child, newHeight,
- clipHeight,
- (int) (newHeight - (previousNotificationStart - newYTranslation)));
-
- if (!child.isTransparent()) {
- // Only update the previous values if we are not transparent,
- // otherwise we would clip to a transparent view.
- previousNotificationStart = newYTranslation + child.getClipTopAmount();
- previousNotificationEnd = newNotificationEnd;
- previousNotificationIsSwiped = child.getTranslationX() != 0;
+ if (state.topOverLap != 0) {
+ updateChildClip(child, newHeight, state.topOverLap);
+ } else {
+ child.setClipBounds(null);
}
if(child instanceof SpeedBumpView) {
- performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd,
+ float speedBumpEnd = newYTranslation + newHeight;
+ performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd,
newYTranslation);
}
}
}
}
+ /**
+ * Updates the clipping of a view
+ *
+ * @param child the view to update
+ * @param height the currently applied height of the view
+ * @param clipInset how much should this view be clipped from the top
+ */
+ private void updateChildClip(View child, int height, int clipInset) {
+ mClipRect.set(0,
+ clipInset,
+ child.getWidth(),
+ height);
+ child.setClipBounds(mClipRect);
+ }
+
private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
float speedBumpStart) {
View nextChild = getNextChildNotGone(i);
@@ -216,45 +211,6 @@
return null;
}
- /**
- * Updates the shadow outline and the clipping for a view.
- *
- * @param child the view to update
- * @param realHeight the currently applied height of the view
- * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
- * @param backgroundHeight the desired background height. The shadows of the view will be
- * based on this height and the content will be clipped from the top
- */
- private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
- float clipHeight, int backgroundHeight) {
- if (realHeight > clipHeight) {
- updateChildClip(child, realHeight, clipHeight);
- } else {
- child.setClipBounds(null);
- }
- if (realHeight > backgroundHeight) {
- child.setClipTopAmount(realHeight - backgroundHeight);
- } else {
- child.setClipTopAmount(0);
- }
- }
-
- /**
- * Updates the clipping of a view
- *
- * @param child the view to update
- * @param height the currently applied height of the view
- * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
- */
- private void updateChildClip(View child, int height, float clipHeight) {
- int clipInset = (int) (height - clipHeight);
- mClipRect.set(0,
- clipInset,
- child.getWidth(),
- height);
- child.setClipBounds(mClipRect);
- }
-
public static class ViewState {
// These are flags such that we can create masks for filtering.
@@ -276,6 +232,18 @@
boolean dimmed;
/**
+ * The amount which the view should be clipped from the top. This is calculated to
+ * perceive consistent shadows.
+ */
+ int clipTopAmount;
+
+ /**
+ * How much does the child overlap with the previous view on the top? Can be used for
+ * a clipping optimization
+ */
+ int topOverLap;
+
+ /**
* The index of the view, only accounting for views not equal to GONE
*/
int notGoneIndex;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index f019e6c..f41ab3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -133,10 +133,11 @@
boolean scaleChanging = child.getScaleX() != viewState.scale;
boolean alphaChanging = alpha != child.getAlpha();
boolean heightChanging = viewState.height != child.getActualHeight();
+ boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
boolean wasAdded = mNewAddChildren.contains(child);
boolean hasDelays = mAnimationFilter.hasDelays;
boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
- alphaChanging || heightChanging;
+ alphaChanging || heightChanging || topInsetChanging;
long delay = 0;
if (hasDelays && isDelayRelevant || wasAdded) {
delay = calculateChildAnimationDelay(viewState, finalState);
@@ -167,6 +168,11 @@
startHeightAnimation(child, viewState, delay);
}
+ // start top inset animation
+ if (topInsetChanging) {
+ startInsetAnimation(child, viewState, delay);
+ }
+
// start dimmed animation
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
@@ -280,6 +286,64 @@
child.setTag(TAG_END_HEIGHT, newEndValue);
}
+ private void startInsetAnimation(final ExpandableView child,
+ StackScrollState.ViewState viewState, long delay) {
+ Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
+ Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
+ int newEndValue = viewState.clipTopAmount;
+ if (previousEndValue != null && previousEndValue == newEndValue) {
+ return;
+ }
+ ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
+ if (!mAnimationFilter.animateTopInset) {
+ // just a local update was performed
+ if (previousAnimator != null) {
+ // we need to increase all animation keyframes of the previous animator by the
+ // relative change to the end value
+ PropertyValuesHolder[] values = previousAnimator.getValues();
+ int relativeDiff = newEndValue - previousEndValue;
+ int newStartValue = previousStartValue + relativeDiff;
+ values[0].setIntValues(newStartValue, newEndValue);
+ child.setTag(TAG_START_TOP_INSET, newStartValue);
+ child.setTag(TAG_END_TOP_INSET, newEndValue);
+ previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+ return;
+ } else {
+ // no new animation needed, let's just apply the value
+ child.setClipTopAmount(newEndValue);
+ return;
+ }
+ }
+
+ ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ child.setClipTopAmount((int) animation.getAnimatedValue());
+ }
+ });
+ animator.setInterpolator(mFastOutSlowInInterpolator);
+ long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+ animator.setDuration(newDuration);
+ if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+ animator.setStartDelay(delay);
+ }
+ animator.addListener(getGlobalAnimationFinishedListener());
+ // remove the tag when the animation is finished
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ child.setTag(TAG_ANIMATOR_TOP_INSET, null);
+ child.setTag(TAG_START_TOP_INSET, null);
+ child.setTag(TAG_END_TOP_INSET, null);
+ }
+ });
+ startAnimator(animator);
+ child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
+ child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
+ child.setTag(TAG_END_TOP_INSET, newEndValue);
+ }
+
private void startAlphaAnimation(final ExpandableView child,
final StackScrollState.ViewState viewState, long delay) {
Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 25147b4..c2bd1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,12 +17,12 @@
package com.android.systemui.statusbar.tv;
import android.os.IBinder;
+import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
-import com.android.internal.policy.IKeyguardShowCallback;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -50,7 +50,7 @@
}
@Override
- public void addNotificationInternal(StatusBarNotification notification) {
+ public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
}
@Override
@@ -58,7 +58,7 @@
}
@Override
- protected void removeNotificationInternal(String key) {
+ protected void removeNotificationInternal(String key, Ranking ranking) {
}
@Override
@@ -117,7 +117,7 @@
}
@Override
- protected void updateNotificationIcons() {
+ protected void updateNotifications() {
}
@Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index e178773..1e21e4f 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -66,6 +66,7 @@
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -1925,9 +1926,8 @@
ServiceManager.checkService(DreamService.DREAM_SERVICE));
}
- static ITelephony getTelephonyService() {
- return ITelephony.Stub.asInterface(
- ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+ TelephonyManager getTelephonyService() {
+ return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
static IAudioService getAudioService() {
@@ -2010,14 +2010,10 @@
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallScreen at this point,
// and his ONLY options are to answer or reject the call.)
- try {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null && telephonyService.isRinging()) {
- Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
- return -1;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null && telephonyManager.isRinging()) {
+ Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+ return -1;
}
// Delay handling home if a double-tap is possible.
@@ -3967,37 +3963,33 @@
}
}
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- // If an incoming call is ringing, either VOLUME key means
- // "silence ringer". We handle these keys here, rather than
- // in the InCallScreen, to make sure we'll respond to them
- // even if the InCallScreen hasn't come to the foreground yet.
- // Look for the DOWN event here, to agree with the "fallback"
- // behavior in the InCallScreen.
- Log.i(TAG, "interceptKeyBeforeQueueing:"
- + " VOLUME key-down while ringing: Silence ringer!");
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ // If an incoming call is ringing, either VOLUME key means
+ // "silence ringer". We handle these keys here, rather than
+ // in the InCallScreen, to make sure we'll respond to them
+ // even if the InCallScreen hasn't come to the foreground yet.
+ // Look for the DOWN event here, to agree with the "fallback"
+ // behavior in the InCallScreen.
+ Log.i(TAG, "interceptKeyBeforeQueueing:"
+ + " VOLUME key-down while ringing: Silence ringer!");
- // Silence the ringer. (It's safe to call this
- // even if the ringer has already been silenced.)
- telephonyService.silenceRinger();
+ // Silence the ringer. (It's safe to call this
+ // even if the ringer has already been silenced.)
+ telephonyManager.silenceRinger();
- // And *don't* pass this key thru to the current activity
- // (which is probably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- break;
- }
- if (telephonyService.isOffhook()
- && (result & ACTION_PASS_TO_USER) == 0) {
- // If we are in call but we decided not to pass the key to
- // the application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
- break;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ // And *don't* pass this key thru to the current activity
+ // (which is probably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
+ break;
+ }
+ if (telephonyManager.isOffhook()
+ && (result & ACTION_PASS_TO_USER) == 0) {
+ // If we are in call but we decided not to pass the key to
+ // the application, handle the volume change here.
+ handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+ break;
}
}
@@ -4014,14 +4006,10 @@
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
- ITelephony telephonyService = getTelephonyService();
+ TelephonyManager telephonyManager = getTelephonyService();
boolean hungUp = false;
- if (telephonyService != null) {
- try {
- hungUp = telephonyService.endCall();
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
- }
+ if (telephonyManager != null) {
+ hungUp = telephonyManager.endCall();
}
interceptPowerKeyDown(!interactive || hungUp);
} else {
@@ -4057,23 +4045,19 @@
interceptScreenshotChord();
}
- ITelephony telephonyService = getTelephonyService();
+ TelephonyManager telephonyManager = getTelephonyService();
boolean hungUp = false;
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- // Pressing Power while there's a ringing incoming
- // call should silence the ringer.
- telephonyService.silenceRinger();
- } else if ((mIncallPowerBehavior
- & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
- && telephonyService.isOffhook() && interactive) {
- // Otherwise, if "Power button ends call" is enabled,
- // the Power button will hang up any current active call.
- hungUp = telephonyService.endCall();
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ // Pressing Power while there's a ringing incoming
+ // call should silence the ringer.
+ telephonyManager.silenceRinger();
+ } else if ((mIncallPowerBehavior
+ & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
+ && telephonyManager.isOffhook() && interactive) {
+ // Otherwise, if "Power button ends call" is enabled,
+ // the Power button will hang up any current active call.
+ hungUp = telephonyManager.endCall();
}
}
interceptPowerKeyDown(!interactive || hungUp
@@ -4106,16 +4090,12 @@
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (!telephonyService.isIdle()) {
- // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
- // to avoid music playback.
- break;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (!telephonyManager.isIdle()) {
+ // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
+ // to avoid music playback.
+ break;
}
}
}
@@ -4145,20 +4125,16 @@
case KeyEvent.KEYCODE_CALL: {
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- Log.i(TAG, "interceptKeyBeforeQueueing:"
- + " CALL key-down while ringing: Answer the call!");
- telephonyService.answerRingingCall();
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ Log.i(TAG, "interceptKeyBeforeQueueing:"
+ + " CALL key-down while ringing: Answer the call!");
+ telephonyManager.answerRingingCall();
- // And *don't* pass this key thru to the current activity
- // (which is presumably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ // And *don't* pass this key thru to the current activity
+ // (which is presumably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
}
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b94ea62..1b1fc8b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -259,13 +259,17 @@
userIds[i]));
}
- ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()];
+ ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>();
final SparseArray<ArrayList<ComponentName>> toAdd
= new SparseArray<ArrayList<ComponentName>>();
synchronized (mMutex) {
- // unbind and remove all existing services
- toRemove = mServices.toArray(toRemove);
+ // Unbind automatically bound services, retain system services.
+ for (ManagedServiceInfo service : mServices) {
+ if (!service.isSystem) {
+ toRemove.add(service);
+ }
+ }
final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
final ArraySet<String> newPackages = new ArraySet<String>();
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
index 6d208ff..80030b4 100644
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -16,12 +16,22 @@
package com.android.server.task;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
import android.content.Context;
-import android.content.Task;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.Log;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
@@ -39,13 +49,6 @@
/** Master list of tasks. */
private final TaskStore mTasks;
- /** Check the pending queue and start any tasks. */
- static final int MSG_RUN_PENDING = 0;
- /** Initiate the stop task flow. */
- static final int MSG_STOP_TASK = 1;
- /** */
- static final int MSG_CHECK_TASKS = 2;
-
/**
* Track Services that have currently active or pending tasks. The index is provided by
* {@link TaskStatus#getServiceToken()}
@@ -54,6 +57,14 @@
new SparseArray<TaskServiceContext>();
private final TaskHandler mHandler;
+ private final TaskManagerStub mTaskManagerStub;
+
+ /** Check the pending queue and start any tasks. */
+ static final int MSG_RUN_PENDING = 0;
+ /** Initiate the stop task flow. */
+ static final int MSG_STOP_TASK = 1;
+ /** */
+ static final int MSG_CHECK_TASKS = 2;
private class TaskHandler extends Handler {
@@ -94,21 +105,6 @@
}
/**
- * Entry point from client to schedule the provided task.
- * This will add the task to the
- * @param task Task object containing execution parameters
- * @param userId The id of the user this task is for.
- * @param uId The package identifier of the application this task is for.
- * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
- * of this task.
- * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
- */
- public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
- TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
- return 0;
- }
-
- /**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
@@ -121,11 +117,42 @@
super(context);
mTasks = new TaskStore(context);
mHandler = new TaskHandler(context.getMainLooper());
+ mTaskManagerStub = new TaskManagerStub();
}
@Override
public void onStart() {
+ publishBinderService(Context.TASK_SERVICE, mTaskManagerStub);
+ }
+ /**
+ * Entry point from client to schedule the provided task.
+ * This will add the task to the
+ * @param task Task object containing execution parameters
+ * @param userId The id of the user this task is for.
+ * @param uId The package identifier of the application this task is for.
+ * @param canPersistTask Whether or not the client has the appropriate permissions for
+ * persisting of this task.
+ * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+ */
+ public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
+ TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
+ return 0;
+ }
+
+ public List<Task> getPendingTasks(int uid) {
+ ArrayList<Task> outList = new ArrayList<Task>(3);
+ synchronized (mTasks) {
+ final SparseArray<TaskStatus> tasks = mTasks.getTasks();
+ final int N = tasks.size();
+ for (int i = 0; i < N; i++) {
+ TaskStatus ts = tasks.get(i);
+ if (ts.getUid() == uid) {
+ outList.add(ts.getTask());
+ }
+ }
+ }
+ return outList;
}
// StateChangedListener implementations.
@@ -162,7 +189,7 @@
public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) {
final TaskServiceContext serviceContext = mActiveServices.get(serviceToken);
if (serviceContext == null) {
- Log.e(TAG, "Task completed for invalid service context; " + serviceToken);
+ Slog.e(TAG, "Task completed for invalid service context; " + serviceToken);
return;
}
@@ -203,4 +230,98 @@
private void postCheckTasksMessage() {
mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
}
+
+ /**
+ * Binder stub trampoline implementation
+ */
+ final class TaskManagerStub extends ITaskManager.Stub {
+ /** Cache determination of whether a given app can persist tasks
+ * key is uid of the calling app; value is undetermined/true/false
+ */
+ private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
+
+ // Determine whether the caller is allowed to persist tasks, with a small cache
+ // because the lookup is expensive enough that we'd like to avoid repeating it.
+ // This must be called from within the calling app's binder identity!
+ private boolean canCallerPersistTasks() {
+ final boolean canPersist;
+ final int callingUid = Binder.getCallingUid();
+ synchronized (mPersistCache) {
+ Boolean cached = mPersistCache.get(callingUid);
+ if (cached) {
+ canPersist = cached.booleanValue();
+ } else {
+ // Persisting tasks is tantamount to running at boot, so we permit
+ // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
+ // permission
+ int result = getContext().checkCallingPermission(
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED);
+ canPersist = (result == PackageManager.PERMISSION_GRANTED);
+ mPersistCache.put(callingUid, canPersist);
+ }
+ }
+ return canPersist;
+ }
+
+ // ITaskManager implementation
+ @Override
+ public int schedule(Task task) throws RemoteException {
+ final boolean canPersist = canCallerPersistTasks();
+ final int uid = Binder.getCallingUid();
+ final int userId = UserHandle.getCallingUserId();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return TaskManagerService.this.schedule(task, userId, uid, canPersist);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public List<Task> getAllPendingTasks() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void cancelAll() throws RemoteException {
+ }
+
+ @Override
+ public void cancel(int taskId) throws RemoteException {
+ }
+
+ /**
+ * "dumpsys" infrastructure
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ TaskManagerService.this.dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ };
+
+ void dumpInternal(PrintWriter pw) {
+ synchronized (mTasks) {
+ pw.print("Registered tasks:");
+ if (mTasks.size() > 0) {
+ SparseArray<TaskStatus> tasks = mTasks.getTasks();
+ for (int i = 0; i < tasks.size(); i++) {
+ TaskStatus ts = tasks.get(i);
+ pw.println();
+ ts.dump(pw, " ");
+ }
+ } else {
+ pw.println();
+ pw.println("No tasks scheduled.");
+ }
+ }
+ pw.println();
+ }
}
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
index b51cbb3..165445a 100644
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -45,7 +45,7 @@
* is reused to start concurrent tasks on the TaskService. Information here is unique
* to the service.
* Functionality provided by this class:
- * - Managages wakelock for the service.
+ * - Manages wakelock for the service.
* - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
* -
*/
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
index 3bfc8a5..81187c8 100644
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ b/services/core/java/com/android/server/task/TaskStore.java
@@ -16,8 +16,8 @@
package com.android.server.task;
+import android.app.task.Task;
import android.content.Context;
-import android.content.Task;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
@@ -95,6 +95,13 @@
}
/**
+ * @return the number of tasks in the store
+ */
+ public int size() {
+ return mTasks.size();
+ }
+
+ /**
* @return The live array of TaskStatus objects.
*/
public SparseArray<TaskStatus> getTasks() {
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
index 6a4e1f3..a0038c5d 100644
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -70,13 +70,10 @@
}
/**
- * @param userId Id of the user for whom we are updating the connectivity state.
+ *
*/
- private void updateTrackedTasks(int userId) {
+ private void updateTrackedTasks() {
for (TaskStatus ts : mTrackedTasks) {
- if (ts.userId != userId) {
- continue;
- }
boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
@@ -107,14 +104,13 @@
final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
// This broadcast gets sent a lot, only update if the active network has changed.
if (activeNetwork.getType() == networkType) {
- final int userid = context.getUserId();
mMetered = false;
mConnectivity =
!intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (mConnectivity) { // No point making the call if we know there's no conn.
mMetered = connManager.isActiveNetworkMetered();
}
- updateTrackedTasks(userid);
+ updateTrackedTasks();
}
} else {
Log.w(TAG, "Unrecognised action in intent: " + action);
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
index d96fedc..d270016 100644
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -16,17 +16,19 @@
package com.android.server.task.controllers;
+import android.app.task.Task;
import android.content.ComponentName;
-import android.content.Task;
import android.content.pm.PackageParser;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.UserHandle;
+import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Uniquely identifies a task internally.
- * Created from the public {@link android.content.Task} object when it lands on the scheduler.
+ * Created from the public {@link android.app.task.Task} object when it lands on the scheduler.
* Contains current state of the requirements of the task, as well as a function to evaluate
* whether it's ready to run.
* This object is shared among the various controllers - hence why the different fields are atomic.
@@ -36,10 +38,9 @@
* @hide
*/
public class TaskStatus {
+ final Task task;
final int taskId;
- final int userId;
final int uId;
- final ComponentName component;
final Bundle extras;
final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
@@ -57,9 +58,9 @@
private long earliestRunTimeElapsedMillis;
private long latestRunTimeElapsedMillis;
- /** Provide a unique handle to the service that this task will be run on. */
+ /** Provide a handle to the service that this task will be run on. */
public int getServiceToken() {
- return component.hashCode() + userId;
+ return uId;
}
/** Generate a TaskStatus object for a given task and uid. */
@@ -70,9 +71,8 @@
/** Set up the state of a newly scheduled task. */
TaskStatus(Task task, int userId, int uId) {
+ this.task = task;
this.taskId = task.getTaskId();
- this.userId = userId;
- this.component = task.getService();
this.extras = task.getExtras();
this.uId = uId;
@@ -100,16 +100,20 @@
hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
}
+ public Task getTask() {
+ return task;
+ }
+
public int getTaskId() {
return taskId;
}
public ComponentName getServiceComponent() {
- return component;
+ return task.getService();
}
public int getUserId() {
- return userId;
+ return UserHandle.getUserId(uId);
}
public int getUid() {
@@ -161,9 +165,9 @@
@Override
public int hashCode() {
- int result = component.hashCode();
+ int result = getServiceComponent().hashCode();
result = 31 * result + taskId;
- result = 31 * result + userId;
+ result = 31 * result + uId;
return result;
}
@@ -174,7 +178,14 @@
TaskStatus that = (TaskStatus) o;
return ((taskId == that.taskId)
- && (userId == that.userId)
- && (component.equals(that.component)));
+ && (uId == that.uId)
+ && (getServiceComponent().equals(that.getServiceComponent())));
+ }
+
+ // Dumpsys infrastructure
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("Task "); pw.println(taskId);
+ pw.print(prefix); pw.print("uid="); pw.println(uId);
+ pw.print(prefix); pw.print("component="); pw.println(task.getService());
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de46b16..bc34e0ef 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,6 +83,7 @@
import com.android.server.search.SearchManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.task.TaskManagerService;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.twilight.TwilightService;
@@ -132,6 +133,8 @@
"com.android.server.hdmi.HdmiCecService";
private static final String ETHERNET_SERVICE_CLASS =
"com.android.server.ethernet.EthernetService";
+ private static final String TASK_SERVICE_CLASS =
+ "com.android.server.task.TaskManagerService";
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -831,6 +834,8 @@
mSystemServiceManager.startService(UiModeManagerService.class);
+ mSystemServiceManager.startService(TaskManagerService.class);
+
if (!disableNonCoreServices) {
try {
if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
new file mode 100644
index 0000000..c439211
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecomm;
+
+/**
+ * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
+ * commands that were previously handled by ITelephony.
+ * {@hide}
+ */
+oneway interface ITelecommService {
+
+ /**
+ * Silence the ringer if an incoming call is currently ringing.
+ * (If vibrating, stop the vibrator also.)
+ *
+ * It's safe to call this if the ringer has already been silenced, or
+ * even if there's no incoming call. (If so, this method will do nothing.)
+ */
+ void silenceRinger();
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a896861..4aed1fe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -30,6 +30,7 @@
import android.telephony.Rlog;
import android.util.Log;
+import com.android.internal.telecomm.ITelecommService;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -65,6 +66,8 @@
public class TelephonyManager {
private static final String TAG = "TelephonyManager";
+ private static final String TELECOMM_SERVICE_NAME = "telecomm";
+
private static ITelephonyRegistry sRegistry;
/**
@@ -1536,6 +1539,10 @@
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
+ private ITelecommService getTelecommService() {
+ return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+ }
+
//
//
// PhoneStateListener
@@ -2016,9 +2023,9 @@
@PrivateApi
public void silenceRinger() {
try {
- getITelephony().silenceRinger();
+ getTelecommService().silenceRinger();
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#silenceRinger", e);
+ Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
}
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
index 31d1594..f4a9f52 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -97,6 +97,13 @@
return found;
}
+ @LayoutlibDelegate
+ /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
+ int[] values, int[] attrs) {
+ // FIXME
+ return null;
+ }
+
// ---- private helper methods ----
private static boolean setupResources(Theme thisTheme) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
index 0a7899a..5d89f83 100644
--- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
@@ -27,4 +27,10 @@
// pass
return false;
}
+
+ @LayoutlibDelegate
+ /*package*/ static TypedArray obtain(Resources res, int len) {
+ // FIXME
+ return null;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 802cf1c..33813d1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -18,9 +18,11 @@
import java.awt.Font;
import java.awt.Graphics2D;
+import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -36,12 +38,12 @@
@SuppressWarnings("deprecation")
public class BidiRenderer {
- /* package */ static class ScriptRun {
+ /*package*/ static class ScriptRun {
int start;
int limit;
boolean isRtl;
int scriptCode;
- FontInfo font;
+ Font font;
public ScriptRun(int start, int limit, boolean isRtl) {
this.start = start;
@@ -51,9 +53,10 @@
}
}
- private Graphics2D mGraphics;
- private Paint_Delegate mPaint;
+ private final Graphics2D mGraphics;
+ private final Paint_Delegate mPaint;
private char[] mText;
+ private List<Font> mFonts;
// Bounds of the text drawn so far.
private RectF mBounds;
private float mBaseline;
@@ -63,11 +66,15 @@
* @param paint The Paint to use to get the fonts. Should not be null.
* @param text Unidirectional text. Should not be null.
*/
- /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+ /*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
assert (paint != null);
mGraphics = graphics;
mPaint = paint;
mText = text;
+ mFonts = new ArrayList<Font>(paint.getFonts().size());
+ for (FontInfo fontInfo : paint.getFonts()) {
+ mFonts.add(fontInfo.mFont);
+ }
}
/**
@@ -94,7 +101,7 @@
// the script runs.
mBounds = new RectF(x, y, x, y);
mBaseline = y;
- for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) {
+ for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
@@ -108,16 +115,15 @@
* much as possible. This also implements a fallback mechanism to render characters that cannot
* be drawn using the preferred font.
*/
- private void renderScript(int start, int limit, FontInfo preferredFont, int flag,
+ private void renderScript(int start, int limit, Font preferredFont, int flag,
float[] advances, int advancesIndex, boolean draw) {
- List<FontInfo> fonts = mPaint.getFonts();
- if (fonts == null || preferredFont == null) {
+ if (mFonts.size() == 0 || preferredFont == null) {
return;
}
while (start < limit) {
boolean foundFont = false;
- int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit);
+ int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
if (canDisplayUpTo == -1) {
// We can draw all characters in the text.
render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
@@ -133,8 +139,8 @@
// The current character cannot be drawn with the preferred font. Cycle through all the
// fonts to check which one can draw it.
int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
- for (FontInfo font : fonts) {
- canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount);
+ for (Font font : mFonts) {
+ canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
if (canDisplayUpTo == -1) {
render(start, start+charCount, font, flag, advances, advancesIndex, draw);
start += charCount;
@@ -160,15 +166,19 @@
* Renders the text to the right of the bounds with the given font.
* @param font The font to render the text with.
*/
- private void render(int start, int limit, FontInfo font, int flag, float[] advances,
+ private void render(int start, int limit, Font font, int flag, float[] advances,
int advancesIndex, boolean draw) {
- // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
- // the anti-aliasing set.
- FontRenderContext f = font.mMetrics.getFontRenderContext();
- FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(),
- f.usesFractionalMetrics());
- GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag);
+ FontRenderContext frc;
+ if (mGraphics != null) {
+ frc = mGraphics.getFontRenderContext();
+ } else {
+ frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+ // Metrics obtained this way don't have anti-aliasing set. So,
+ // we create a new FontRenderContext with anti-aliasing set.
+ frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
+ }
+ GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
int ng = gv.getNumGlyphs();
int[] ci = gv.getGlyphCharIndices(0, ng, null);
if (advances != null) {
@@ -206,7 +216,7 @@
}
/* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
- boolean isRtl, List<FontInfo> fonts) {
+ boolean isRtl, List<Font> fonts) {
LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
int count = limit - start;
@@ -225,10 +235,10 @@
// TODO: Replace this method with one which returns the font based on the scriptCode.
private static void setScriptFont(char[] text, ScriptRun run,
- List<FontInfo> fonts) {
- for (FontInfo fontInfo : fonts) {
- if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) {
- run.font = fontInfo;
+ List<Font> fonts) {
+ for (Font font : fonts) {
+ if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
+ run.font = font;
return;
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 56c0de9..e9daffd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -979,7 +979,6 @@
final float startX, final float startY, final int flags, long paint,
long typeface) {
- // TODO: use typeface.
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -1097,7 +1096,7 @@
/**
* Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
* <p>Note that the drawable may actually be executed several times if there are
- * layers involved (see {@link #saveLayer(RectF, int, int)}.
+ * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
*/
private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
GcSnapshot.Drawable drawable) {
@@ -1117,7 +1116,7 @@
* Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
* to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
* <p>Note that the drawable may actually be executed several times if there are
- * layers involved (see {@link #saveLayer(RectF, int, int)}.
+ * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
*/
private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
// get the delegate from the native int.
@@ -1190,12 +1189,6 @@
return mSnapshot.clipRect(left, top, right, bottom, regionOp);
}
- private void setBitmap(Bitmap_Delegate bitmap) {
- mBitmap = bitmap;
- assert mSnapshot.size() == 1;
- mSnapshot.setBitmap(mBitmap);
- }
-
private static void drawBitmap(
long nativeCanvas,
Bitmap_Delegate bitmap,
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
new file mode 100644
index 0000000..5e7543a0
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -0,0 +1,225 @@
+/*
+ * 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.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Font;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
+
+/**
+ * Delegate implementing the native methods of android.graphics.FontFamily
+ *
+ * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original FontFamily class.
+ *
+ * @see DelegateManager
+ */
+public class FontFamily_Delegate {
+
+ // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
+ // separately.
+ private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
+ private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
+ private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
+ private static final String FONT_SUBSTRING_COMPACT = "UI";
+
+ /**
+ * A class associating {@link Font} with its metadata.
+ */
+ private static final class FontInfo {
+ Font mFont;
+ /** Regular, Bold, Italic, or BoldItalic. */
+ int mStyle;
+ /**
+ * The variant of the Font - compact or elegant.
+ * @see Paint#setElegantTextHeight(boolean)
+ */
+ boolean mIsCompact;
+ }
+
+ // ---- delegate manager ----
+ private static final DelegateManager<FontFamily_Delegate> sManager =
+ new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
+
+ // ---- delegate helper data ----
+ private static String sFontLocation;
+ private static final List<FontFamily_Delegate> sPostInitDelegate = new
+ ArrayList<FontFamily_Delegate>();
+
+
+ // ---- delegate data ----
+ private List<FontInfo> mFonts = new ArrayList<FontInfo>();
+ // Path of fonts that haven't been created since sFontLoader hasn't been initialized.
+ private List<String> mPath = new ArrayList<String>();
+
+
+ // ---- Public Helper methods ----
+
+ public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
+ return sManager.getDelegate(nativeFontFamily);
+ }
+
+ public static synchronized void setFontLocation(String fontLocation) {
+ sFontLocation = fontLocation;
+ for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
+ fontFamily.init();
+ }
+ sPostInitDelegate.clear();
+ }
+
+ public Font getFont(int style, boolean isCompact) {
+ FontInfo plainFont = null;
+ FontInfo styledFont = null; // Font matching the style but not isCompact
+ for (FontInfo font : mFonts) {
+ if (font.mStyle == style) {
+ if (font.mIsCompact == isCompact) {
+ return font.mFont;
+ }
+ styledFont = font;
+ }
+ if (font.mStyle == Font.PLAIN) {
+ if (plainFont == null) {
+ plainFont = font;
+ continue;
+ }
+ if (font.mIsCompact == isCompact) {
+ // Override the previous selection of plain font since we've found a better one.
+ plainFont = font;
+ }
+ }
+ }
+ if (styledFont != null) {
+ return styledFont.mFont;
+ }
+
+ // No font with the mentioned style is found. Try to derive one.
+ if (plainFont != null && style > 0 && style < 4) {
+ styledFont = new FontInfo();
+ styledFont.mFont = plainFont.mFont.deriveFont(style);
+ styledFont.mStyle = style;
+ styledFont.mIsCompact = plainFont.mIsCompact;
+ // Add the font to the list of fonts so that we don't have to derive it the next time.
+ mFonts.add(styledFont);
+ return styledFont.mFont;
+ }
+ return null;
+ }
+
+ // ---- native methods ----
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreateFamily() {
+ FontFamily_Delegate delegate = new FontFamily_Delegate();
+ if (sFontLocation != null) {
+ delegate.init();
+ } else {
+ sPostInitDelegate.add(delegate);
+ }
+ return sManager.addNewDelegate(delegate);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nUnrefFamily(long nativePtr) {
+ sManager.removeJavaReferenceFor(nativePtr);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean nAddFont(long nativeFamily, String path) {
+ FontFamily_Delegate delegate = getDelegate(nativeFamily);
+ if (delegate != null) {
+ if (sFontLocation == null) {
+ delegate.mPath.add(path);
+ return true;
+ }
+ return delegate.addFont(path);
+ }
+ return false;
+ }
+
+ private void init() {
+ for (String path : mPath) {
+ addFont(path);
+ }
+ mPath = null;
+ }
+
+ private boolean addFont(String path) {
+ Font font = loadFont(path);
+ if (font == null) {
+ return false;
+ }
+ FontInfo fontInfo = new FontInfo();
+ fontInfo.mFont = font;
+ addFontMetadata(fontInfo, path);
+ // TODO ensure that mFonts doesn't have the font with this style already.
+ mFonts.add(fontInfo);
+ return true;
+ }
+
+ private static void addFontMetadata(FontInfo fontInfo, String path) {
+ int style = Font.PLAIN;
+ String fontName = path.substring(path.lastIndexOf('/'), path.length());
+ if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
+ style = Font.BOLD | Font.ITALIC;
+ } else if (fontName.endsWith(FONT_SUFFIX_BOLD)) {
+ style = Font.BOLD;
+ } else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) {
+ style = Font.ITALIC;
+ }
+ fontInfo.mStyle = style;
+
+ // Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf.
+ // This should go away when this info is passed on by nAddFont().
+ int hyphenIndex = fontName.lastIndexOf('-');
+ fontInfo.mIsCompact = hyphenIndex > 0 &&
+ fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT);
+
+ }
+
+ private static Font loadFont(String path) {
+ if (path.startsWith(SYSTEM_FONTS) ) {
+ String relativePath = path.substring(SYSTEM_FONTS.length());
+ File f = new File(sFontLocation, relativePath);
+
+ try {
+ return Font.createFont(Font.TRUETYPE_FONT, f);
+ } catch (Exception e) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+ String.format("Unable to load font %1$s", relativePath),
+ null /*throwable*/, null /*data*/);
+ }
+ } else {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
+ null /*throwable*/, null /*data*/);
+ }
+
+ return null;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 8862f5b..f42f48f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -203,6 +203,16 @@
}
@LayoutlibDelegate
+ /*package*/ static boolean native_isAffine(long native_object) {
+ Matrix_Delegate d = sManager.getDelegate(native_object);
+ if (d == null) {
+ return true;
+ }
+
+ return (d.computeTypeMask() & kPerspective_Mask) == 0;
+ }
+
+ @LayoutlibDelegate
/*package*/ static boolean native_rectStaysRect(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 83df745..911f4e7 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -53,7 +53,7 @@
public class Paint_Delegate {
/**
- * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+ * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
*/
/*package*/ static final class FontInfo {
Font mFont;
@@ -66,8 +66,6 @@
// ---- delegate helper data ----
private List<FontInfo> mFonts;
- private final FontRenderContext mFontContext = new FontRenderContext(
- new AffineTransform(), true, true);
// ---- delegate data ----
private int mFlags;
@@ -83,6 +81,7 @@
private float mTextScaleX;
private float mTextSkewX;
private int mHintingMode = Paint.HINTING_ON;
+ private boolean mIsCompact = true;
private Xfermode_Delegate mXfermode;
private ColorFilter_Delegate mColorFilter;
@@ -101,8 +100,7 @@
}
/**
- * Returns the list of {@link Font} objects. The first item is the main font, the rest
- * are fall backs for characters not present in the main font.
+ * Returns the list of {@link Font} objects.
*/
public List<FontInfo> getFonts() {
return mFonts;
@@ -437,12 +435,20 @@
@LayoutlibDelegate
/*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
- return false;
+ // get the delegate from the native int.
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ return delegate != null && !delegate.mIsCompact;
}
@LayoutlibDelegate
/*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
- // TODO
+ // get the delegate from the native int.
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ if (delegate == null) {
+ return;
+ }
+
+ delegate.mIsCompact = !elegant;
}
@LayoutlibDelegate
@@ -621,7 +627,6 @@
int inc = count > 0 ? 1 : -1;
int measureIndex = 0;
- float measureAcc = 0;
for (int i = index; i != index + count; i += inc, measureIndex++) {
int start, end;
if (i < index) {
@@ -640,7 +645,6 @@
measuredWidth[measureIndex] = res;
}
- measureAcc += res;
if (res > maxWidth) {
// we should not return this char index, but since it's 0-based
// and we need to return a count, we simply return measureIndex;
@@ -818,7 +822,7 @@
return filter;
}
- delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
+ delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
// since none of those are supported, display a fidelity warning right away
if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
@@ -940,52 +944,17 @@
}
@LayoutlibDelegate
- /*package*/ static int native_getTextWidths(long native_object, char[] text, int index,
- int count, int bidiFlags, float[] widths) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return 0;
- }
-
- if (delegate.mFonts.size() > 0) {
- // FIXME: handle multi-char characters (see measureText)
- float totalAdvance = 0;
- for (int i = 0; i < count; i++) {
- char c = text[i + index];
- boolean found = false;
- for (FontInfo info : delegate.mFonts) {
- if (info.mFont.canDisplay(c)) {
- float adv = info.mMetrics.charWidth(c);
- totalAdvance += adv;
- if (widths != null) {
- widths[i] = adv;
- }
-
- found = true;
- break;
- }
- }
-
- if (found == false) {
- // no advance for this char.
- if (widths != null) {
- widths[i] = 0.f;
- }
- }
- }
-
- return (int) totalAdvance;
- }
-
- return 0;
+ /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+ char[] text, int index, int count, int bidiFlags, float[] widths) {
+ return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
+ index, count, bidiFlags, widths, 0);
}
@LayoutlibDelegate
- /*package*/ static int native_getTextWidths(long native_object, String text, int start,
- int end, int bidiFlags, float[] widths) {
- return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
- bidiFlags, widths);
+ /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+ String text, int start, int end, int bidiFlags, float[] widths) {
+ return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
+ end - start, bidiFlags, widths);
}
@LayoutlibDelegate
@@ -997,15 +966,20 @@
@LayoutlibDelegate
/*package*/ static float native_getTextRunAdvances(long native_object,
+ long native_typeface /*ignored*/,
char[] text, int index, int count, int contextIndex, int contextCount,
int flags, float[] advances, int advancesIndex) {
+ // native_typeface is passed here since Framework's old implementation did not have the
+ // typeface object associated with the Paint. Since, we follow the new framework way,
+ // we store the typeface with the paint and use it directly.
+
if (advances != null)
for (int i = advancesIndex; i< advancesIndex+count; i++)
advances[i]=0;
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+ if (delegate == null) {
return 0.f;
}
boolean isRtl = isRtl(flags);
@@ -1017,7 +991,7 @@
}
@LayoutlibDelegate
- /*package*/ static float native_getTextRunAdvances(long native_object,
+ /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
String text, int start, int end, int contextStart, int contextEnd,
int flags, float[] advances, int advancesIndex) {
// FIXME: support contextStart and contextEnd
@@ -1025,8 +999,8 @@
char[] buffer = TemporaryBuffer.obtain(count);
TextUtils.getChars(text, start, end, buffer, 0);
- return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
- contextEnd - contextStart, flags, advances, advancesIndex);
+ return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
+ contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
}
@LayoutlibDelegate
@@ -1076,7 +1050,7 @@
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+ if (delegate == null) {
return;
}
delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
@@ -1150,7 +1124,7 @@
private void updateFontObject() {
if (mTypeface != null) {
// Get the fonts from the TypeFace object.
- List<Font> fonts = mTypeface.getFonts();
+ List<Font> fonts = mTypeface.getFonts(mIsCompact);
// create new font objects as well as FontMetrics, based on the current text size
// and skew info.
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 60cd157..ed8f3b4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -19,7 +19,6 @@
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.content.res.AssetManager;
@@ -44,100 +43,67 @@
*/
public final class Typeface_Delegate {
- private static final String SYSTEM_FONTS = "/system/fonts/";
+ public static final String SYSTEM_FONTS = "/system/fonts/";
// ---- delegate manager ----
private static final DelegateManager<Typeface_Delegate> sManager =
new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
// ---- delegate helper data ----
- private static final String DEFAULT_FAMILY = "sans-serif";
-
- private static FontLoader sFontLoader;
- private static final List<Typeface_Delegate> sPostInitDelegate =
- new ArrayList<Typeface_Delegate>();
+ private static String sFontLocation;
// ---- delegate data ----
- private final String mFamily;
+ private final long[] mFontFamilies; // the reference to FontFamily_Delegate.
private int mStyle;
- private List<Font> mFonts;
+ private static long sDefaultTypeface;
// ---- Public Helper methods ----
-
- public static synchronized void init(FontLoader fontLoader) {
- sFontLoader = fontLoader;
-
- for (Typeface_Delegate delegate : sPostInitDelegate) {
- delegate.init();
- }
- sPostInitDelegate.clear();
+ public static synchronized void setFontLocation(String fontLocation) {
+ sFontLocation = fontLocation;
+ FontFamily_Delegate.setFontLocation(fontLocation);
}
public static Typeface_Delegate getDelegate(long nativeTypeface) {
return sManager.getDelegate(nativeTypeface);
}
- public static List<Font> getFonts(Typeface typeface) {
- return getFonts(typeface.native_instance);
- }
-
- public static List<Font> getFonts(long native_int) {
- Typeface_Delegate delegate = sManager.getDelegate(native_int);
- if (delegate == null) {
- return null;
+ public List<Font> getFonts(boolean compact) {
+ List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
+ for (long fontFamily : mFontFamilies) {
+ FontFamily_Delegate ffd = FontFamily_Delegate.getDelegate(fontFamily);
+ if (ffd != null) {
+ Font font = ffd.getFont(mStyle, compact);
+ if (font != null) {
+ fonts.add(font);
+ }
+ }
}
-
- return delegate.getFonts();
- }
-
- public List<Font> getFonts() {
- return mFonts;
+ return fonts;
}
// ---- native methods ----
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreate(String familyName, int style) {
- if (familyName == null) {
- familyName = DEFAULT_FAMILY;
- }
- if (style < 0) {
- style = Typeface.NORMAL;
- }
-
- Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
- if (sFontLoader != null) {
- newDelegate.init();
- } else {
- // font loader has not been initialized yet, add the delegate to a list of delegates
- // to init when the font loader is initialized.
- // There won't be any rendering before this happens anyway.
- sPostInitDelegate.add(newDelegate);
- }
-
- return sManager.addNewDelegate(newDelegate);
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Could not find font with family \"" + familyName + "\".",
+ null /*throwable*/, null /*data*/);
+ return 0;
}
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
Typeface_Delegate delegate = sManager.getDelegate(native_instance);
if (delegate == null) {
+ delegate = sManager.getDelegate(sDefaultTypeface);
+ }
+ if (delegate == null) {
return 0;
}
- Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
- if (sFontLoader != null) {
- newDelegate.init();
- } else {
- // font loader has not been initialized yet, add the delegate to a list of delegates
- // to init when the font loader is initialized.
- // There won't be any rendering before this happens anyway.
- sPostInitDelegate.add(newDelegate);
- }
-
- return sManager.addNewDelegate(newDelegate);
+ return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style));
}
@LayoutlibDelegate
@@ -149,31 +115,15 @@
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreateFromFile(String path) {
- if (path.startsWith(SYSTEM_FONTS) ) {
- String relativePath = path.substring(SYSTEM_FONTS.length());
- File f = new File(sFontLoader.getOsFontsLocation(), relativePath);
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Typeface.createFromFile() is not supported.,", null, null);
+ return 0;
+ }
- try {
- Font font = Font.createFont(Font.TRUETYPE_FONT, f);
- if (font != null) {
- Typeface_Delegate newDelegate = new Typeface_Delegate(font);
- return sManager.addNewDelegate(newDelegate);
- }
- } catch (Exception e) {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
- String.format("Unable to load font %1$s", relativePath),
- null /*throwable*/, null /*data*/);
- }
- } else {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Typeface.createFromFile() can only work with platform fonts located in " +
- SYSTEM_FONTS,
- null /*throwable*/, null /*data*/);
- }
-
-
- // return a copy of the base font
- return nativeCreate(null, 0);
+ @LayoutlibDelegate
+ /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
+ Typeface_Delegate delegate = new Typeface_Delegate(familyArray, Typeface.NORMAL);
+ return sManager.addNewDelegate(delegate);
}
@LayoutlibDelegate
@@ -191,24 +141,21 @@
return delegate.mStyle;
}
+ @LayoutlibDelegate
+ /*package*/ static void nativeSetDefault(long native_instance) {
+ sDefaultTypeface = native_instance;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static File getSystemFontConfigLocation() {
+ return new File(sFontLocation);
+ }
+
// ---- Private delegate/helper methods ----
- private Typeface_Delegate(String family, int style) {
- mFamily = family;
+ private Typeface_Delegate(long[] fontFamilies, int style) {
+ mFontFamilies = fontFamilies;
mStyle = style;
}
- private Typeface_Delegate(Font font) {
- mFamily = font.getFamily();
- mStyle = Typeface.NORMAL;
-
- mFonts = sFontLoader.getFallbackFonts(mStyle);
-
- // insert the font glyph first.
- mFonts.add(0, font);
- }
-
- private void init() {
- mFonts = sFontLoader.getFont(mFamily, mStyle);
- }
}
diff --git a/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
new file mode 100644
index 0000000..a193330
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
@@ -0,0 +1,49 @@
+/*
+ * 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.util;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Delegate overriding some methods of android.util.Xml
+ *
+ * Through the layoutlib_create tool, the original methods of Xml have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ */
+public class Xml_Delegate {
+
+ @LayoutlibDelegate
+ /*package*/ static XmlPullParser newPullParser() {
+ try {
+ KXmlParser parser = new KXmlParser();
+ // The prebuilt kxml2 library with the IDE doesn't support DOCECL.
+// parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ return parser;
+ } catch (XmlPullParserException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index fa8050f..ffab4de 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -26,7 +26,6 @@
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
-import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.layoutlib.bridge.impl.RenderDrawable;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -61,7 +60,7 @@
/**
* Main entry point of the LayoutLib Bridge.
* <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
- * {@link #createScene(SceneParams)}
+ * {@link #createSession(SessionParams)}
*/
public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
@@ -147,8 +146,7 @@
if (getClass() != obj.getClass()) return false;
IntArray other = (IntArray) obj;
- if (!Arrays.equals(mArray, other.mArray)) return false;
- return true;
+ return Arrays.equals(mArray, other.mArray);
}
}
@@ -251,14 +249,7 @@
}
// load the fonts.
- FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
- if (fontLoader != null) {
- Typeface_Delegate.init(fontLoader);
- } else {
- log.error(LayoutLog.TAG_BROKEN,
- "Failed create FontLoader in layout lib.", null);
- return false;
- }
+ Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath());
// now parse com.android.internal.R (and only this one as android.R is a subset of
// the internal version), and put the content in the maps.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
deleted file mode 100644
index cc7338a..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.impl;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import android.graphics.Typeface;
-
-import java.awt.Font;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Provides {@link Font} object to the layout lib.
- * <p/>
- * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
- * fonts.xml file located alongside the ttf files.
- */
-public final class FontLoader {
- private static final String FONTS_SYSTEM = "system_fonts.xml";
- private static final String FONTS_VENDOR = "vendor_fonts.xml";
- private static final String FONTS_FALLBACK = "fallback_fonts.xml";
-
- private static final String NODE_FAMILYSET = "familyset";
- private static final String NODE_FAMILY = "family";
- private static final String NODE_NAME = "name";
- private static final String NODE_FILE = "file";
-
- private static final String ATTRIBUTE_VARIANT = "variant";
- private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
- private static final String FONT_SUFFIX_NONE = ".ttf";
- private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
- private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
- // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
- // separately.
- private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
- private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf";
-
- // This must match the values of Typeface styles so that we can use them for indices in this
- // array.
- private static final int[] AWT_STYLES = new int[] {
- Font.PLAIN,
- Font.BOLD,
- Font.ITALIC,
- Font.BOLD | Font.ITALIC
- };
- private static int[] DERIVE_BOLD_ITALIC = new int[] {
- Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL
- };
- private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL };
- private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL };
-
- private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>();
- private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>();
-
- private final String mOsFontsLocation;
-
- public static FontLoader create(String fontOsLocation) {
- try {
- SAXParserFactory parserFactory = SAXParserFactory.newInstance();
- parserFactory.setNamespaceAware(true);
-
- // parse the system fonts
- FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM);
- List<FontInfo> systemFonts = handler.getFontList();
-
-
- // parse the fallback fonts
- handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK);
- List<FontInfo> fallbackFonts = handler.getFontList();
-
- return new FontLoader(fontOsLocation, systemFonts, fallbackFonts);
- } catch (ParserConfigurationException e) {
- // return null below
- } catch (SAXException e) {
- // return null below
- } catch (FileNotFoundException e) {
- // return null below
- } catch (IOException e) {
- // return null below
- }
-
- return null;
- }
-
- private static FontHandler parseFontFile(SAXParserFactory parserFactory,
- String fontOsLocation, String fontFileName)
- throws ParserConfigurationException, SAXException, IOException, FileNotFoundException {
-
- SAXParser parser = parserFactory.newSAXParser();
- File f = new File(fontOsLocation, fontFileName);
-
- FontHandler definitionParser = new FontHandler(
- fontOsLocation + File.separator);
- parser.parse(new FileInputStream(f), definitionParser);
- return definitionParser;
- }
-
- private FontLoader(String fontOsLocation,
- List<FontInfo> fontList, List<FontInfo> fallBackList) {
- mOsFontsLocation = fontOsLocation;
- mMainFonts.addAll(fontList);
- mFallbackFonts.addAll(fallBackList);
- }
-
-
- public String getOsFontsLocation() {
- return mOsFontsLocation;
- }
-
- /**
- * Returns a {@link Font} object given a family name and a style value (constant in
- * {@link Typeface}).
- * @param family the family name
- * @param style a 1-item array containing the requested style. Based on the font being read
- * the actual style may be different. The array contains the actual style after
- * the method returns.
- * @return the font object or null if no match could be found.
- */
- public synchronized List<Font> getFont(String family, int style) {
- List<Font> result = new ArrayList<Font>();
-
- if (family == null) {
- return result;
- }
-
-
- // get the font objects from the main list based on family.
- for (FontInfo info : mMainFonts) {
- if (info.families.contains(family)) {
- result.add(info.font[style]);
- break;
- }
- }
-
- // add all the fallback fonts for the given style
- for (FontInfo info : mFallbackFonts) {
- result.add(info.font[style]);
- }
-
- return result;
- }
-
-
- public synchronized List<Font> getFallbackFonts(int style) {
- List<Font> result = new ArrayList<Font>();
- // add all the fallback fonts
- for (FontInfo info : mFallbackFonts) {
- result.add(info.font[style]);
- }
- return result;
- }
-
-
- private final static class FontInfo {
- final Font[] font = new Font[4]; // Matches the 4 type-face styles.
- final Set<String> families;
-
- FontInfo() {
- families = new HashSet<String>();
- }
- }
-
- private final static class FontHandler extends DefaultHandler {
- private final String mOsFontsLocation;
-
- private FontInfo mFontInfo = null;
- private final StringBuilder mBuilder = new StringBuilder();
- private List<FontInfo> mFontList = new ArrayList<FontInfo>();
- private boolean isCompactFont = true;
-
- private FontHandler(String osFontsLocation) {
- super();
- mOsFontsLocation = osFontsLocation;
- }
-
- public List<FontInfo> getFontList() {
- return mFontList;
- }
-
- /* (non-Javadoc)
- * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
- */
- @Override
- public void startElement(String uri, String localName, String name, Attributes attributes)
- throws SAXException {
- if (NODE_FAMILYSET.equals(localName)) {
- mFontList = new ArrayList<FontInfo>();
- } else if (NODE_FAMILY.equals(localName)) {
- if (mFontList != null) {
- mFontInfo = null;
- }
- } else if (NODE_NAME.equals(localName)) {
- if (mFontList != null && mFontInfo == null) {
- mFontInfo = new FontInfo();
- }
- } else if (NODE_FILE.equals(localName)) {
- if (mFontList != null && mFontInfo == null) {
- mFontInfo = new FontInfo();
- }
- if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
- isCompactFont = false;
- } else {
- isCompactFont = true;
- }
- }
-
- mBuilder.setLength(0);
-
- super.startElement(uri, localName, name, attributes);
- }
-
- /* (non-Javadoc)
- * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
- */
- @Override
- public void characters(char[] ch, int start, int length) throws SAXException {
- if (isCompactFont) {
- mBuilder.append(ch, start, length);
- }
- }
-
- /* (non-Javadoc)
- * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
- */
- @Override
- public void endElement(String uri, String localName, String name) throws SAXException {
- if (NODE_FAMILY.equals(localName)) {
- if (mFontInfo != null) {
- // if has a normal font file, add to the list
- if (mFontInfo.font[Typeface.NORMAL] != null) {
- mFontList.add(mFontInfo);
-
- // create missing font styles, order is important.
- if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) {
- computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC);
- }
- if (mFontInfo.font[Typeface.ITALIC] == null) {
- computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC);
- }
- if (mFontInfo.font[Typeface.BOLD] == null) {
- computeDerivedFont(Typeface.BOLD, DERIVE_BOLD);
- }
- }
-
- mFontInfo = null;
- }
- } else if (NODE_NAME.equals(localName)) {
- // handle a new name for an existing Font Info
- if (mFontInfo != null) {
- String family = trimXmlWhitespaces(mBuilder.toString());
- mFontInfo.families.add(family);
- }
- } else if (NODE_FILE.equals(localName)) {
- // handle a new file for an existing Font Info
- if (isCompactFont && mFontInfo != null) {
- String fileName = trimXmlWhitespaces(mBuilder.toString());
- Font font = getFont(fileName);
- if (font != null) {
- if (fileName.endsWith(FONT_SUFFIX_REGULAR)) {
- mFontInfo.font[Typeface.NORMAL] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_BOLD)) {
- mFontInfo.font[Typeface.BOLD] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
- mFontInfo.font[Typeface.BOLD_ITALIC] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) {
- mFontInfo.font[Typeface.ITALIC] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_NONE)) {
- mFontInfo.font[Typeface.NORMAL] = font;
- }
- }
- }
- }
- }
-
- private Font getFont(String fileName) {
- try {
- File file = new File(mOsFontsLocation, fileName);
- if (file.exists()) {
- return Font.createFont(Font.TRUETYPE_FONT, file);
- }
- } catch (Exception e) {
-
- }
-
- return null;
- }
-
- private void computeDerivedFont( int toCompute, int[] basedOnList) {
- for (int basedOn : basedOnList) {
- if (mFontInfo.font[basedOn] != null) {
- mFontInfo.font[toCompute] =
- mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]);
- return;
- }
- }
-
- // we really shouldn't stop there. This means we don't have a NORMAL font...
- assert false;
- }
-
- private String trimXmlWhitespaces(String value) {
- if (value == null) {
- return null;
- }
-
- // look for carriage return and replace all whitespace around it by just 1 space.
- int index;
-
- while ((index = value.indexOf('\n')) != -1) {
- // look for whitespace on each side
- int left = index - 1;
- while (left >= 0) {
- if (Character.isWhitespace(value.charAt(left))) {
- left--;
- } else {
- break;
- }
- }
-
- int right = index + 1;
- int count = value.length();
- while (right < count) {
- if (Character.isWhitespace(value.charAt(right))) {
- right++;
- } else {
- break;
- }
- }
-
- // remove all between left and right (non inclusive) and replace by a single space.
- String leftString = null;
- if (left >= 0) {
- leftString = value.substring(0, left + 1);
- }
- String rightString = null;
- if (right < count) {
- rightString = value.substring(right);
- }
-
- if (leftString != null) {
- value = leftString;
- if (rightString != null) {
- value += " " + rightString;
- }
- } else {
- value = rightString != null ? rightString : "";
- }
- }
-
- // now we un-escape the string
- int length = value.length();
- char[] buffer = value.toCharArray();
-
- for (int i = 0 ; i < length ; i++) {
- if (buffer[i] == '\\') {
- if (buffer[i+1] == 'n') {
- // replace the char with \n
- buffer[i+1] = '\n';
- }
-
- // offset the rest of the buffer since we go from 2 to 1 char
- System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
- length--;
- }
- }
-
- return new String(buffer, 0, length);
- }
-
- }
-}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index bb72a1e..1f7a28e 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -125,15 +125,19 @@
"android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
"android.content.res.Resources$Theme#obtainStyledAttributes",
"android.content.res.Resources$Theme#resolveAttribute",
+ "android.content.res.Resources$Theme#resolveAttributes",
"android.content.res.Resources#localeToLanguageTag",
"android.content.res.AssetManager#newTheme",
"android.content.res.AssetManager#deleteTheme",
"android.content.res.AssetManager#applyThemeStyle",
"android.content.res.TypedArray#getValueAt",
+ "android.content.res.TypedArray#obtain",
"android.graphics.BitmapFactory#finishDecode",
+ "android.graphics.Typeface#getSystemFontConfigLocation",
"android.os.Handler#sendMessageAtTime",
"android.os.HandlerThread#run",
"android.text.format.DateFormat#is24HourFormat",
+ "android.util.Xml#newPullParser",
"android.view.Choreographer#getRefreshRate",
"android.view.Display#updateDisplayInfoLocked",
"android.view.Display#getWindowManager",
@@ -170,6 +174,7 @@
"android.graphics.DiscretePathEffect",
"android.graphics.DrawFilter",
"android.graphics.EmbossMaskFilter",
+ "android.graphics.FontFamily",
"android.graphics.LayerRasterizer",
"android.graphics.LightingColorFilter",
"android.graphics.LinearGradient",