diff options
author | Jason Monk <jmonk@google.com> | 2017-10-19 18:17:25 +0000 |
---|---|---|
committer | Jason Monk <jmonk@google.com> | 2017-10-19 18:17:25 +0000 |
commit | 07f9f65561c2b81bcd189b895b31bb2ad0438d74 (patch) | |
tree | 49f76f879a89c256a4f65b674086be50760bdffb /android/app | |
parent | d439404c9988df6001e4ff8bce31537e2692660e (diff) | |
download | android-28-07f9f65561c2b81bcd189b895b31bb2ad0438d74.tar.gz |
Revert "Import Android SDK Platform P [4402356]"
This reverts commit d439404c9988df6001e4ff8bce31537e2692660e.
Change-Id: I825790bdf38523800388bc1bb531cecfcd7e60bd
Diffstat (limited to 'android/app')
28 files changed, 311 insertions, 3409 deletions
diff --git a/android/app/Activity.java b/android/app/Activity.java index 85f73bb7..e0ac9113 100644 --- a/android/app/Activity.java +++ b/android/app/Activity.java @@ -542,9 +542,9 @@ import java.util.List; * <ul> * <li> <p>When creating a new document, the backing database entry or file for * it is created immediately. For example, if the user chooses to write - * a new email, a new entry for that email is created as soon as they + * a new e-mail, a new entry for that e-mail is created as soon as they * start entering data, so that if they go to any other activity after - * that point this email will now appear in the list of drafts.</p> + * that point this e-mail will now appear in the list of drafts.</p> * <li> <p>When an activity's <code>onPause()</code> method is called, it should * commit to the backing content provider or file any changes the user * has made. This ensures that those changes will be seen by any other @@ -1879,7 +1879,7 @@ public class Activity extends ContextThemeWrapper if (isFinishing()) { if (mAutoFillResetNeeded) { - getAutofillManager().onActivityFinished(); + getAutofillManager().commit(); } else if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) { // Activity was launched when user tapped a link in the Autofill Save UI - since @@ -6259,8 +6259,6 @@ public class Activity extends ContextThemeWrapper final AutofillManager afm = getAutofillManager(); if (afm != null) { afm.dump(prefix, writer); - } else { - writer.print(prefix); writer.println("No AutofillManager"); } } diff --git a/android/app/ActivityManager.java b/android/app/ActivityManager.java index fc4c8d7f..5e61727f 100644 --- a/android/app/ActivityManager.java +++ b/android/app/ActivityManager.java @@ -16,8 +16,14 @@ package android.app; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import android.Manifest; -import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -664,6 +670,138 @@ public class ActivityManager { /** Invalid stack ID. */ public static final int INVALID_STACK_ID = -1; + /** First static stack ID. + * @hide */ + private static final int FIRST_STATIC_STACK_ID = 0; + + /** ID of stack where fullscreen activities are normally launched into. + * @hide */ + public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1; + + /** ID of stack where freeform/resized activities are normally launched into. + * @hide */ + public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1; + + /** ID of stack that occupies a dedicated region of the screen. + * @hide */ + public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1; + + /** ID of stack that always on top (always visible) when it exist. + * @hide */ + public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1; + + /** Last static stack stack ID. + * @hide */ + private static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID; + + /** Start of ID range used by stacks that are created dynamically. + * @hide */ + public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1; + + // TODO: Figure-out a way to remove this. + /** @hide */ + public static boolean isStaticStack(int stackId) { + return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID; + } + + // TODO: It seems this mostly means a stack on a secondary display now. Need to see if + // there are other meanings. If not why not just use information from the display? + /** @hide */ + public static boolean isDynamicStack(int stackId) { + return stackId >= FIRST_DYNAMIC_STACK_ID; + } + + /** + * Returns true if we try to maintain focus in the current stack when the top activity + * finishes. + * @hide + */ + // TODO: Figure-out a way to remove. Probably isn't needed in the new world... + public static boolean keepFocusInStackIfPossible(int stackId) { + return stackId == FREEFORM_WORKSPACE_STACK_ID + || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID; + } + + /** + * Returns true if the windows of tasks being moved to the target stack from the source + * stack should be replaced, meaning that window manager will keep the old window around + * until the new is ready. + * @hide + */ + public static boolean replaceWindowsOnTaskMove(int sourceStackId, int targetStackId) { + return sourceStackId == FREEFORM_WORKSPACE_STACK_ID + || targetStackId == FREEFORM_WORKSPACE_STACK_ID; + } + + /** + * Returns true if the top task in the task is allowed to return home when finished and + * there are other tasks in the stack. + * @hide + */ + public static boolean allowTopTaskToReturnHome(int stackId) { + return stackId != PINNED_STACK_ID; + } + + /** + * Returns true if the stack should be resized to match the bounds specified by + * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack. + * @hide + */ + public static boolean resizeStackWithLaunchBounds(int stackId) { + return stackId == PINNED_STACK_ID; + } + + /** + * Returns true if a window from the specified stack with {@param stackId} are normally + * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it + * controls system bars, lockscreen occluded/dismissing state, screen rotation animation, + * etc. + * @hide + */ + // TODO: What about the other side of docked stack if we move this to WindowConfiguration? + public static boolean normallyFullscreenWindows(int stackId) { + return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID + && stackId != DOCKED_STACK_ID; + } + + /** Returns the stack id for the input windowing mode. + * @hide */ + // TODO: To be removed once we are not using stack id for stuff... + public static int getStackIdForWindowingMode(int windowingMode) { + switch (windowingMode) { + case WINDOWING_MODE_PINNED: return PINNED_STACK_ID; + case WINDOWING_MODE_FREEFORM: return FREEFORM_WORKSPACE_STACK_ID; + case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return DOCKED_STACK_ID; + case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return FULLSCREEN_WORKSPACE_STACK_ID; + case WINDOWING_MODE_FULLSCREEN: return FULLSCREEN_WORKSPACE_STACK_ID; + default: return INVALID_STACK_ID; + } + } + + /** Returns the windowing mode that should be used for this input stack id. + * @hide */ + // TODO: To be removed once we are not using stack id for stuff... + public static int getWindowingModeForStackId(int stackId, boolean inSplitScreenMode) { + final int windowingMode; + switch (stackId) { + case FULLSCREEN_WORKSPACE_STACK_ID: + windowingMode = inSplitScreenMode + ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN; + break; + case PINNED_STACK_ID: + windowingMode = WINDOWING_MODE_PINNED; + break; + case DOCKED_STACK_ID: + windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; + break; + case FREEFORM_WORKSPACE_STACK_ID: + windowingMode = WINDOWING_MODE_FREEFORM; + break; + default : + windowingMode = WINDOWING_MODE_UNDEFINED; + } + return windowingMode; + } } /** @@ -942,14 +1080,11 @@ public class ActivityManager { ATTR_TASKDESCRIPTION_PREFIX + "color"; private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND = ATTR_TASKDESCRIPTION_PREFIX + "colorBackground"; - private static final String ATTR_TASKDESCRIPTIONICON_FILENAME = + private static final String ATTR_TASKDESCRIPTIONICONFILENAME = ATTR_TASKDESCRIPTION_PREFIX + "icon_filename"; - private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE = - ATTR_TASKDESCRIPTION_PREFIX + "icon_resource"; private String mLabel; private Bitmap mIcon; - private int mIconRes; private String mIconFilename; private int mColorPrimary; private int mColorBackground; @@ -963,27 +1098,9 @@ public class ActivityManager { * @param icon An icon that represents the current state of this task. * @param colorPrimary A color to override the theme's primary color. This color must be * opaque. - * @deprecated use TaskDescription constructor with icon resource instead */ - @Deprecated public TaskDescription(String label, Bitmap icon, int colorPrimary) { - this(label, icon, 0, null, colorPrimary, 0, 0, 0); - if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { - throw new RuntimeException("A TaskDescription's primary color should be opaque"); - } - } - - /** - * Creates the TaskDescription to the specified values. - * - * @param label A label and description of the current state of this task. - * @param iconRes A drawable resource of an icon that represents the current state of this - * activity. - * @param colorPrimary A color to override the theme's primary color. This color must be - * opaque. - */ - public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) { - this(label, null, iconRes, null, colorPrimary, 0, 0, 0); + this(label, icon, null, colorPrimary, 0, 0, 0); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -994,22 +1111,9 @@ public class ActivityManager { * * @param label A label and description of the current state of this activity. * @param icon An icon that represents the current state of this activity. - * @deprecated use TaskDescription constructor with icon resource instead */ - @Deprecated public TaskDescription(String label, Bitmap icon) { - this(label, icon, 0, null, 0, 0, 0, 0); - } - - /** - * Creates the TaskDescription to the specified values. - * - * @param label A label and description of the current state of this activity. - * @param iconRes A drawable resource of an icon that represents the current state of this - * activity. - */ - public TaskDescription(String label, @DrawableRes int iconRes) { - this(label, null, iconRes, null, 0, 0, 0, 0); + this(label, icon, null, 0, 0, 0, 0); } /** @@ -1018,22 +1122,21 @@ public class ActivityManager { * @param label A label and description of the current state of this activity. */ public TaskDescription(String label) { - this(label, null, 0, null, 0, 0, 0, 0); + this(label, null, null, 0, 0, 0, 0); } /** * Creates an empty TaskDescription. */ public TaskDescription() { - this(null, null, 0, null, 0, 0, 0, 0); + this(null, null, null, 0, 0, 0, 0); } /** @hide */ - public TaskDescription(String label, Bitmap bitmap, int iconRes, String iconFilename, - int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor) { + public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary, + int colorBackground, int statusBarColor, int navigationBarColor) { mLabel = label; - mIcon = bitmap; - mIconRes = iconRes; + mIcon = icon; mIconFilename = iconFilename; mColorPrimary = colorPrimary; mColorBackground = colorBackground; @@ -1055,7 +1158,6 @@ public class ActivityManager { public void copyFrom(TaskDescription other) { mLabel = other.mLabel; mIcon = other.mIcon; - mIconRes = other.mIconRes; mIconFilename = other.mIconFilename; mColorPrimary = other.mColorPrimary; mColorBackground = other.mColorBackground; @@ -1071,7 +1173,6 @@ public class ActivityManager { public void copyFromPreserveHiddenFields(TaskDescription other) { mLabel = other.mLabel; mIcon = other.mIcon; - mIconRes = other.mIconRes; mIconFilename = other.mIconFilename; mColorPrimary = other.mColorPrimary; if (other.mColorBackground != 0) { @@ -1144,14 +1245,6 @@ public class ActivityManager { } /** - * Sets the icon resource for this task description. - * @hide - */ - public void setIcon(int iconRes) { - mIconRes = iconRes; - } - - /** * Moves the icon bitmap reference from an actual Bitmap to a file containing the * bitmap. * @hide @@ -1179,13 +1272,6 @@ public class ActivityManager { } /** @hide */ - @TestApi - public int getIconResource() { - return mIconRes; - } - - /** @hide */ - @TestApi public String getIconFilename() { return mIconFilename; } @@ -1251,10 +1337,7 @@ public class ActivityManager { Integer.toHexString(mColorBackground)); } if (mIconFilename != null) { - out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename); - } - if (mIconRes != 0) { - out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, Integer.toString(mIconRes)); + out.attribute(null, ATTR_TASKDESCRIPTIONICONFILENAME, mIconFilename); } } @@ -1266,10 +1349,8 @@ public class ActivityManager { setPrimaryColor((int) Long.parseLong(attrValue, 16)); } else if (ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND.equals(attrName)) { setBackgroundColor((int) Long.parseLong(attrValue, 16)); - } else if (ATTR_TASKDESCRIPTIONICON_FILENAME.equals(attrName)) { + } else if (ATTR_TASKDESCRIPTIONICONFILENAME.equals(attrName)) { setIconFilename(attrValue); - } else if (ATTR_TASKDESCRIPTIONICON_RESOURCE.equals(attrName)) { - setIcon(Integer.parseInt(attrValue, 10)); } } @@ -1292,7 +1373,6 @@ public class ActivityManager { dest.writeInt(1); mIcon.writeToParcel(dest, 0); } - dest.writeInt(mIconRes); dest.writeInt(mColorPrimary); dest.writeInt(mColorBackground); dest.writeInt(mStatusBarColor); @@ -1308,7 +1388,6 @@ public class ActivityManager { public void readFromParcel(Parcel source) { mLabel = source.readInt() > 0 ? source.readString() : null; mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null; - mIconRes = source.readInt(); mColorPrimary = source.readInt(); mColorBackground = source.readInt(); mStatusBarColor = source.readInt(); @@ -1329,8 +1408,8 @@ public class ActivityManager { @Override public String toString() { return "TaskDescription Label: " + mLabel + " Icon: " + mIcon + - " IconRes: " + mIconRes + " IconFilename: " + mIconFilename + - " colorPrimary: " + mColorPrimary + " colorBackground: " + mColorBackground + + " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary + + " colorBackground: " + mColorBackground + " statusBarColor: " + mColorBackground + " navigationBarColor: " + mNavigationBarColor; } @@ -1492,6 +1571,7 @@ public class ActivityManager { } dest.writeInt(stackId); dest.writeInt(userId); + dest.writeLong(firstActiveTime); dest.writeLong(lastActiveTime); dest.writeInt(affiliatedTaskId); dest.writeInt(affiliatedTaskColor); @@ -1520,6 +1600,7 @@ public class ActivityManager { TaskDescription.CREATOR.createFromParcel(source) : null; stackId = source.readInt(); userId = source.readInt(); + firstActiveTime = source.readLong(); lastActiveTime = source.readLong(); affiliatedTaskId = source.readInt(); affiliatedTaskColor = source.readInt(); @@ -1562,6 +1643,31 @@ public class ActivityManager { public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002; /** + * Provides a list that contains recent tasks for all + * profiles of a user. + * @hide + */ + public static final int RECENT_INCLUDE_PROFILES = 0x0004; + + /** + * Ignores all tasks that are on the home stack. + * @hide + */ + public static final int RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS = 0x0008; + + /** + * Ignores the top task in the docked stack. + * @hide + */ + public static final int RECENT_INGORE_DOCKED_STACK_TOP_TASK = 0x0010; + + /** + * Ignores all tasks that are on the pinned stack. + * @hide + */ + public static final int RECENT_INGORE_PINNED_STACK_TASKS = 0x0020; + + /** * <p></p>Return a list of the tasks that the user has recently launched, with * the most recent being first and older ones after in order. * @@ -1596,7 +1702,33 @@ public class ActivityManager { public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags) throws SecurityException { try { - return getService().getRecentTasks(maxNum, flags, UserHandle.myUserId()).getList(); + return getService().getRecentTasks(maxNum, + flags, UserHandle.myUserId()).getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Same as {@link #getRecentTasks(int, int)} but returns the recent tasks for a + * specific user. It requires holding + * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. + * @param maxNum The maximum number of entries to return in the list. The + * actual number returned may be smaller, depending on how many tasks the + * user has started and the maximum number the system can remember. + * @param flags Information about what to return. May be any combination + * of {@link #RECENT_WITH_EXCLUDED} and {@link #RECENT_IGNORE_UNAVAILABLE}. + * + * @return Returns a list of RecentTaskInfo records describing each of + * the recent tasks. Most recently activated tasks go first. + * + * @hide + */ + public List<RecentTaskInfo> getRecentTasksForUser(int maxNum, int flags, int userId) + throws SecurityException { + try { + return getService().getRecentTasks(maxNum, + flags, userId).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1889,6 +2021,22 @@ public class ActivityManager { } /** + * Completely remove the given task. + * + * @param taskId Identifier of the task to be removed. + * @return Returns true if the given task was found and removed. + * + * @hide + */ + public boolean removeTask(int taskId) throws SecurityException { + try { + return getService().removeTask(taskId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Sets the windowing mode for a specific task. Only works on tasks of type * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} * @param taskId The id of the task to set the windowing mode for. diff --git a/android/app/ActivityOptions.java b/android/app/ActivityOptions.java index b62e4c2d..a68c3a5c 100644 --- a/android/app/ActivityOptions.java +++ b/android/app/ActivityOptions.java @@ -17,13 +17,13 @@ package android.app; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; import android.annotation.Nullable; import android.annotation.TestApi; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; @@ -159,12 +159,6 @@ public class ActivityOptions { private static final String KEY_ANIM_SPECS = "android:activity.animSpecs"; /** - * Whether the activity should be launched into LockTask mode. - * @see #setLockTaskMode(boolean) - */ - private static final String KEY_LOCK_TASK_MODE = "android:activity.lockTaskMode"; - - /** * The display id the activity should be launched into. * @see #setLaunchDisplayId(int) * @hide @@ -285,7 +279,6 @@ public class ActivityOptions { private int mResultCode; private int mExitCoordinatorIndex; private PendingIntent mUsageTimeReport; - private boolean mLockTaskMode = false; private int mLaunchDisplayId = INVALID_DISPLAY; @WindowConfiguration.WindowingMode private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED; @@ -877,7 +870,6 @@ public class ActivityOptions { mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX); break; } - mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false); mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY); mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED); mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED); @@ -1064,37 +1056,6 @@ public class ActivityOptions { } /** - * Gets whether the activity is to be launched into LockTask mode. - * @return {@code true} if the activity is to be launched into LockTask mode. - * @see Activity#startLockTask() - * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[]) - */ - public boolean getLockTaskMode() { - return mLockTaskMode; - } - - /** - * Sets whether the activity is to be launched into LockTask mode. - * - * Use this option to start an activity in LockTask mode. Note that only apps permitted by - * {@link android.app.admin.DevicePolicyManager} can run in LockTask mode. Therefore, if - * {@link android.app.admin.DevicePolicyManager#isLockTaskPermitted(String)} returns - * {@code false} for the package of the target activity, a {@link SecurityException} will be - * thrown during {@link Context#startActivity(Intent, Bundle)}. - * - * Defaults to {@code false} if not set. - * - * @param lockTaskMode {@code true} if the activity is to be launched into LockTask mode. - * @return {@code this} {@link ActivityOptions} instance. - * @see Activity#startLockTask() - * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[]) - */ - public ActivityOptions setLockTaskMode(boolean lockTaskMode) { - mLockTaskMode = lockTaskMode; - return this; - } - - /** * Gets the id of the display where activity should be launched. * @return The id of the display where activity should be launched, * {@link android.view.Display#INVALID_DISPLAY} if not set. @@ -1287,7 +1248,6 @@ public class ActivityOptions { mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex; break; } - mLockTaskMode = otherOptions.mLockTaskMode; mAnimSpecs = otherOptions.mAnimSpecs; mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; mSpecsFuture = otherOptions.mSpecsFuture; @@ -1362,7 +1322,6 @@ public class ActivityOptions { b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex); break; } - b.putBoolean(KEY_LOCK_TASK_MODE, mLockTaskMode); b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId); b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode); b.putInt(KEY_LAUNCH_ACTIVITY_TYPE, mLaunchActivityType); diff --git a/android/app/KeyguardManager.java b/android/app/KeyguardManager.java index 1fe29004..54f74b15 100644 --- a/android/app/KeyguardManager.java +++ b/android/app/KeyguardManager.java @@ -387,6 +387,8 @@ public class KeyguardManager { * such as the Home key and the right soft keys, don't work. * * @return true if in keyguard restricted input mode. + * + * @see android.view.WindowManagerPolicy#inKeyguardRestrictedKeyInputMode */ public boolean inKeyguardRestrictedInputMode() { try { diff --git a/android/app/NotificationChannel.java b/android/app/NotificationChannel.java index c06ad3f3..47063f08 100644 --- a/android/app/NotificationChannel.java +++ b/android/app/NotificationChannel.java @@ -32,8 +32,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.util.Preconditions; -import com.android.internal.util.Preconditions; - import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; diff --git a/android/app/NotificationManager.java b/android/app/NotificationManager.java index a52dc1e4..eb52cb7f 100644 --- a/android/app/NotificationManager.java +++ b/android/app/NotificationManager.java @@ -934,14 +934,8 @@ public class NotificationManager { public static final int PRIORITY_CATEGORY_CALLS = 1 << 3; /** Calls from repeat callers are prioritized. */ public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4; - /** Alarms are prioritized */ - public static final int PRIORITY_CATEGORY_ALARMS = 1 << 5; - /** Media, system, game (catch-all for non-never suppressible sounds) are prioritized */ - public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 1 << 6; private static final int[] ALL_PRIORITY_CATEGORIES = { - PRIORITY_CATEGORY_ALARMS, - PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, PRIORITY_CATEGORY_REMINDERS, PRIORITY_CATEGORY_EVENTS, PRIORITY_CATEGORY_MESSAGES, @@ -1141,9 +1135,6 @@ public class NotificationManager { case PRIORITY_CATEGORY_MESSAGES: return "PRIORITY_CATEGORY_MESSAGES"; case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS"; case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS"; - case PRIORITY_CATEGORY_ALARMS: return "PRIORITY_CATEGORY_ALARMS"; - case PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER: - return "PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER"; default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory; } } diff --git a/android/app/StatusBarManager.java b/android/app/StatusBarManager.java index 23c4166d..8987bc02 100644 --- a/android/app/StatusBarManager.java +++ b/android/app/StatusBarManager.java @@ -73,16 +73,15 @@ public class StatusBarManager { public static final int DISABLE2_QUICK_SETTINGS = 1; public static final int DISABLE2_SYSTEM_ICONS = 1 << 1; public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2; - public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3; public static final int DISABLE2_NONE = 0x00000000; public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS - | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS; + | DISABLE2_NOTIFICATION_SHADE; @IntDef(flag = true, value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS, - DISABLE2_NOTIFICATION_SHADE, DISABLE2_GLOBAL_ACTIONS}) + DISABLE2_NOTIFICATION_SHADE}) @Retention(RetentionPolicy.SOURCE) public @interface Disable2Flags {} diff --git a/android/app/TaskStackListener.java b/android/app/TaskStackListener.java index 895d12a7..402e2095 100644 --- a/android/app/TaskStackListener.java +++ b/android/app/TaskStackListener.java @@ -77,7 +77,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onTaskRemovalStarted(int taskId) throws RemoteException { + public void onTaskRemovalStarted(int taskId) { } @Override @@ -91,10 +91,11 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onTaskProfileLocked(int taskId, int userId) throws RemoteException { + public void onTaskProfileLocked(int taskId, int userId) { } @Override - public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException { + public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) + throws RemoteException { } } diff --git a/android/app/WallpaperManager.java b/android/app/WallpaperManager.java index 081bd814..942cc995 100644 --- a/android/app/WallpaperManager.java +++ b/android/app/WallpaperManager.java @@ -388,12 +388,11 @@ public class WallpaperManager { public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which) { - return peekWallpaperBitmap(context, returnDefault, which, context.getUserId(), - false /* hardware */); + return peekWallpaperBitmap(context, returnDefault, which, context.getUserId()); } public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, - @SetWallpaperFlags int which, int userId, boolean hardware) { + @SetWallpaperFlags int which, int userId) { if (mService != null) { try { if (!mService.isWallpaperSupported(context.getOpPackageName())) { @@ -410,7 +409,7 @@ public class WallpaperManager { mCachedWallpaper = null; mCachedWallpaperUserId = 0; try { - mCachedWallpaper = getCurrentWallpaperLocked(context, userId, hardware); + mCachedWallpaper = getCurrentWallpaperLocked(context, userId); mCachedWallpaperUserId = userId; } catch (OutOfMemoryError e) { Log.w(TAG, "Out of memory loading the current wallpaper: " + e); @@ -448,7 +447,7 @@ public class WallpaperManager { } } - private Bitmap getCurrentWallpaperLocked(Context context, int userId, boolean hardware) { + private Bitmap getCurrentWallpaperLocked(Context context, int userId) { if (mService == null) { Log.w(TAG, "WallpaperService not running"); return null; @@ -461,9 +460,6 @@ public class WallpaperManager { if (fd != null) { try { BitmapFactory.Options options = new BitmapFactory.Options(); - if (hardware) { - options.inPreferredConfig = Bitmap.Config.HARDWARE; - } return BitmapFactory.decodeFileDescriptor( fd.getFileDescriptor(), null, options); } catch (OutOfMemoryError e) { @@ -818,23 +814,12 @@ public class WallpaperManager { } /** - * Like {@link #getDrawable()} but returns a Bitmap with default {@link Bitmap.Config}. - * - * @hide - */ - public Bitmap getBitmap() { - return getBitmap(false); - } - - /** * Like {@link #getDrawable()} but returns a Bitmap. * - * @param hardware Asks for a hardware backed bitmap. - * @see Bitmap.Config#HARDWARE * @hide */ - public Bitmap getBitmap(boolean hardware) { - return getBitmapAsUser(mContext.getUserId(), hardware); + public Bitmap getBitmap() { + return getBitmapAsUser(mContext.getUserId()); } /** @@ -842,8 +827,8 @@ public class WallpaperManager { * * @hide */ - public Bitmap getBitmapAsUser(int userId, boolean hardware) { - return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware); + public Bitmap getBitmapAsUser(int userId) { + return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId); } /** diff --git a/android/app/WindowConfiguration.java b/android/app/WindowConfiguration.java index 251863ca..6b405384 100644 --- a/android/app/WindowConfiguration.java +++ b/android/app/WindowConfiguration.java @@ -511,8 +511,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED; } - /** @hide */ - public static String windowingModeToString(@WindowingMode int windowingMode) { + private static String windowingModeToString(@WindowingMode int windowingMode) { switch (windowingMode) { case WINDOWING_MODE_UNDEFINED: return "undefined"; case WINDOWING_MODE_FULLSCREEN: return "fullscreen"; diff --git a/android/app/assist/AssistStructure.java b/android/app/assist/AssistStructure.java index e491a4f9..d9b7cd7e 100644 --- a/android/app/assist/AssistStructure.java +++ b/android/app/assist/AssistStructure.java @@ -616,9 +616,6 @@ public class AssistStructure implements Parcelable { CharSequence[] mAutofillOptions; boolean mSanitized; HtmlInfo mHtmlInfo; - int mMinEms = -1; - int mMaxEms = -1; - int mMaxLength = -1; // POJO used to override some autofill-related values when the node is parcelized. // Not written to parcel. @@ -716,9 +713,6 @@ public class AssistStructure implements Parcelable { if (p instanceof HtmlInfo) { mHtmlInfo = (HtmlInfo) p; } - mMinEms = in.readInt(); - mMaxEms = in.readInt(); - mMaxLength = in.readInt(); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -882,9 +876,6 @@ public class AssistStructure implements Parcelable { } else { out.writeParcelable(null, 0); } - out.writeInt(mMinEms); - out.writeInt(mMaxEms); - out.writeInt(mMaxLength); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); @@ -1453,39 +1444,6 @@ public class AssistStructure implements Parcelable { public ViewNode getChildAt(int index) { return mChildren[index]; } - - /** - * Returns the minimum width in ems of the text associated with this node, or {@code -1} - * if not supported by the node. - * - * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes, - * not for assist purposes. - */ - public int getMinTextEms() { - return mMinEms; - } - - /** - * Returns the maximum width in ems of the text associated with this node, or {@code -1} - * if not supported by the node. - * - * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes, - * not for assist purposes. - */ - public int getMaxTextEms() { - return mMaxEms; - } - - /** - * Returns the maximum length of the text associated with this node node, or {@code -1} - * if not supported by the node or not set. - * - * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes, - * not for assist purposes. - */ - public int getMaxTextLength() { - return mMaxLength; - } } /** @@ -1818,21 +1776,6 @@ public class AssistStructure implements Parcelable { } @Override - public void setMinTextEms(int minEms) { - mNode.mMinEms = minEms; - } - - @Override - public void setMaxTextEms(int maxEms) { - mNode.mMaxEms = maxEms; - } - - @Override - public void setMaxTextLength(int maxLength) { - mNode.mMaxLength = maxLength; - } - - @Override public void setDataIsSensitive(boolean sensitive) { mNode.mSanitized = !sensitive; } diff --git a/android/app/job/JobScheduler.java b/android/app/job/JobScheduler.java index 0deb2e13..3868439f 100644 --- a/android/app/job/JobScheduler.java +++ b/android/app/job/JobScheduler.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.ClipData; import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.os.PersistableBundle; @@ -39,18 +40,16 @@ import java.util.List; * and how to construct them. You will construct these JobInfo objects and pass them to the * JobScheduler with {@link #schedule(JobInfo)}. When the criteria declared are met, the * system will execute this job on your application's {@link android.app.job.JobService}. - * You identify the service component that implements the logic for your job when you - * construct the JobInfo using + * You identify which JobService is meant to execute the logic for your job when you create the + * JobInfo with * {@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}. * </p> * <p> - * The framework will be intelligent about when it executes jobs, and attempt to batch - * and defer them as much as possible. Typically if you don't specify a deadline on a job, it - * can be run at any moment depending on the current state of the JobScheduler's internal queue. - * <p> - * While a job is running, the system holds a wakelock on behalf of your app. For this reason, - * you do not need to take any action to guarantee that the device stays awake for the - * duration of the job. + * The framework will be intelligent about when you receive your callbacks, and attempt to batch + * and defer them as much as possible. Typically if you don't specify a deadline on your job, it + * can be run at any moment depending on the current state of the JobScheduler's internal queue, + * however it might be deferred as long as until the next time the device is connected to a power + * source. * </p> * <p>You do not * instantiate this class directly; instead, retrieve it through @@ -142,34 +141,30 @@ public abstract class JobScheduler { int userId, String tag); /** - * Cancel the specified job. If the job is currently executing, it is stopped - * immediately and the return value from its {@link JobService#onStopJob(JobParameters)} - * method is ignored. - * - * @param jobId unique identifier for the job to be canceled, as supplied to - * {@link JobInfo.Builder#JobInfo.Builder(int, android.content.ComponentName) - * JobInfo.Builder(int, android.content.ComponentName)}. + * Cancel a job that is pending in the JobScheduler. + * @param jobId unique identifier for this job. Obtain this value from the jobs returned by + * {@link #getAllPendingJobs()}. */ public abstract void cancel(int jobId); /** - * Cancel <em>all</em> jobs that have been scheduled by the calling application. + * Cancel all jobs that have been registered with the JobScheduler by this package. */ public abstract void cancelAll(); /** - * Retrieve all jobs that have been scheduled by the calling application. + * Retrieve all jobs for this package that are pending in the JobScheduler. * - * @return a list of all of the app's scheduled jobs. This includes jobs that are - * currently started as well as those that are still waiting to run. + * @return a list of all the jobs registered by this package that have not + * yet been executed. */ public abstract @NonNull List<JobInfo> getAllPendingJobs(); /** - * Look up the description of a scheduled job. + * Retrieve a specific job for this package that is pending in the + * JobScheduler. * - * @return The {@link JobInfo} description of the given scheduled job, or {@code null} - * if the supplied job ID does not correspond to any job. + * @return job registered by this package that has not yet been executed. */ public abstract @Nullable JobInfo getPendingJob(int jobId); } diff --git a/android/app/job/JobService.java b/android/app/job/JobService.java index 69afed20..9096b47b 100644 --- a/android/app/job/JobService.java +++ b/android/app/job/JobService.java @@ -18,7 +18,16 @@ package android.app.job; import android.app.Service; import android.content.Intent; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.lang.ref.WeakReference; /** * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p> @@ -46,7 +55,7 @@ public abstract class JobService extends Service { * </pre> * * <p>If a job service is declared in the manifest but not protected with this - * permission, that service will be ignored by the system. + * permission, that service will be ignored by the OS. */ public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE"; @@ -72,36 +81,14 @@ public abstract class JobService extends Service { } /** - * Called to indicate that the job has begun executing. Override this method with the - * logic for your job. Like all other component lifecycle callbacks, this method executes - * on your application's main thread. - * <p> - * Return {@code true} from this method if your job needs to continue running. If you - * do this, the job remains active until you call - * {@link #jobFinished(JobParameters, boolean)} to tell the system that it has completed - * its work, or until the job's required constraints are no longer satisfied. For - * example, if the job was scheduled using - * {@link JobInfo.Builder#setRequiresCharging(boolean) setRequiresCharging(true)}, - * it will be immediately halted by the system if the user unplugs the device from power, - * the job's {@link #onStopJob(JobParameters)} callback will be invoked, and the app - * will be expected to shut down all ongoing work connected with that job. - * <p> - * The system holds a wakelock on behalf of your app as long as your job is executing. - * This wakelock is acquired before this method is invoked, and is not released until either - * you call {@link #jobFinished(JobParameters, boolean)}, or after the system invokes - * {@link #onStopJob(JobParameters)} to notify your job that it is being shut down - * prematurely. - * <p> - * Returning {@code false} from this method means your job is already finished. The - * system's wakelock for the job will be released, and {@link #onStopJob(JobParameters)} - * will not be invoked. + * Override this method with the callback logic for your job. Any such logic needs to be + * performed on a separate thread, as this function is executed on your application's main + * thread. * - * @param params Parameters specifying info about this job, including the optional - * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle). - * This object serves to identify this specific running job instance when calling - * {@link #jobFinished(JobParameters, boolean)}. - * @return {@code true} if your service will continue running, using a separate thread - * when appropriate. {@code false} means that this job has completed its work. + * @param params Parameters specifying info about this job, including the extras bundle you + * optionally provided at job-creation time. + * @return True if your service needs to process the work (on a separate thread). False if + * there's no more work to be done for this job. */ public abstract boolean onStartJob(JobParameters params); @@ -114,44 +101,37 @@ public abstract class JobService extends Service { * {@link android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your * job was executing the user toggled WiFi. Another example is if you had specified * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its - * idle maintenance window. You are solely responsible for the behavior of your application - * upon receipt of this message; your app will likely start to misbehave if you ignore it. - * <p> - * Once this method returns, the system releases the wakelock that it is holding on - * behalf of the job.</p> + * 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> * - * @param params The parameters identifying this job, as supplied to - * the job in the {@link #onStartJob(JobParameters)} callback. - * @return {@code true} to indicate to the JobManager whether you'd like to reschedule - * this job based on the retry criteria provided at job creation-time; or {@code false} - * to end the job entirely. Regardless of the value returned, your job must stop executing. + * @param params Parameters specifying info about this job. + * @return True to indicate to the JobManager whether you'd like to reschedule this job based + * on the retry criteria provided at job creation-time. False to drop the job. Regardless of + * the value returned, your job must stop executing. */ public abstract boolean onStopJob(JobParameters params); /** - * Call this to inform the JobScheduler that the job has finished its work. When the - * system receives this message, it releases the wakelock being held for the job. + * Call this to inform the JobManager you've finished executing. This can be called from any + * thread, as it will ultimately be run on your application's main thread. When the system + * receives this message it will release the wakelock being held. * <p> - * You can request that the job be scheduled again by passing {@code true} as - * the <code>wantsReschedule</code> parameter. This will apply back-off policy - * for the job; this policy can be adjusted through the - * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} method - * when the job is originally scheduled. The job's initial - * requirements are preserved when jobs are rescheduled, regardless of backed-off - * policy. - * <p class="note"> - * A job running while the device is dozing will not be rescheduled with the normal back-off - * policy. Instead, the job will be re-added to the queue and executed again during - * a future idle maintenance window. + * You can specify post-execution behaviour to the scheduler here with + * <code>needsReschedule </code>. This will apply a back-off timer to your job based on + * the default, or what was set with + * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original + * requirements are always honoured even for a backed-off job. Note that a job running in + * idle mode will not be backed-off. Instead what will happen is the job will be re-added + * to the queue and re-executed within a future idle maintenance window. * </p> * - * @param params The parameters identifying this job, as supplied to - * the job in the {@link #onStartJob(JobParameters)} callback. - * @param wantsReschedule {@code true} if this job should be rescheduled according - * to the back-off criteria specified when it was first scheduled; {@code false} - * otherwise. + * @param params Parameters specifying system-provided info about this job, this was given to + * your application in {@link #onStartJob(JobParameters)}. + * @param needsReschedule True if this job should be rescheduled according to the back-off + * criteria specified at schedule-time. False otherwise. */ - public final void jobFinished(JobParameters params, boolean wantsReschedule) { - mEngine.jobFinished(params, wantsReschedule); + public final void jobFinished(JobParameters params, boolean needsReschedule) { + mEngine.jobFinished(params, needsReschedule); } -} +}
\ No newline at end of file diff --git a/android/app/slice/Slice.java b/android/app/slice/Slice.java deleted file mode 100644 index 7f9f74b4..00000000 --- a/android/app/slice/Slice.java +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (C) 2017 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.slice; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.StringDef; -import android.app.PendingIntent; -import android.app.RemoteInput; -import android.content.ContentResolver; -import android.content.IContentProvider; -import android.graphics.drawable.Icon; -import android.net.Uri; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.widget.RemoteViews; - -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.Preconditions; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A slice is a piece of app content and actions that can be surfaced outside of the app. - * - * <p>They are constructed using {@link Builder} in a tree structure - * that provides the OS some information about how the content should be displayed. - */ -public final class Slice implements Parcelable { - - /** - * @hide - */ - @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED, - HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL}) - public @interface SliceHint{ } - - /** - * Hint that this content is a title of other content in the slice. - */ - public static final String HINT_TITLE = "title"; - /** - * Hint that all sub-items/sub-slices within this content should be considered - * to have {@link #HINT_LIST_ITEM}. - */ - public static final String HINT_LIST = "list"; - /** - * Hint that this item is part of a list and should be formatted as if is part - * of a list. - */ - public static final String HINT_LIST_ITEM = "list_item"; - /** - * Hint that this content is important and should be larger when displayed if - * possible. - */ - public static final String HINT_LARGE = "large"; - /** - * Hint that this slice contains a number of actions that can be grouped together - * in a sort of controls area of the UI. - */ - public static final String HINT_ACTIONS = "actions"; - /** - * Hint indicating that this item (and its sub-items) are the current selection. - */ - public static final String HINT_SELECTED = "selected"; - /** - * Hint to indicate that this is a message as part of a communication - * sequence in this slice. - */ - public static final String HINT_MESSAGE = "message"; - /** - * Hint to tag the source (i.e. sender) of a {@link #HINT_MESSAGE}. - */ - public static final String HINT_SOURCE = "source"; - /** - * Hint that list items within this slice or subslice would appear better - * if organized horizontally. - */ - public static final String HINT_HORIZONTAL = "horizontal"; - /** - * Hint to indicate that this content should not be tinted. - */ - public static final String HINT_NO_TINT = "no_tint"; - /** - * Hint to indicate that this slice is incomplete and an update will be sent once - * loading is complete. Slices which contain HINT_PARTIAL will not be cached by the - * OS and should not be cached by apps. - */ - public static final String HINT_PARTIAL = "partial"; - - // These two are coming over from prototyping, but we probably don't want in - // public API, at least not right now. - /** - * @hide - */ - public static final String HINT_ALT = "alt"; - - private final SliceItem[] mItems; - private final @SliceHint String[] mHints; - private Uri mUri; - - Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) { - mHints = hints; - mItems = items.toArray(new SliceItem[items.size()]); - mUri = uri; - } - - protected Slice(Parcel in) { - mHints = in.readStringArray(); - int n = in.readInt(); - mItems = new SliceItem[n]; - for (int i = 0; i < n; i++) { - mItems[i] = SliceItem.CREATOR.createFromParcel(in); - } - mUri = Uri.CREATOR.createFromParcel(in); - } - - /** - * @return The Uri that this Slice represents. - */ - public Uri getUri() { - return mUri; - } - - /** - * @return All child {@link SliceItem}s that this Slice contains. - */ - public List<SliceItem> getItems() { - return Arrays.asList(mItems); - } - - /** - * @return All hints associated with this Slice. - */ - public @SliceHint List<String> getHints() { - return Arrays.asList(mHints); - } - - /** - * @hide - */ - public SliceItem getPrimaryIcon() { - for (SliceItem item : getItems()) { - if (item.getType() == SliceItem.TYPE_IMAGE) { - return item; - } - if (!(item.getType() == SliceItem.TYPE_SLICE && item.hasHint(Slice.HINT_LIST)) - && !item.hasHint(Slice.HINT_ACTIONS) - && !item.hasHint(Slice.HINT_LIST_ITEM) - && (item.getType() != SliceItem.TYPE_ACTION)) { - SliceItem icon = SliceQuery.find(item, SliceItem.TYPE_IMAGE); - if (icon != null) return icon; - } - } - return null; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStringArray(mHints); - dest.writeInt(mItems.length); - for (int i = 0; i < mItems.length; i++) { - mItems[i].writeToParcel(dest, flags); - } - mUri.writeToParcel(dest, 0); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * @hide - */ - public boolean hasHint(@SliceHint String hint) { - return ArrayUtils.contains(mHints, hint); - } - - /** - * A Builder used to construct {@link Slice}s - */ - public static class Builder { - - private final Uri mUri; - private ArrayList<SliceItem> mItems = new ArrayList<>(); - private @SliceHint ArrayList<String> mHints = new ArrayList<>(); - - /** - * Create a builder which will construct a {@link Slice} for the Given Uri. - * @param uri Uri to tag for this slice. - */ - public Builder(@NonNull Uri uri) { - mUri = uri; - } - - /** - * Create a builder for a {@link Slice} that is a sub-slice of the slice - * being constructed by the provided builder. - * @param parent The builder constructing the parent slice - */ - public Builder(@NonNull Slice.Builder parent) { - mUri = parent.mUri.buildUpon().appendPath("_gen") - .appendPath(String.valueOf(mItems.size())).build(); - } - - /** - * Add hints to the Slice being constructed - */ - public Builder addHints(@SliceHint String... hints) { - mHints.addAll(Arrays.asList(hints)); - return this; - } - - /** - * Add hints to the Slice being constructed - */ - public Builder addHints(@SliceHint List<String> hints) { - return addHints(hints.toArray(new String[hints.size()])); - } - - /** - * Add a sub-slice to the slice being constructed - */ - public Builder addSubSlice(@NonNull Slice slice) { - mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints().toArray( - new String[slice.getHints().size()]))); - return this; - } - - /** - * Add an action to the slice being constructed - */ - public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) { - mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, new String[0])); - return this; - } - - /** - * Add text to the slice being constructed - */ - public Builder addText(CharSequence text, @SliceHint String... hints) { - mItems.add(new SliceItem(text, SliceItem.TYPE_TEXT, hints)); - return this; - } - - /** - * Add text to the slice being constructed - */ - public Builder addText(CharSequence text, @SliceHint List<String> hints) { - return addText(text, hints.toArray(new String[hints.size()])); - } - - /** - * Add an image to the slice being constructed - */ - public Builder addIcon(Icon icon, @SliceHint String... hints) { - mItems.add(new SliceItem(icon, SliceItem.TYPE_IMAGE, hints)); - return this; - } - - /** - * Add an image to the slice being constructed - */ - public Builder addIcon(Icon icon, @SliceHint List<String> hints) { - return addIcon(icon, hints.toArray(new String[hints.size()])); - } - - /** - * @hide This isn't final - */ - public Builder addRemoteView(RemoteViews remoteView, @SliceHint String... hints) { - mItems.add(new SliceItem(remoteView, SliceItem.TYPE_REMOTE_VIEW, hints)); - return this; - } - - /** - * Add remote input to the slice being constructed - */ - public Slice.Builder addRemoteInput(RemoteInput remoteInput, - @SliceHint List<String> hints) { - return addRemoteInput(remoteInput, hints.toArray(new String[hints.size()])); - } - - /** - * Add remote input to the slice being constructed - */ - public Slice.Builder addRemoteInput(RemoteInput remoteInput, @SliceHint String... hints) { - mItems.add(new SliceItem(remoteInput, SliceItem.TYPE_REMOTE_INPUT, hints)); - return this; - } - - /** - * Add a color to the slice being constructed - */ - public Builder addColor(int color, @SliceHint String... hints) { - mItems.add(new SliceItem(color, SliceItem.TYPE_COLOR, hints)); - return this; - } - - /** - * Add a color to the slice being constructed - */ - public Builder addColor(int color, @SliceHint List<String> hints) { - return addColor(color, hints.toArray(new String[hints.size()])); - } - - /** - * Add a timestamp to the slice being constructed - */ - public Slice.Builder addTimestamp(long time, @SliceHint String... hints) { - mItems.add(new SliceItem(time, SliceItem.TYPE_TIMESTAMP, hints)); - return this; - } - - /** - * Add a timestamp to the slice being constructed - */ - public Slice.Builder addTimestamp(long time, @SliceHint List<String> hints) { - return addTimestamp(time, hints.toArray(new String[hints.size()])); - } - - /** - * Construct the slice. - */ - public Slice build() { - return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri); - } - } - - public static final Creator<Slice> CREATOR = new Creator<Slice>() { - @Override - public Slice createFromParcel(Parcel in) { - return new Slice(in); - } - - @Override - public Slice[] newArray(int size) { - return new Slice[size]; - } - }; - - /** - * @hide - * @return A string representation of this slice. - */ - public String toString() { - return toString(""); - } - - private String toString(String indent) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < mItems.length; i++) { - sb.append(indent); - if (mItems[i].getType() == SliceItem.TYPE_SLICE) { - sb.append("slice:\n"); - sb.append(mItems[i].getSlice().toString(indent + " ")); - } else if (mItems[i].getType() == SliceItem.TYPE_TEXT) { - sb.append("text: "); - sb.append(mItems[i].getText()); - sb.append("\n"); - } else { - sb.append(SliceItem.typeToString(mItems[i].getType())); - sb.append("\n"); - } - } - return sb.toString(); - } - - /** - * Turns a slice Uri into slice content. - * - * @param resolver ContentResolver to be used. - * @param uri The URI to a slice provider - * @return The Slice provided by the app or null if none is given. - * @see Slice - */ - public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri) { - Preconditions.checkNotNull(uri, "uri"); - IContentProvider provider = resolver.acquireProvider(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URI " + uri); - } - try { - Bundle extras = new Bundle(); - extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri); - final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE, - null, extras); - Bundle.setDefusable(res, true); - return res.getParcelable(SliceProvider.EXTRA_SLICE); - } catch (RemoteException e) { - // Arbitrary and not worth documenting, as Activity - // Manager will kill this process shortly anyway. - return null; - } finally { - resolver.releaseProvider(provider); - } - } -} diff --git a/android/app/slice/SliceItem.java b/android/app/slice/SliceItem.java deleted file mode 100644 index 6e69b051..00000000 --- a/android/app/slice/SliceItem.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (C) 2017 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.slice; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.app.PendingIntent; -import android.app.RemoteInput; -import android.graphics.drawable.Icon; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.Pair; -import android.widget.RemoteViews; - -import com.android.internal.util.ArrayUtils; - -import java.util.Arrays; -import java.util.List; - - -/** - * A SliceItem is a single unit in the tree structure of a {@link Slice}. - * - * A SliceItem a piece of content and some hints about what that content - * means or how it should be displayed. The types of content can be: - * <li>{@link #TYPE_SLICE}</li> - * <li>{@link #TYPE_TEXT}</li> - * <li>{@link #TYPE_IMAGE}</li> - * <li>{@link #TYPE_ACTION}</li> - * <li>{@link #TYPE_COLOR}</li> - * <li>{@link #TYPE_TIMESTAMP}</li> - * <li>{@link #TYPE_REMOTE_INPUT}</li> - * - * The hints that a {@link SliceItem} are a set of strings which annotate - * the content. The hints that are guaranteed to be understood by the system - * are defined on {@link Slice}. - */ -public final class SliceItem implements Parcelable { - - /** - * @hide - */ - @IntDef({TYPE_SLICE, TYPE_TEXT, TYPE_IMAGE, TYPE_ACTION, TYPE_COLOR, - TYPE_TIMESTAMP, TYPE_REMOTE_INPUT}) - public @interface SliceType {} - - /** - * A {@link SliceItem} that contains a {@link Slice} - */ - public static final int TYPE_SLICE = 1; - /** - * A {@link SliceItem} that contains a {@link CharSequence} - */ - public static final int TYPE_TEXT = 2; - /** - * A {@link SliceItem} that contains an {@link Icon} - */ - public static final int TYPE_IMAGE = 3; - /** - * A {@link SliceItem} that contains a {@link PendingIntent} - * - * Note: Actions contain 2 pieces of data, In addition to the pending intent, the - * item contains a {@link Slice} that the action applies to. - */ - public static final int TYPE_ACTION = 4; - /** - * @hide This isn't final - */ - public static final int TYPE_REMOTE_VIEW = 5; - /** - * A {@link SliceItem} that contains a Color int. - */ - public static final int TYPE_COLOR = 6; - /** - * A {@link SliceItem} that contains a timestamp. - */ - public static final int TYPE_TIMESTAMP = 8; - /** - * A {@link SliceItem} that contains a {@link RemoteInput}. - */ - public static final int TYPE_REMOTE_INPUT = 9; - - /** - * @hide - */ - protected @Slice.SliceHint - String[] mHints; - private final int mType; - private final Object mObj; - - /** - * @hide - */ - public SliceItem(Object obj, @SliceType int type, @Slice.SliceHint String[] hints) { - mHints = hints; - mType = type; - mObj = obj; - } - - /** - * @hide - */ - public SliceItem(PendingIntent intent, Slice slice, int type, @Slice.SliceHint String[] hints) { - this(new Pair<>(intent, slice), type, hints); - } - - /** - * Gets all hints associated with this SliceItem. - * @return Array of hints. - */ - public @NonNull @Slice.SliceHint List<String> getHints() { - return Arrays.asList(mHints); - } - - /** - * @hide - */ - public void addHint(@Slice.SliceHint String hint) { - mHints = ArrayUtils.appendElement(String.class, mHints, hint); - } - - /** - * @hide - */ - public void removeHint(String hint) { - ArrayUtils.removeElement(String.class, mHints, hint); - } - - public @SliceType int getType() { - return mType; - } - - /** - * @return The text held by this {@link #TYPE_TEXT} SliceItem - */ - public CharSequence getText() { - return (CharSequence) mObj; - } - - /** - * @return The icon held by this {@link #TYPE_IMAGE} SliceItem - */ - public Icon getIcon() { - return (Icon) mObj; - } - - /** - * @return The pending intent held by this {@link #TYPE_ACTION} SliceItem - */ - public PendingIntent getAction() { - return ((Pair<PendingIntent, Slice>) mObj).first; - } - - /** - * @hide This isn't final - */ - public RemoteViews getRemoteView() { - return (RemoteViews) mObj; - } - - /** - * @return The remote input held by this {@link #TYPE_REMOTE_INPUT} SliceItem - */ - public RemoteInput getRemoteInput() { - return (RemoteInput) mObj; - } - - /** - * @return The color held by this {@link #TYPE_COLOR} SliceItem - */ - public int getColor() { - return (Integer) mObj; - } - - /** - * @return The slice held by this {@link #TYPE_ACTION} or {@link #TYPE_SLICE} SliceItem - */ - public Slice getSlice() { - if (getType() == TYPE_ACTION) { - return ((Pair<PendingIntent, Slice>) mObj).second; - } - return (Slice) mObj; - } - - /** - * @return The timestamp held by this {@link #TYPE_TIMESTAMP} SliceItem - */ - public long getTimestamp() { - return (Long) mObj; - } - - /** - * @param hint The hint to check for - * @return true if this item contains the given hint - */ - public boolean hasHint(@Slice.SliceHint String hint) { - return ArrayUtils.contains(mHints, hint); - } - - /** - * @hide - */ - public SliceItem(Parcel in) { - mHints = in.readStringArray(); - mType = in.readInt(); - mObj = readObj(mType, in); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStringArray(mHints); - dest.writeInt(mType); - writeObj(dest, flags, mObj, mType); - } - - /** - * @hide - */ - public boolean hasHints(@Slice.SliceHint String[] hints) { - if (hints == null) return true; - for (String hint : hints) { - if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) { - return false; - } - } - return true; - } - - /** - * @hide - */ - public boolean hasAnyHints(@Slice.SliceHint String[] hints) { - if (hints == null) return false; - for (String hint : hints) { - if (ArrayUtils.contains(mHints, hint)) { - return true; - } - } - return false; - } - - private void writeObj(Parcel dest, int flags, Object obj, int type) { - switch (type) { - case TYPE_SLICE: - case TYPE_REMOTE_VIEW: - case TYPE_IMAGE: - case TYPE_REMOTE_INPUT: - ((Parcelable) obj).writeToParcel(dest, flags); - break; - case TYPE_ACTION: - ((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags); - ((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags); - break; - case TYPE_TEXT: - TextUtils.writeToParcel((CharSequence) mObj, dest, flags); - break; - case TYPE_COLOR: - dest.writeInt((Integer) mObj); - break; - case TYPE_TIMESTAMP: - dest.writeLong((Long) mObj); - break; - } - } - - private static Object readObj(int type, Parcel in) { - switch (type) { - case TYPE_SLICE: - return Slice.CREATOR.createFromParcel(in); - case TYPE_TEXT: - return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - case TYPE_IMAGE: - return Icon.CREATOR.createFromParcel(in); - case TYPE_ACTION: - return new Pair<PendingIntent, Slice>( - PendingIntent.CREATOR.createFromParcel(in), - Slice.CREATOR.createFromParcel(in)); - case TYPE_REMOTE_VIEW: - return RemoteViews.CREATOR.createFromParcel(in); - case TYPE_COLOR: - return in.readInt(); - case TYPE_TIMESTAMP: - return in.readLong(); - case TYPE_REMOTE_INPUT: - return RemoteInput.CREATOR.createFromParcel(in); - } - throw new RuntimeException("Unsupported type " + type); - } - - public static final Creator<SliceItem> CREATOR = new Creator<SliceItem>() { - @Override - public SliceItem createFromParcel(Parcel in) { - return new SliceItem(in); - } - - @Override - public SliceItem[] newArray(int size) { - return new SliceItem[size]; - } - }; - - /** - * @hide - */ - public static String typeToString(int type) { - switch (type) { - case TYPE_SLICE: - return "Slice"; - case TYPE_TEXT: - return "Text"; - case TYPE_IMAGE: - return "Image"; - case TYPE_ACTION: - return "Action"; - case TYPE_REMOTE_VIEW: - return "RemoteView"; - case TYPE_COLOR: - return "Color"; - case TYPE_TIMESTAMP: - return "Timestamp"; - case TYPE_REMOTE_INPUT: - return "RemoteInput"; - } - return "Unrecognized type: " + type; - } -} diff --git a/android/app/slice/SliceProvider.java b/android/app/slice/SliceProvider.java deleted file mode 100644 index df87b455..00000000 --- a/android/app/slice/SliceProvider.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2017 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.slice; - -import android.Manifest.permission; -import android.content.ContentProvider; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.ContentObserver; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.os.CancellationSignal; -import android.os.Handler; -import android.os.Looper; -import android.os.StrictMode; -import android.os.StrictMode.ThreadPolicy; -import android.util.Log; - -import java.util.concurrent.CountDownLatch; - -/** - * A SliceProvider allows app to provide content to be displayed in system - * spaces. This content is templated and can contain actions, and the behavior - * of how it is surfaced is specific to the system surface. - * - * <p>Slices are not currently live content. They are bound once and shown to the - * user. If the content changes due to a callback from user interaction, then - * {@link ContentResolver#notifyChange(Uri, ContentObserver)} - * should be used to notify the system.</p> - * - * <p>The provider needs to be declared in the manifest to provide the authority - * for the app. The authority for most slices is expected to match the package - * of the application.</p> - * <pre class="prettyprint"> - * {@literal - * <provider - * android:name="com.android.mypkg.MySliceProvider" - * android:authorities="com.android.mypkg" />} - * </pre> - * - * @see Slice - */ -public abstract class SliceProvider extends ContentProvider { - - /** - * This is the Android platform's MIME type for a slice: URI - * containing a slice implemented through {@link SliceProvider}. - */ - public static final String SLICE_TYPE = "vnd.android.slice"; - - private static final String TAG = "SliceProvider"; - /** - * @hide - */ - public static final String EXTRA_BIND_URI = "slice_uri"; - /** - * @hide - */ - public static final String METHOD_SLICE = "bind_slice"; - /** - * @hide - */ - public static final String EXTRA_SLICE = "slice"; - - private static final boolean DEBUG = false; - - /** - * Implemented to create a slice. Will be called on the main thread. - * <p> - * onBindSlice should return as quickly as possible so that the UI tied - * to this slice can be responsive. No network or other IO will be allowed - * during onBindSlice. Any loading that needs to be done should happen - * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)} - * when the app is ready to provide the complete data in onBindSlice. - * <p> - * - * @see {@link Slice}. - * @see {@link Slice#HINT_PARTIAL} - */ - // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)). - public abstract Slice onBindSlice(Uri sliceUri); - - @Override - public final int update(Uri uri, ContentValues values, String selection, - String[] selectionArgs) { - if (DEBUG) Log.d(TAG, "update " + uri); - return 0; - } - - @Override - public final int delete(Uri uri, String selection, String[] selectionArgs) { - if (DEBUG) Log.d(TAG, "delete " + uri); - return 0; - } - - @Override - public final Cursor query(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) { - if (DEBUG) Log.d(TAG, "query " + uri); - return null; - } - - @Override - public final Cursor query(Uri uri, String[] projection, String selection, String[] - selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { - if (DEBUG) Log.d(TAG, "query " + uri); - return null; - } - - @Override - public final Cursor query(Uri uri, String[] projection, Bundle queryArgs, - CancellationSignal cancellationSignal) { - if (DEBUG) Log.d(TAG, "query " + uri); - return null; - } - - @Override - public final Uri insert(Uri uri, ContentValues values) { - if (DEBUG) Log.d(TAG, "insert " + uri); - return null; - } - - @Override - public final String getType(Uri uri) { - if (DEBUG) Log.d(TAG, "getType " + uri); - return SLICE_TYPE; - } - - @Override - public Bundle call(String method, String arg, Bundle extras) { - if (method.equals(METHOD_SLICE)) { - getContext().enforceCallingPermission(permission.BIND_SLICE, - "Slice binding requires the permission BIND_SLICE"); - Uri uri = extras.getParcelable(EXTRA_BIND_URI); - - Slice s = handleBindSlice(uri); - Bundle b = new Bundle(); - b.putParcelable(EXTRA_SLICE, s); - return b; - } - return super.call(method, arg, extras); - } - - private Slice handleBindSlice(Uri sliceUri) { - Slice[] output = new Slice[1]; - CountDownLatch latch = new CountDownLatch(1); - Handler mainHandler = new Handler(Looper.getMainLooper()); - mainHandler.post(() -> { - ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); - try { - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyDeath() - .build()); - output[0] = onBindSlice(sliceUri); - } finally { - StrictMode.setThreadPolicy(oldPolicy); - latch.countDown(); - } - }); - try { - latch.await(); - return output[0]; - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } -} diff --git a/android/app/slice/SliceQuery.java b/android/app/slice/SliceQuery.java deleted file mode 100644 index d1fe2c90..00000000 --- a/android/app/slice/SliceQuery.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2017 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.slice; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import java.util.Spliterators; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -/** - * A bunch of utilities for searching the contents of a slice. - * @hide - */ -public class SliceQuery { - private static final String TAG = "SliceQuery"; - - /** - * @hide - */ - public static SliceItem findNotContaining(SliceItem container, List<SliceItem> list) { - SliceItem ret = null; - while (ret == null && list.size() != 0) { - SliceItem remove = list.remove(0); - if (!contains(container, remove)) { - ret = remove; - } - } - return ret; - } - - /** - * @hide - */ - private static boolean contains(SliceItem container, SliceItem item) { - if (container == null || item == null) return false; - return stream(container).filter(s -> (s == item)).findAny().isPresent(); - } - - /** - * @hide - */ - public static List<SliceItem> findAll(SliceItem s, int type) { - return findAll(s, type, (String[]) null, null); - } - - /** - * @hide - */ - public static List<SliceItem> findAll(SliceItem s, int type, String hints, String nonHints) { - return findAll(s, type, new String[]{ hints }, new String[]{ nonHints }); - } - - /** - * @hide - */ - public static List<SliceItem> findAll(SliceItem s, int type, String[] hints, - String[] nonHints) { - return stream(s).filter(item -> (type == -1 || item.getType() == type) - && (item.hasHints(hints) && !item.hasAnyHints(nonHints))) - .collect(Collectors.toList()); - } - - /** - * @hide - */ - public static SliceItem find(Slice s, int type, String hints, String nonHints) { - return find(s, type, new String[]{ hints }, new String[]{ nonHints }); - } - - /** - * @hide - */ - public static SliceItem find(Slice s, int type) { - return find(s, type, (String[]) null, null); - } - - /** - * @hide - */ - public static SliceItem find(SliceItem s, int type) { - return find(s, type, (String[]) null, null); - } - - /** - * @hide - */ - public static SliceItem find(SliceItem s, int type, String hints, String nonHints) { - return find(s, type, new String[]{ hints }, new String[]{ nonHints }); - } - - /** - * @hide - */ - public static SliceItem find(Slice s, int type, String[] hints, String[] nonHints) { - List<String> h = s.getHints(); - return find(new SliceItem(s, SliceItem.TYPE_SLICE, h.toArray(new String[h.size()])), type, - hints, nonHints); - } - - /** - * @hide - */ - public static SliceItem find(SliceItem s, int type, String[] hints, String[] nonHints) { - return stream(s).filter(item -> (item.getType() == type || type == -1) - && (item.hasHints(hints) && !item.hasAnyHints(nonHints))).findFirst().orElse(null); - } - - /** - * @hide - */ - public static Stream<SliceItem> stream(SliceItem slice) { - Queue<SliceItem> items = new LinkedList(); - items.add(slice); - Iterator<SliceItem> iterator = new Iterator<SliceItem>() { - @Override - public boolean hasNext() { - return items.size() != 0; - } - - @Override - public SliceItem next() { - SliceItem item = items.poll(); - if (item.getType() == SliceItem.TYPE_SLICE - || item.getType() == SliceItem.TYPE_ACTION) { - items.addAll(item.getSlice().getItems()); - } - return item; - } - }; - return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); - } -} diff --git a/android/app/slice/views/ActionRow.java b/android/app/slice/views/ActionRow.java deleted file mode 100644 index c7d99f7f..00000000 --- a/android/app/slice/views/ActionRow.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.app.PendingIntent; -import android.app.PendingIntent.CanceledException; -import android.app.RemoteInput; -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.Icon; -import android.os.AsyncTask; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewParent; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.ImageView.ScaleType; -import android.widget.LinearLayout; -import android.widget.TextView; - -/** - * @hide - */ -public class ActionRow extends FrameLayout { - - private static final int MAX_ACTIONS = 5; - private final int mSize; - private final int mIconPadding; - private final LinearLayout mActionsGroup; - private final boolean mFullActions; - private int mColor = Color.BLACK; - - public ActionRow(Context context, boolean fullActions) { - super(context); - mFullActions = fullActions; - mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, - context.getResources().getDisplayMetrics()); - mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, - context.getResources().getDisplayMetrics()); - mActionsGroup = new LinearLayout(context); - mActionsGroup.setOrientation(LinearLayout.HORIZONTAL); - mActionsGroup.setLayoutParams( - new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - addView(mActionsGroup); - } - - private void setColor(int color) { - mColor = color; - for (int i = 0; i < mActionsGroup.getChildCount(); i++) { - View view = mActionsGroup.getChildAt(i); - SliceItem item = (SliceItem) view.getTag(); - boolean tint = !item.hasHint(Slice.HINT_NO_TINT); - if (tint) { - ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor)); - } - } - } - - private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) { - ImageView imageView = new ImageView(getContext()); - imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding); - imageView.setScaleType(ScaleType.FIT_CENTER); - imageView.setImageIcon(icon); - if (allowTint) { - imageView.setImageTintList(ColorStateList.valueOf(mColor)); - } - imageView.setBackground(SliceViewUtil.getDrawable(getContext(), - android.R.attr.selectableItemBackground)); - imageView.setTag(image); - addAction(imageView); - return imageView; - } - - /** - * Set the actions and color for this action row. - */ - public void setActions(SliceItem actionRow, SliceItem defColor) { - removeAllViews(); - mActionsGroup.removeAllViews(); - addView(mActionsGroup); - - SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR); - if (color == null) { - color = defColor; - } - if (color != null) { - setColor(color.getColor()); - } - SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> { - if (mActionsGroup.getChildCount() >= MAX_ACTIONS) { - return; - } - SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE); - if (image == null) { - return; - } - boolean tint = !image.hasHint(Slice.HINT_NO_TINT); - SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT); - if (input != null && input.getRemoteInput().getAllowFreeFormInput()) { - addAction(image.getIcon(), tint, image).setOnClickListener( - v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput())); - createRemoteInputView(mColor, getContext()); - } else { - addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute( - () -> { - try { - action.getAction().send(); - } catch (CanceledException e) { - e.printStackTrace(); - } - })); - } - }); - setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE); - } - - private void addAction(View child) { - mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1)); - } - - private void createRemoteInputView(int color, Context context) { - View riv = RemoteInputView.inflate(context, this); - riv.setVisibility(View.INVISIBLE); - addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - riv.setBackgroundColor(color); - } - - private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent, - RemoteInput input) { - if (input == null) { - return false; - } - - ViewParent p = view.getParent().getParent(); - RemoteInputView riv = null; - while (p != null) { - if (p instanceof View) { - View pv = (View) p; - riv = findRemoteInputView(pv); - if (riv != null) { - break; - } - } - p = p.getParent(); - } - if (riv == null) { - return false; - } - - int width = view.getWidth(); - if (view instanceof TextView) { - // Center the reveal on the text which might be off-center from the TextView - TextView tv = (TextView) view; - if (tv.getLayout() != null) { - int innerWidth = (int) tv.getLayout().getLineWidth(0); - innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight(); - width = Math.min(width, innerWidth); - } - } - int cx = view.getLeft() + width / 2; - int cy = view.getTop() + view.getHeight() / 2; - int w = riv.getWidth(); - int h = riv.getHeight(); - int r = Math.max( - Math.max(cx + cy, cx + (h - cy)), - Math.max((w - cx) + cy, (w - cx) + (h - cy))); - - riv.setRevealParameters(cx, cy, r); - riv.setPendingIntent(pendingIntent); - riv.setRemoteInput(new RemoteInput[] { - input - }, input); - riv.focusAnimated(); - return true; - } - - private RemoteInputView findRemoteInputView(View v) { - if (v == null) { - return null; - } - return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG); - } -} diff --git a/android/app/slice/views/GridView.java b/android/app/slice/views/GridView.java deleted file mode 100644 index 6f30c507..00000000 --- a/android/app/slice/views/GridView.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; - -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.views.LargeSliceAdapter.SliceListView; -import android.content.Context; -import android.graphics.Color; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.ImageView.ScaleType; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.internal.R; - -import java.util.ArrayList; -import java.util.List; - -/** - * @hide - */ -public class GridView extends LinearLayout implements SliceListView { - - private static final String TAG = "GridView"; - - private static final int MAX_IMAGES = 3; - private static final int MAX_ALL = 5; - private boolean mIsAllImages; - - public GridView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mIsAllImages) { - int width = MeasureSpec.getSize(widthMeasureSpec); - int height = width / getChildCount(); - heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, - height); - getLayoutParams().height = height; - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).getLayoutParams().height = height; - } - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - public void setSliceItem(SliceItem slice) { - mIsAllImages = true; - removeAllViews(); - int total = 1; - if (slice.getType() == SliceItem.TYPE_SLICE) { - List<SliceItem> items = slice.getSlice().getItems(); - total = items.size(); - for (int i = 0; i < total; i++) { - SliceItem item = items.get(i); - if (isFull()) { - continue; - } - if (!addItem(item)) { - mIsAllImages = false; - } - } - } else { - if (!isFull()) { - if (!addItem(slice)) { - mIsAllImages = false; - } - } - } - if (total > getChildCount() && mIsAllImages) { - addExtraCount(total - getChildCount()); - } - } - - private void addExtraCount(int numExtra) { - View last = getChildAt(getChildCount() - 1); - FrameLayout frame = new FrameLayout(getContext()); - frame.setLayoutParams(last.getLayoutParams()); - - removeView(last); - frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT)); - - TextView v = new TextView(getContext()); - v.setTextColor(Color.WHITE); - v.setBackgroundColor(0x4d000000); - v.setText(getResources().getString(R.string.slice_more_content, numExtra)); - v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); - v.setGravity(Gravity.CENTER); - frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT)); - - addView(frame); - } - - private boolean isFull() { - return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL); - } - - /** - * Returns true if this item is just an image. - */ - private boolean addItem(SliceItem item) { - if (item.getType() == SliceItem.TYPE_IMAGE) { - ImageView v = new ImageView(getContext()); - v.setImageIcon(item.getIcon()); - v.setScaleType(ScaleType.CENTER_CROP); - addView(v, new LayoutParams(0, MATCH_PARENT, 1)); - return true; - } else { - LinearLayout v = new LinearLayout(getContext()); - int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - 12, getContext().getResources().getDisplayMetrics()); - v.setPadding(0, s, 0, 0); - v.setOrientation(LinearLayout.VERTICAL); - v.setGravity(Gravity.CENTER_HORIZONTAL); - // TODO: Unify sporadic inflates that happen throughout the code. - ArrayList<SliceItem> items = new ArrayList<>(); - if (item.getType() == SliceItem.TYPE_SLICE) { - items.addAll(item.getSlice().getItems()); - } - items.forEach(i -> { - Context context = getContext(); - switch (i.getType()) { - case SliceItem.TYPE_TEXT: - boolean title = false; - if ((item.hasAnyHints(new String[] { - Slice.HINT_LARGE, Slice.HINT_TITLE - }))) { - title = true; - } - TextView tv = (TextView) LayoutInflater.from(context).inflate( - title ? R.layout.slice_title : R.layout.slice_secondary_text, null); - tv.setText(i.getText()); - v.addView(tv); - break; - case SliceItem.TYPE_IMAGE: - ImageView iv = new ImageView(context); - iv.setImageIcon(i.getIcon()); - if (item.hasHint(Slice.HINT_LARGE)) { - iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - } else { - int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - 48, context.getResources().getDisplayMetrics()); - iv.setLayoutParams(new LayoutParams(size, size)); - } - v.addView(iv); - break; - case SliceItem.TYPE_REMOTE_VIEW: - v.addView(i.getRemoteView().apply(context, v)); - break; - case SliceItem.TYPE_COLOR: - // TODO: Support color to tint stuff here. - break; - } - }); - addView(v, new LayoutParams(0, WRAP_CONTENT, 1)); - return false; - } - } -} diff --git a/android/app/slice/views/LargeSliceAdapter.java b/android/app/slice/views/LargeSliceAdapter.java deleted file mode 100644 index 6794ff98..00000000 --- a/android/app/slice/views/LargeSliceAdapter.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; -import android.app.slice.views.LargeSliceAdapter.SliceViewHolder; -import android.content.Context; -import android.util.ArrayMap; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.widget.FrameLayout; - -import com.android.internal.R; -import com.android.internal.widget.RecyclerView; -import com.android.internal.widget.RecyclerView.ViewHolder; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @hide - */ -public class LargeSliceAdapter extends RecyclerView.Adapter<SliceViewHolder> { - - public static final int TYPE_DEFAULT = 1; - public static final int TYPE_HEADER = 2; - public static final int TYPE_GRID = 3; - public static final int TYPE_MESSAGE = 4; - public static final int TYPE_MESSAGE_LOCAL = 5; - public static final int TYPE_REMOTE_VIEWS = 6; - - private final IdGenerator mIdGen = new IdGenerator(); - private final Context mContext; - private List<SliceWrapper> mSlices = new ArrayList<>(); - private SliceItem mColor; - - public LargeSliceAdapter(Context context) { - mContext = context; - setHasStableIds(true); - } - - /** - * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color. - */ - public void setSliceItems(List<SliceItem> slices, SliceItem color) { - mColor = color; - mIdGen.resetUsage(); - mSlices = slices.stream().map(s -> new SliceWrapper(s, mIdGen)) - .collect(Collectors.toList()); - notifyDataSetChanged(); - } - - @Override - public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View v = inflateforType(viewType); - v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - return new SliceViewHolder(v); - } - - @Override - public int getItemViewType(int position) { - return mSlices.get(position).mType; - } - - @Override - public long getItemId(int position) { - return mSlices.get(position).mId; - } - - @Override - public int getItemCount() { - return mSlices.size(); - } - - @Override - public void onBindViewHolder(SliceViewHolder holder, int position) { - SliceWrapper slice = mSlices.get(position); - if (holder.mSliceView != null) { - holder.mSliceView.setColor(mColor); - holder.mSliceView.setSliceItem(slice.mItem); - } else if (slice.mType == TYPE_REMOTE_VIEWS) { - FrameLayout frame = (FrameLayout) holder.itemView; - frame.removeAllViews(); - frame.addView(slice.mItem.getRemoteView().apply(mContext, frame)); - } - } - - private View inflateforType(int viewType) { - switch (viewType) { - case TYPE_REMOTE_VIEWS: - return new FrameLayout(mContext); - case TYPE_GRID: - return LayoutInflater.from(mContext).inflate(R.layout.slice_grid, null); - case TYPE_MESSAGE: - return LayoutInflater.from(mContext).inflate(R.layout.slice_message, null); - case TYPE_MESSAGE_LOCAL: - return LayoutInflater.from(mContext).inflate(R.layout.slice_message_local, null); - } - return new SmallTemplateView(mContext); - } - - protected static class SliceWrapper { - private final SliceItem mItem; - private final int mType; - private final long mId; - - public SliceWrapper(SliceItem item, IdGenerator idGen) { - mItem = item; - mType = getType(item); - mId = idGen.getId(item); - } - - public static int getType(SliceItem item) { - if (item.getType() == SliceItem.TYPE_REMOTE_VIEW) { - return TYPE_REMOTE_VIEWS; - } - if (item.hasHint(Slice.HINT_MESSAGE)) { - // TODO: Better way to determine me or not? Something more like Messaging style. - if (SliceQuery.find(item, -1, Slice.HINT_SOURCE, null) != null) { - return TYPE_MESSAGE; - } else { - return TYPE_MESSAGE_LOCAL; - } - } - if (item.hasHint(Slice.HINT_HORIZONTAL)) { - return TYPE_GRID; - } - return TYPE_DEFAULT; - } - } - - /** - * A {@link ViewHolder} for presenting slices in {@link LargeSliceAdapter}. - */ - public static class SliceViewHolder extends ViewHolder { - public final SliceListView mSliceView; - - public SliceViewHolder(View itemView) { - super(itemView); - mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null; - } - } - - /** - * View slices being displayed in {@link LargeSliceAdapter}. - */ - public interface SliceListView { - /** - * Set the slice item for this view. - */ - void setSliceItem(SliceItem slice); - - /** - * Set the color for the items in this view. - */ - default void setColor(SliceItem color) { - - } - } - - private static class IdGenerator { - private long mNextLong = 0; - private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>(); - private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>(); - - public long getId(SliceItem item) { - String str = genString(item); - if (!mCurrentIds.containsKey(str)) { - mCurrentIds.put(str, mNextLong++); - } - long id = mCurrentIds.get(str); - int index = mUsedIds.getOrDefault(str, 0); - mUsedIds.put(str, index + 1); - return id + index * 10000; - } - - private String genString(SliceItem item) { - StringBuilder builder = new StringBuilder(); - SliceQuery.stream(item).forEach(i -> { - builder.append(i.getType()); - i.removeHint(Slice.HINT_SELECTED); - builder.append(i.getHints()); - switch (i.getType()) { - case SliceItem.TYPE_REMOTE_VIEW: - builder.append(i.getRemoteView()); - break; - case SliceItem.TYPE_IMAGE: - builder.append(i.getIcon()); - break; - case SliceItem.TYPE_TEXT: - builder.append(i.getText()); - break; - case SliceItem.TYPE_COLOR: - builder.append(i.getColor()); - break; - } - }); - return builder.toString(); - } - - public void resetUsage() { - mUsedIds.clear(); - } - } -} diff --git a/android/app/slice/views/LargeTemplateView.java b/android/app/slice/views/LargeTemplateView.java deleted file mode 100644 index 9e225162..00000000 --- a/android/app/slice/views/LargeTemplateView.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; - -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; -import android.app.slice.views.SliceView.SliceModeView; -import android.content.Context; -import android.util.TypedValue; - -import com.android.internal.widget.LinearLayoutManager; -import com.android.internal.widget.RecyclerView; - -import java.util.ArrayList; -import java.util.List; - -/** - * @hide - */ -public class LargeTemplateView extends SliceModeView { - private final LargeSliceAdapter mAdapter; - private final RecyclerView mRecyclerView; - private final int mDefaultHeight; - private final int mMaxHeight; - private Slice mSlice; - - public LargeTemplateView(Context context) { - super(context); - - mRecyclerView = new RecyclerView(getContext()); - mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - mAdapter = new LargeSliceAdapter(context); - mRecyclerView.setAdapter(mAdapter); - addView(mRecyclerView); - int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, - getResources().getDisplayMetrics()); - setLayoutParams(new LayoutParams(width, WRAP_CONTENT)); - mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, - getResources().getDisplayMetrics()); - mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, - getResources().getDisplayMetrics()); - } - - @Override - public String getMode() { - return SliceView.MODE_LARGE; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - mRecyclerView.getLayoutParams().height = WRAP_CONTENT; - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (mRecyclerView.getMeasuredHeight() > mMaxHeight - || mSlice.hasHint(Slice.HINT_PARTIAL)) { - mRecyclerView.getLayoutParams().height = mDefaultHeight; - } else { - mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight(); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - public void setSlice(Slice slice) { - SliceItem color = SliceQuery.find(slice, SliceItem.TYPE_COLOR); - mSlice = slice; - List<SliceItem> items = new ArrayList<>(); - boolean[] hasHeader = new boolean[1]; - if (slice.hasHint(Slice.HINT_LIST)) { - addList(slice, items); - } else { - slice.getItems().forEach(item -> { - if (item.hasHint(Slice.HINT_ACTIONS)) { - return; - } else if (item.getType() == SliceItem.TYPE_COLOR) { - return; - } else if (item.getType() == SliceItem.TYPE_SLICE - && item.hasHint(Slice.HINT_LIST)) { - addList(item.getSlice(), items); - } else if (item.hasHint(Slice.HINT_LIST_ITEM)) { - items.add(item); - } else if (!hasHeader[0]) { - hasHeader[0] = true; - items.add(0, item); - } else { - item.addHint(Slice.HINT_LIST_ITEM); - items.add(item); - } - }); - } - mAdapter.setSliceItems(items, color); - } - - private void addList(Slice slice, List<SliceItem> items) { - List<SliceItem> sliceItems = slice.getItems(); - sliceItems.forEach(i -> i.addHint(Slice.HINT_LIST_ITEM)); - items.addAll(sliceItems); - } -} diff --git a/android/app/slice/views/MessageView.java b/android/app/slice/views/MessageView.java deleted file mode 100644 index 77252bf2..00000000 --- a/android/app/slice/views/MessageView.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; -import android.app.slice.views.LargeSliceAdapter.SliceListView; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.text.SpannableStringBuilder; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -/** - * @hide - */ -public class MessageView extends LinearLayout implements SliceListView { - - private TextView mDetails; - private ImageView mIcon; - - public MessageView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mDetails = findViewById(android.R.id.summary); - mIcon = findViewById(android.R.id.icon); - } - - @Override - public void setSliceItem(SliceItem slice) { - SliceItem source = SliceQuery.find(slice, SliceItem.TYPE_IMAGE, Slice.HINT_SOURCE, null); - if (source != null) { - final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - 24, getContext().getResources().getDisplayMetrics()); - // TODO try and turn this into a drawable - Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888); - Canvas iconCanvas = new Canvas(iconBm); - Drawable d = source.getIcon().loadDrawable(getContext()); - d.setBounds(0, 0, iconSize, iconSize); - d.draw(iconCanvas); - mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm)); - } - SpannableStringBuilder builder = new SpannableStringBuilder(); - SliceQuery.findAll(slice, SliceItem.TYPE_TEXT).forEach(text -> { - if (builder.length() != 0) { - builder.append('\n'); - } - builder.append(text.getText()); - }); - mDetails.setText(builder.toString()); - } - -} diff --git a/android/app/slice/views/RemoteInputView.java b/android/app/slice/views/RemoteInputView.java deleted file mode 100644 index e53cb1ea..00000000 --- a/android/app/slice/views/RemoteInputView.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.animation.Animator; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.RemoteInput; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ShortcutManager; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewAnimationUtils; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; -import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.internal.R; - -/** - * Host for the remote input. - * - * @hide - */ -// TODO this should be unified with SystemUI RemoteInputView (b/67527720) -public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher { - - private static final String TAG = "RemoteInput"; - - /** - * A marker object that let's us easily find views of this class. - */ - public static final Object VIEW_TAG = new Object(); - - private RemoteEditText mEditText; - private ImageButton mSendButton; - private ProgressBar mProgressBar; - private PendingIntent mPendingIntent; - private RemoteInput[] mRemoteInputs; - private RemoteInput mRemoteInput; - - private int mRevealCx; - private int mRevealCy; - private int mRevealR; - private boolean mResetting; - - public RemoteInputView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - mProgressBar = findViewById(R.id.remote_input_progress); - mSendButton = findViewById(R.id.remote_input_send); - mSendButton.setOnClickListener(this); - - mEditText = (RemoteEditText) getChildAt(0); - mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - final boolean isSoftImeEvent = event == null - && (actionId == EditorInfo.IME_ACTION_DONE - || actionId == EditorInfo.IME_ACTION_NEXT - || actionId == EditorInfo.IME_ACTION_SEND); - final boolean isKeyboardEnterKey = event != null - && KeyEvent.isConfirmKey(event.getKeyCode()) - && event.getAction() == KeyEvent.ACTION_DOWN; - - if (isSoftImeEvent || isKeyboardEnterKey) { - if (mEditText.length() > 0) { - sendRemoteInput(); - } - // Consume action to prevent IME from closing. - return true; - } - return false; - } - }); - mEditText.addTextChangedListener(this); - mEditText.setInnerFocusable(false); - mEditText.mRemoteInputView = this; - } - - private void sendRemoteInput() { - Bundle results = new Bundle(); - results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString()); - Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent, - results); - - mEditText.setEnabled(false); - mSendButton.setVisibility(INVISIBLE); - mProgressBar.setVisibility(VISIBLE); - mEditText.mShowImeOnInputConnection = false; - - // Tell ShortcutManager that this package has been "activated". ShortcutManager - // will reset the throttling for this package. - // Strictly speaking, the intent receiver may be different from the intent creator, - // but that's an edge case, and also because we can't always know which package will receive - // an intent, so we just reset for the creator. - getContext().getSystemService(ShortcutManager.class).onApplicationActive( - mPendingIntent.getCreatorPackage(), - getContext().getUserId()); - - try { - mPendingIntent.send(mContext, 0, fillInIntent); - reset(); - } catch (PendingIntent.CanceledException e) { - Log.i(TAG, "Unable to send remote input result", e); - Toast.makeText(mContext, "Failure sending pending intent for inline reply :(", - Toast.LENGTH_SHORT).show(); - reset(); - } - } - - /** - * Creates a remote input view. - */ - public static RemoteInputView inflate(Context context, ViewGroup root) { - RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate( - R.layout.slice_remote_input, root, false); - v.setTag(VIEW_TAG); - return v; - } - - @Override - public void onClick(View v) { - if (v == mSendButton) { - sendRemoteInput(); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - - // We never want for a touch to escape to an outer view or one we covered. - return true; - } - - private void onDefocus() { - setVisibility(INVISIBLE); - } - - /** - * Set the pending intent for remote input. - */ - public void setPendingIntent(PendingIntent pendingIntent) { - mPendingIntent = pendingIntent; - } - - /** - * Set the remote inputs for this view. - */ - public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) { - mRemoteInputs = remoteInputs; - mRemoteInput = remoteInput; - mEditText.setHint(mRemoteInput.getLabel()); - } - - /** - * Focuses the remote input view. - */ - public void focusAnimated() { - if (getVisibility() != VISIBLE) { - Animator animator = ViewAnimationUtils.createCircularReveal( - this, mRevealCx, mRevealCy, 0, mRevealR); - animator.setDuration(200); - animator.start(); - } - focus(); - } - - private void focus() { - setVisibility(VISIBLE); - mEditText.setInnerFocusable(true); - mEditText.mShowImeOnInputConnection = true; - mEditText.setSelection(mEditText.getText().length()); - mEditText.requestFocus(); - updateSendButton(); - } - - private void reset() { - mResetting = true; - - mEditText.getText().clear(); - mEditText.setEnabled(true); - mSendButton.setVisibility(VISIBLE); - mProgressBar.setVisibility(INVISIBLE); - updateSendButton(); - onDefocus(); - - mResetting = false; - } - - @Override - public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { - if (mResetting && child == mEditText) { - // Suppress text events if it happens during resetting. Ideally this would be - // suppressed by the text view not being shown, but that doesn't work here because it - // needs to stay visible for the animation. - return false; - } - return super.onRequestSendAccessibilityEvent(child, event); - } - - private void updateSendButton() { - mSendButton.setEnabled(mEditText.getText().length() != 0); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - updateSendButton(); - } - - /** - * Tries to find an action that matches the current pending intent of this view and updates its - * state to that of the found action - * - * @return true if a matching action was found, false otherwise - */ - public boolean updatePendingIntentFromActions(Notification.Action[] actions) { - if (mPendingIntent == null || actions == null) { - return false; - } - Intent current = mPendingIntent.getIntent(); - if (current == null) { - return false; - } - - for (Notification.Action a : actions) { - RemoteInput[] inputs = a.getRemoteInputs(); - if (a.actionIntent == null || inputs == null) { - continue; - } - Intent candidate = a.actionIntent.getIntent(); - if (!current.filterEquals(candidate)) { - continue; - } - - RemoteInput input = null; - for (RemoteInput i : inputs) { - if (i.getAllowFreeFormInput()) { - input = i; - } - } - if (input == null) { - continue; - } - setPendingIntent(a.actionIntent); - setRemoteInput(inputs, input); - return true; - } - return false; - } - - /** - * @hide - */ - public void setRevealParameters(int cx, int cy, int r) { - mRevealCx = cx; - mRevealCy = cy; - mRevealR = r; - } - - @Override - public void dispatchStartTemporaryDetach() { - super.dispatchStartTemporaryDetach(); - // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and - // won't lose IME focus. - detachViewFromParent(mEditText); - } - - @Override - public void dispatchFinishTemporaryDetach() { - if (isAttachedToWindow()) { - attachViewToParent(mEditText, 0, mEditText.getLayoutParams()); - } else { - removeDetachedView(mEditText, false /* animate */); - } - super.dispatchFinishTemporaryDetach(); - } - - /** - * An EditText that changes appearance based on whether it's focusable and becomes un-focusable - * whenever the user navigates away from it or it becomes invisible. - */ - public static class RemoteEditText extends EditText { - - private final Drawable mBackground; - private RemoteInputView mRemoteInputView; - boolean mShowImeOnInputConnection; - - public RemoteEditText(Context context, AttributeSet attrs) { - super(context, attrs); - mBackground = getBackground(); - } - - private void defocusIfNeeded(boolean animate) { - if (mRemoteInputView != null || isTemporarilyDetached()) { - if (isTemporarilyDetached()) { - // We might get reattached but then the other one of HUN / expanded might steal - // our focus, so we'll need to save our text here. - } - return; - } - if (isFocusable() && isEnabled()) { - setInnerFocusable(false); - if (mRemoteInputView != null) { - mRemoteInputView.onDefocus(); - } - mShowImeOnInputConnection = false; - } - } - - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - - if (!isShown()) { - defocusIfNeeded(false /* animate */); - } - } - - @Override - protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { - super.onFocusChanged(focused, direction, previouslyFocusedRect); - if (!focused) { - defocusIfNeeded(true /* animate */); - } - } - - @Override - public void getFocusedRect(Rect r) { - super.getFocusedRect(r); - r.top = mScrollY; - r.bottom = mScrollY + (mBottom - mTop); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - // Eat the DOWN event here to prevent any default behavior. - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - defocusIfNeeded(true /* animate */); - return true; - } - return super.onKeyUp(keyCode, event); - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); - - if (mShowImeOnInputConnection && inputConnection != null) { - final InputMethodManager imm = InputMethodManager.getInstance(); - if (imm != null) { - // onCreateInputConnection is called by InputMethodManager in the middle of - // setting up the connection to the IME; wait with requesting the IME until that - // work has completed. - post(new Runnable() { - @Override - public void run() { - imm.viewClicked(RemoteEditText.this); - imm.showSoftInput(RemoteEditText.this, 0); - } - }); - } - } - - return inputConnection; - } - - @Override - public void onCommitCompletion(CompletionInfo text) { - clearComposingText(); - setText(text.getText()); - setSelection(getText().length()); - } - - void setInnerFocusable(boolean focusable) { - setFocusableInTouchMode(focusable); - setFocusable(focusable); - setCursorVisible(focusable); - - if (focusable) { - requestFocus(); - setBackground(mBackground); - } else { - setBackground(null); - } - - } - } -} diff --git a/android/app/slice/views/ShortcutView.java b/android/app/slice/views/ShortcutView.java deleted file mode 100644 index b6790c7d..00000000 --- a/android/app/slice/views/ShortcutView.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.app.PendingIntent; -import android.app.PendingIntent.CanceledException; -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; -import android.app.slice.views.SliceView.SliceModeView; -import android.content.Context; -import android.content.Intent; -import android.graphics.Color; -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.OvalShape; -import android.net.Uri; -import android.view.ViewGroup; - -import com.android.internal.R; - -/** - * @hide - */ -public class ShortcutView extends SliceModeView { - - private static final String TAG = "ShortcutView"; - - private PendingIntent mAction; - private Uri mUri; - private int mLargeIconSize; - private int mSmallIconSize; - - public ShortcutView(Context context) { - super(context); - mLargeIconSize = getContext().getResources() - .getDimensionPixelSize(R.dimen.slice_shortcut_size); - mSmallIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size); - setLayoutParams(new ViewGroup.LayoutParams(mLargeIconSize, mLargeIconSize)); - } - - @Override - public void setSlice(Slice slice) { - removeAllViews(); - SliceItem sliceItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION); - SliceItem iconItem = slice.getPrimaryIcon(); - SliceItem textItem = sliceItem != null - ? SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT) - : SliceQuery.find(slice, SliceItem.TYPE_TEXT); - SliceItem colorItem = sliceItem != null - ? SliceQuery.find(sliceItem, SliceItem.TYPE_COLOR) - : SliceQuery.find(slice, SliceItem.TYPE_COLOR); - if (colorItem == null) { - colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR); - } - // TODO: pick better default colour - final int color = colorItem != null ? colorItem.getColor() : Color.GRAY; - ShapeDrawable circle = new ShapeDrawable(new OvalShape()); - circle.setTint(color); - setBackground(circle); - if (iconItem != null) { - final boolean isLarge = iconItem.hasHint(Slice.HINT_LARGE); - final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize; - SliceViewUtil.createCircledIcon(getContext(), color, iconSize, iconItem.getIcon(), - isLarge, this /* parent */); - mAction = sliceItem != null ? sliceItem.getAction() - : null; - mUri = slice.getUri(); - setClickable(true); - } else { - setClickable(false); - } - } - - @Override - public String getMode() { - return SliceView.MODE_SHORTCUT; - } - - @Override - public boolean performClick() { - if (!callOnClick()) { - try { - if (mAction != null) { - mAction.send(); - } else { - Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getContext().startActivity(intent); - } - } catch (CanceledException e) { - e.printStackTrace(); - } - } - return true; - } -} diff --git a/android/app/slice/views/SliceView.java b/android/app/slice/views/SliceView.java deleted file mode 100644 index 32484fca..00000000 --- a/android/app/slice/views/SliceView.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.annotation.StringDef; -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.graphics.drawable.ColorDrawable; -import android.net.Uri; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.LinearLayout; - -import java.util.List; - -/** - * A view that can display a {@link Slice} in different {@link SliceMode}'s. - * - * @hide - */ -public class SliceView extends LinearLayout { - - private static final String TAG = "SliceView"; - - /** - * @hide - */ - public abstract static class SliceModeView extends FrameLayout { - - public SliceModeView(Context context) { - super(context); - } - - /** - * @return the {@link SliceMode} of the slice being presented. - */ - public abstract String getMode(); - - /** - * @param slice the slice to show in this view. - */ - public abstract void setSlice(Slice slice); - } - - /** - * @hide - */ - @StringDef({ - MODE_SMALL, MODE_LARGE, MODE_SHORTCUT - }) - public @interface SliceMode {} - - /** - * Mode indicating this slice should be presented in small template format. - */ - public static final String MODE_SMALL = "SLICE_SMALL"; - /** - * Mode indicating this slice should be presented in large template format. - */ - public static final String MODE_LARGE = "SLICE_LARGE"; - /** - * Mode indicating this slice should be presented as an icon. - */ - public static final String MODE_SHORTCUT = "SLICE_ICON"; - - /** - * Will select the type of slice binding based on size of the View. TODO: Put in some info about - * that selection. - */ - private static final String MODE_AUTO = "auto"; - - private String mMode = MODE_AUTO; - private SliceModeView mCurrentView; - private final ActionRow mActions; - private Slice mCurrentSlice; - private boolean mShowActions = true; - - /** - * Simple constructor to create a slice view from code. - * - * @param context The context the view is running in. - */ - public SliceView(Context context) { - super(context); - setOrientation(LinearLayout.VERTICAL); - mActions = new ActionRow(mContext, true); - mActions.setBackground(new ColorDrawable(0xffeeeeee)); - mCurrentView = new LargeTemplateView(mContext); - addView(mCurrentView); - addView(mActions); - } - - /** - * @hide - */ - public void bindSlice(Intent intent) { - // TODO - } - - /** - * Binds this view to the {@link Slice} associated with the provided {@link Uri}. - */ - public void bindSlice(Uri sliceUri) { - validate(sliceUri); - Slice s = Slice.bindSlice(mContext.getContentResolver(), sliceUri); - bindSlice(s); - } - - /** - * Binds this view to the provided {@link Slice}. - */ - public void bindSlice(Slice slice) { - mCurrentSlice = slice; - if (mCurrentSlice != null) { - reinflate(); - } - } - - /** - * Call to clean up the view. - */ - public void unbindSlice() { - mCurrentSlice = null; - } - - /** - * Set the {@link SliceMode} this view should present in. - */ - public void setMode(@SliceMode String mode) { - setMode(mode, false /* animate */); - } - - /** - * @hide - */ - public void setMode(@SliceMode String mode, boolean animate) { - if (animate) { - Log.e(TAG, "Animation not supported yet"); - } - mMode = mode; - reinflate(); - } - - /** - * @return the {@link SliceMode} this view is presenting in. - */ - public @SliceMode String getMode() { - if (mMode.equals(MODE_AUTO)) { - return MODE_LARGE; - } - return mMode; - } - - /** - * @hide - * - * Whether this view should show a row of actions with it. - */ - public void setShowActionRow(boolean show) { - mShowActions = show; - reinflate(); - } - - private SliceModeView createView(String mode) { - switch (mode) { - case MODE_SHORTCUT: - return new ShortcutView(getContext()); - case MODE_SMALL: - return new SmallTemplateView(getContext()); - } - return new LargeTemplateView(getContext()); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - unbindSlice(); - } - - private void reinflate() { - if (mCurrentSlice == null) { - return; - } - // TODO: Smarter mapping here from one state to the next. - SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR); - List<SliceItem> items = mCurrentSlice.getItems(); - SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE, - Slice.HINT_ACTIONS, - Slice.HINT_ALT); - String mode = getMode(); - if (!mode.equals(mCurrentView.getMode())) { - removeAllViews(); - mCurrentView = createView(mode); - addView(mCurrentView); - addView(mActions); - } - if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) { - mCurrentView.setVisibility(View.VISIBLE); - mCurrentView.setSlice(mCurrentSlice); - } else { - mCurrentView.setVisibility(View.GONE); - } - - boolean showActions = mShowActions && actionRow != null - && !mode.equals(MODE_SHORTCUT); - if (showActions) { - mActions.setActions(actionRow, color); - mActions.setVisibility(View.VISIBLE); - } else { - mActions.setVisibility(View.GONE); - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - // TODO -- may need to rethink for AGSA - if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { - requestDisallowInterceptTouchEvent(true); - } - return super.onInterceptTouchEvent(ev); - } - - private static void validate(Uri sliceUri) { - if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) { - throw new RuntimeException("Invalid uri " + sliceUri); - } - if (sliceUri.getPathSegments().size() == 0) { - throw new RuntimeException("Invalid uri " + sliceUri); - } - } -} diff --git a/android/app/slice/views/SliceViewUtil.java b/android/app/slice/views/SliceViewUtil.java deleted file mode 100644 index 19e8e7c9..00000000 --- a/android/app/slice/views/SliceViewUtil.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.annotation.ColorInt; -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PorterDuff.Mode; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; -import android.view.Gravity; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; - -/** - * A bunch of utilities for slice UI. - * - * @hide - */ -public class SliceViewUtil { - - /** - * @hide - */ - @ColorInt - public static int getColorAccent(Context context) { - return getColorAttr(context, android.R.attr.colorAccent); - } - - /** - * @hide - */ - @ColorInt - public static int getColorError(Context context) { - return getColorAttr(context, android.R.attr.colorError); - } - - /** - * @hide - */ - @ColorInt - public static int getDefaultColor(Context context, int resId) { - final ColorStateList list = context.getResources().getColorStateList(resId, - context.getTheme()); - - return list.getDefaultColor(); - } - - /** - * @hide - */ - @ColorInt - public static int getDisabled(Context context, int inputColor) { - return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor); - } - - /** - * @hide - */ - @ColorInt - public static int applyAlphaAttr(Context context, int attr, int inputColor) { - TypedArray ta = context.obtainStyledAttributes(new int[] { - attr - }); - float alpha = ta.getFloat(0, 0); - ta.recycle(); - return applyAlpha(alpha, inputColor); - } - - /** - * @hide - */ - @ColorInt - public static int applyAlpha(float alpha, int inputColor) { - alpha *= Color.alpha(inputColor); - return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor), - Color.blue(inputColor)); - } - - /** - * @hide - */ - @ColorInt - public static int getColorAttr(Context context, int attr) { - TypedArray ta = context.obtainStyledAttributes(new int[] { - attr - }); - @ColorInt - int colorAccent = ta.getColor(0, 0); - ta.recycle(); - return colorAccent; - } - - /** - * @hide - */ - public static int getThemeAttr(Context context, int attr) { - TypedArray ta = context.obtainStyledAttributes(new int[] { - attr - }); - int theme = ta.getResourceId(0, 0); - ta.recycle(); - return theme; - } - - /** - * @hide - */ - public static Drawable getDrawable(Context context, int attr) { - TypedArray ta = context.obtainStyledAttributes(new int[] { - attr - }); - Drawable drawable = ta.getDrawable(0); - ta.recycle(); - return drawable; - } - - /** - * @hide - */ - public static void createCircledIcon(Context context, int color, int iconSize, Icon icon, - boolean isLarge, ViewGroup parent) { - ImageView v = new ImageView(context); - v.setImageIcon(icon); - parent.addView(v); - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); - if (isLarge) { - // XXX better way to convert from icon -> bitmap or crop an icon (?) - Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888); - Canvas iconCanvas = new Canvas(iconBm); - v.layout(0, 0, iconSize, iconSize); - v.draw(iconCanvas); - v.setImageBitmap(getCircularBitmap(iconBm)); - } else { - v.setColorFilter(Color.WHITE); - } - lp.width = iconSize; - lp.height = iconSize; - lp.gravity = Gravity.CENTER; - } - - /** - * @hide - */ - public static Bitmap getCircularBitmap(Bitmap bitmap) { - Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), - bitmap.getHeight(), Config.ARGB_8888); - Canvas canvas = new Canvas(output); - final Paint paint = new Paint(); - final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); - paint.setAntiAlias(true); - canvas.drawARGB(0, 0, 0, 0); - canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, - bitmap.getWidth() / 2, paint); - paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); - canvas.drawBitmap(bitmap, rect, rect, paint); - return output; - } -} diff --git a/android/app/slice/views/SmallTemplateView.java b/android/app/slice/views/SmallTemplateView.java deleted file mode 100644 index 42b2d213..00000000 --- a/android/app/slice/views/SmallTemplateView.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2017 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.slice.views; - -import android.app.PendingIntent.CanceledException; -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; -import android.app.slice.views.LargeSliceAdapter.SliceListView; -import android.app.slice.views.SliceView.SliceModeView; -import android.content.Context; -import android.os.AsyncTask; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.internal.R; - -import java.text.Format; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * Small template is also used to construct list items for use with {@link LargeTemplateView}. - * - * @hide - */ -public class SmallTemplateView extends SliceModeView implements SliceListView { - - private static final String TAG = "SmallTemplateView"; - - private int mIconSize; - private int mPadding; - - private LinearLayout mStartContainer; - private TextView mTitleText; - private TextView mSecondaryText; - private LinearLayout mEndContainer; - - public SmallTemplateView(Context context) { - super(context); - inflate(context, R.layout.slice_small_template, this); - mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size); - mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.slice_padding); - - mStartContainer = (LinearLayout) findViewById(android.R.id.icon_frame); - mTitleText = (TextView) findViewById(android.R.id.title); - mSecondaryText = (TextView) findViewById(android.R.id.summary); - mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame); - } - - @Override - public String getMode() { - return SliceView.MODE_SMALL; - } - - @Override - public void setSliceItem(SliceItem slice) { - resetViews(); - SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR); - int color = colorItem != null ? colorItem.getColor() : -1; - - // Look for any title elements - List<SliceItem> titleItems = SliceQuery.findAll(slice, -1, Slice.HINT_TITLE, - null); - boolean hasTitleText = false; - boolean hasTitleItem = false; - for (int i = 0; i < titleItems.size(); i++) { - SliceItem item = titleItems.get(i); - if (!hasTitleItem) { - // icon, action icon, or timestamp - if (item.getType() == SliceItem.TYPE_ACTION) { - hasTitleItem = addIcon(item, color, mStartContainer); - } else if (item.getType() == SliceItem.TYPE_IMAGE) { - addIcon(item, color, mStartContainer); - hasTitleItem = true; - } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) { - TextView tv = new TextView(getContext()); - tv.setText(convertTimeToString(item.getTimestamp())); - hasTitleItem = true; - } - } - if (!hasTitleText && item.getType() == SliceItem.TYPE_TEXT) { - mTitleText.setText(item.getText()); - hasTitleText = true; - } - if (hasTitleText && hasTitleItem) { - break; - } - } - mTitleText.setVisibility(hasTitleText ? View.VISIBLE : View.GONE); - mStartContainer.setVisibility(hasTitleItem ? View.VISIBLE : View.GONE); - - if (slice.getType() != SliceItem.TYPE_SLICE) { - return; - } - - // Deal with remaining items - int itemCount = 0; - boolean hasSummary = false; - ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>( - slice.getSlice().getItems()); - for (int i = 0; i < sliceItems.size(); i++) { - SliceItem item = sliceItems.get(i); - if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT - && !item.hasHint(Slice.HINT_TITLE)) { - // TODO -- Should combine all text items? - mSecondaryText.setText(item.getText()); - hasSummary = true; - } - if (itemCount <= 3) { - if (item.getType() == SliceItem.TYPE_ACTION) { - if (addIcon(item, color, mEndContainer)) { - itemCount++; - } - } else if (item.getType() == SliceItem.TYPE_IMAGE) { - addIcon(item, color, mEndContainer); - itemCount++; - } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) { - TextView tv = new TextView(getContext()); - tv.setText(convertTimeToString(item.getTimestamp())); - mEndContainer.addView(tv); - itemCount++; - } else if (item.getType() == SliceItem.TYPE_SLICE) { - List<SliceItem> subItems = item.getSlice().getItems(); - for (int j = 0; j < subItems.size(); j++) { - sliceItems.add(subItems.get(j)); - } - } - } - } - } - - @Override - public void setSlice(Slice slice) { - setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE, - slice.getHints().toArray(new String[slice.getHints().size()]))); - } - - /** - * @return Whether an icon was added. - */ - private boolean addIcon(SliceItem sliceItem, int color, LinearLayout container) { - SliceItem image = null; - SliceItem action = null; - if (sliceItem.getType() == SliceItem.TYPE_ACTION) { - image = SliceQuery.find(sliceItem.getSlice(), SliceItem.TYPE_IMAGE); - action = sliceItem; - } else if (sliceItem.getType() == SliceItem.TYPE_IMAGE) { - image = sliceItem; - } - if (image != null) { - ImageView iv = new ImageView(getContext()); - iv.setImageIcon(image.getIcon()); - if (action != null) { - final SliceItem sliceAction = action; - iv.setOnClickListener(v -> AsyncTask.execute( - () -> { - try { - sliceAction.getAction().send(); - } catch (CanceledException e) { - e.printStackTrace(); - } - })); - iv.setBackground(SliceViewUtil.getDrawable(getContext(), - android.R.attr.selectableItemBackground)); - } - if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) { - iv.setColorFilter(color); - } - container.addView(iv); - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams(); - lp.width = mIconSize; - lp.height = mIconSize; - lp.setMarginStart(mPadding); - return true; - } - return false; - } - - private String convertTimeToString(long time) { - // TODO -- figure out what format(s) we support - Date date = new Date(time); - Format format = new SimpleDateFormat("MM dd yyyy HH:mm:ss"); - return format.format(date); - } - - private void resetViews() { - mStartContainer.removeAllViews(); - mEndContainer.removeAllViews(); - mTitleText.setText(null); - mSecondaryText.setText(null); - } -} diff --git a/android/app/usage/UsageStatsManager.java b/android/app/usage/UsageStatsManager.java index fd579fce..051dccbd 100644 --- a/android/app/usage/UsageStatsManager.java +++ b/android/app/usage/UsageStatsManager.java @@ -48,10 +48,10 @@ import java.util.Map; * </pre> * A request for data in the middle of a time interval will include that interval. * <p/> - * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS. - * However, declaring the permission implies intention to use the API and the user of the device - * still needs to grant permission through the Settings application. - * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS} + * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS, which + * is a system-level permission and will not be granted to third-party apps. However, declaring + * the permission implies intention to use the API and the user of the device can grant permission + * through the Settings application. */ @SystemService(Context.USAGE_STATS_SERVICE) public final class UsageStatsManager { @@ -122,7 +122,7 @@ public final class UsageStatsManager { * @param intervalType The time interval by which the stats are aggregated. * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return A list of {@link UsageStats} + * @return A list of {@link UsageStats} or null if none are available. * * @see #INTERVAL_DAILY * @see #INTERVAL_WEEKLY @@ -139,7 +139,7 @@ public final class UsageStatsManager { return slice.getList(); } } catch (RemoteException e) { - // fallthrough and return the empty list. + // fallthrough and return null. } return Collections.emptyList(); } @@ -152,7 +152,7 @@ public final class UsageStatsManager { * @param intervalType The time interval by which the stats are aggregated. * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return A list of {@link ConfigurationStats} + * @return A list of {@link ConfigurationStats} or null if none are available. */ public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime, long endTime) { @@ -185,7 +185,7 @@ public final class UsageStatsManager { return iter; } } catch (RemoteException e) { - // fallthrough and return empty result. + // fallthrough and return null } return sEmptyResults; } @@ -197,7 +197,8 @@ public final class UsageStatsManager { * * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return A {@link java.util.Map} keyed by package name + * @return A {@link java.util.Map} keyed by package name, or null if no stats are + * available. */ public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime); |