resolve merge conflicts of 26b174659 to ub-launcher3-master

Test: I solemnly swear I tested this conflict resolution.
Change-Id: Ifb24f291b096b979044e5c87ec58c24e9c479728
This commit is contained in:
Tony
2017-03-30 18:10:05 -07:00
24 changed files with 491 additions and 97 deletions

View File

@@ -22,7 +22,7 @@
<com.android.launcher3.shortcuts.DeepShortcutTextView
style="@style/BaseIcon"
android:id="@+id/deep_shortcut"
android:id="@+id/bubble_text"
android:background="?android:attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
@@ -34,8 +34,7 @@
android:fontFamily="sans-serif"
launcher:layoutHorizontal="true"
launcher:iconDisplay="shortcut_popup"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
android:elevation="@dimen/deep_shortcuts_elevation" />
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" />
<View
android:id="@+id/icon"

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<com.android.launcher3.shortcuts.DeepShortcutView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="@dimen/bg_popup_item_height" >
<com.android.launcher3.BubbleTextView
style="@style/BaseIcon"
android:id="@+id/bubble_text"
android:background="?android:attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:paddingStart="@dimen/bg_popup_item_height"
android:paddingEnd="@dimen/deep_shortcut_padding_end"
android:textSize="14sp"
android:fontFamily="sans-serif"
launcher:layoutHorizontal="true" />
<View
android:id="@+id/icon"
android:layout_width="@dimen/system_shortcut_icon_size"
android:layout_height="@dimen/system_shortcut_icon_size"
android:layout_margin="@dimen/system_shortcut_padding_start"
android:layout_gravity="start" />
<View
android:id="@+id/divider"
android:layout_width="@dimen/deep_shortcuts_divider_width"
android:layout_height="@dimen/popup_item_divider_height"
android:layout_gravity="end|bottom"
android:visibility="gone"
android:background="?android:attr/listDivider" />
</com.android.launcher3.shortcuts.DeepShortcutView>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/deep_shortcut_icon_size"
android:layout_height="@dimen/deep_shortcut_icon_size"
android:background="?android:attr/selectableItemBackground"
android:layout_marginEnd="@dimen/deep_shortcut_padding_start"
android:padding="4dp" />

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_shortcut_icons"
android:layout_width="match_parent"
android:layout_height="@dimen/bg_popup_item_height"
android:paddingEnd="@dimen/deep_shortcut_padding_start"
android:orientation="horizontal"
android:gravity="end|center_vertical"
android:background="@color/notification_header_background_color" />

View File

@@ -47,19 +47,5 @@
launcher:iconSizeOverride="@dimen/widget_section_icon_size"
launcher:layoutHorizontal="true" />
<HorizontalScrollView
android:id="@+id/widgets_scroll_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/colorPrimaryDark"
android:scrollbars="none">
<LinearLayout
android:id="@+id/widgets_cell_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/widget_row_padding"
android:paddingEnd="0dp"
android:orientation="horizontal"
android:showDividers="none"/>
</HorizontalScrollView>
<include layout="@layout/widgets_scroll_container" />
</LinearLayout>

View File

@@ -30,6 +30,7 @@
<color name="spring_loaded_panel_color">#40FFFFFF</color>
<color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
<!-- Notifications -->
<color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
<color name="notification_header_background_color">#F5F5F5</color> <!-- Gray 100 -->
<color name="notification_background_color">#FFF</color>
@@ -37,4 +38,7 @@
<color name="divider_color">@color/notification_color_beneath</color>
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
<color name="legacy_icon_background">#FFFFFF</color>
<!-- System shortcuts -->
<color name="system_shortcuts_icon_color">@android:color/tertiary_text_light</color>
</resources>

View File

@@ -172,6 +172,8 @@
<dimen name="deep_shortcuts_arrow_horizontal_offset">19dp</dimen>
<!-- popup_item_width - icon_size - padding_start - drawable_padding -->
<dimen name="deep_shortcuts_divider_width">158dp</dimen>
<dimen name="system_shortcut_icon_size">28dp</dimen>
<dimen name="system_shortcut_padding_start">10dp</dimen>
<!-- Icon badges (with notification counts) -->
<dimen name="badge_size">24dp</dimen>

View File

@@ -92,6 +92,9 @@ public abstract class AbstractFloatingView extends LinearLayout {
return mIsOpen;
}
protected void onWidgetsBound() {
}
protected abstract boolean isOfType(@FloatingViewType int type);
protected static <T extends AbstractFloatingView> T getOpenView(

View File

@@ -3938,11 +3938,24 @@ public class Launcher extends BaseActivity
mWidgetsView.setWidgets(allWidgets);
mAllWidgets = null;
}
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null) {
topView.onWidgetsBound();
}
}
public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
return mWidgetsView.getWidgetsForPackageUser(packageUserKey);
}
@Override
public void notifyWidgetProvidersChanged() {
if (mWorkspace.getState().shouldUpdateWidget) {
notifyWidgetProvidersChanged(false);
}
public void notifyWidgetProvidersChanged(boolean force) {
if (force || mWorkspace.getState().shouldUpdateWidget) {
mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty());
}
}

View File

@@ -91,11 +91,12 @@ public class WidgetPreviewLoader {
* @return a request id which can be used to cancel the request.
*/
public CancellationSignal getPreview(WidgetItem item, int previewWidth,
int previewHeight, WidgetCell caller) {
int previewHeight, WidgetCell caller, boolean animate) {
String size = previewWidth + "x" + previewHeight;
WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size);
PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller);
PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller,
animate);
task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
CancellationSignal signal = new CancellationSignal();
@@ -522,17 +523,19 @@ public class WidgetPreviewLoader {
private final int mPreviewHeight;
private final int mPreviewWidth;
private final WidgetCell mCaller;
private final boolean mAnimatePreviewIn;
private final BaseActivity mActivity;
@Thunk long[] mVersions;
@Thunk Bitmap mBitmapToRecycle;
PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
int previewHeight, WidgetCell caller) {
int previewHeight, WidgetCell caller, boolean animate) {
mKey = key;
mInfo = info;
mPreviewHeight = previewHeight;
mPreviewWidth = previewWidth;
mCaller = caller;
mAnimatePreviewIn = animate;
mActivity = BaseActivity.fromContext(mCaller.getContext());
if (DEBUG) {
Log.d(TAG, String.format("%s, %s, %d, %d",
@@ -588,7 +591,7 @@ public class WidgetPreviewLoader {
@Override
protected void onPostExecute(final Bitmap preview) {
mCaller.applyPreview(preview);
mCaller.applyPreview(preview, mAnimatePreviewIn);
// Write the generated preview to the DB in the worker thread
if (mVersions != null) {

View File

@@ -22,7 +22,6 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
@@ -66,6 +65,7 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.TriangleShape;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutsItemView;
import com.android.launcher3.util.PackageUserKey;
@@ -138,19 +138,21 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
return null;
}
ItemInfo itemInfo = (ItemInfo) icon.getTag();
if (!DeepShortcutManager.supportsShortcuts(itemInfo)) {
return null;
}
List<String> shortcutIds = launcher.getPopupDataProvider().getShortcutIdsForItem(itemInfo);
List<NotificationKeyData> notificationKeys = launcher.getPopupDataProvider()
.getNotificationKeysForItem(itemInfo);
if (shortcutIds.size() > 0 || notificationKeys.size() > 0) {
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
container.setVisibility(View.INVISIBLE);
launcher.getDragLayer().addView(container);
container.populateAndShow(icon, shortcutIds, notificationKeys);
return container;
}
return null;
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
container.setVisibility(View.INVISIBLE);
launcher.getDragLayer().addView(container);
container.populateAndShow(icon, shortcutIds, notificationKeys);
return container;
}
public void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
@@ -187,6 +189,9 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
List<DeepShortcutView> shortcutViews = mShortcutsItemView == null
? Collections.EMPTY_LIST
: mShortcutsItemView.getDeepShortcutViews(reverseOrder);
List<View> systemShortcutViews = mShortcutsItemView == null
? Collections.EMPTY_LIST
: mShortcutsItemView.getSystemShortcutViews(reverseOrder);
if (mNotificationItemView != null) {
BadgeInfo badgeInfo = mLauncher.getPopupDataProvider()
.getBadgeInfoForItem(originalItemInfo);
@@ -208,7 +213,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
final Looper workerLooper = LauncherModel.getWorkerLooper();
new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
this, shortcutIds, shortcutViews, notificationKeys, mNotificationItemView));
this, shortcutIds, shortcutViews, notificationKeys, mNotificationItemView,
systemShortcutViews));
}
private void addDummyViews(BubbleTextView originalIcon,
@@ -216,9 +222,12 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
final Resources res = getResources();
final int spacing = res.getDimensionPixelSize(R.dimen.popup_items_spacing);
final LayoutInflater inflater = mLauncher.getLayoutInflater();
int numItems = itemTypesToPopulate.length;
for (int i = 0; i < numItems; i++) {
PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i];
PopupPopulator.Item nextItemTypeToPopulate =
i < numItems - 1 ? itemTypesToPopulate[i + 1] : null;
final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false);
if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
@@ -228,23 +237,23 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
}
boolean itemIsFollowedByDifferentType = i < numItems - 1
&& itemTypesToPopulate[i + 1] != itemTypeToPopulate;
boolean shouldAddBottomMargin = nextItemTypeToPopulate != null
&& itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
item.setAccessibilityDelegate(mAccessibilityDelegate);
if (itemTypeToPopulate == PopupPopulator.Item.SHORTCUT) {
if (itemTypeToPopulate.isShortcut) {
if (mShortcutsItemView == null) {
mShortcutsItemView = (ShortcutsItemView) inflater.inflate(
R.layout.shortcuts_item, this, false);
addView(mShortcutsItemView);
}
mShortcutsItemView.addDeepShortcutView((DeepShortcutView) item);
if (itemIsFollowedByDifferentType) {
mShortcutsItemView.addShortcutView(item, itemTypeToPopulate, mIsAboveIcon);
if (shouldAddBottomMargin) {
((LayoutParams) mShortcutsItemView.getLayoutParams()).bottomMargin = spacing;
}
} else {
addView(item);
if (itemIsFollowedByDifferentType) {
if (shouldAddBottomMargin) {
((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
}
}
@@ -602,6 +611,16 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
badgeInfo.getNotificationKeys()));
}
@Override
protected void onWidgetsBound() {
enableWidgets();
}
public boolean enableWidgets() {
return mShortcutsItemView != null && mShortcutsItemView.enableWidgets(
(ItemInfo) mOriginalIcon.getTag());
}
private ObjectAnimator createArrowScaleAnim(float scale) {
return LauncherAnimUtils.ofPropertyValuesHolder(
mArrow, new PropertyListBuilder().scale(scale).build());

View File

@@ -48,6 +48,12 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
private static final boolean LOGD = false;
private static final String TAG = "PopupDataProvider";
/** Note that these are in order of priority. */
public static final SystemShortcut[] SYSTEM_SHORTCUTS = new SystemShortcut[] {
new SystemShortcut.Widgets(),
new SystemShortcut.AppInfo(),
};
private final Launcher mLauncher;
/** Maps launcher activity components to their list of shortcut ids. */

View File

@@ -156,7 +156,8 @@ public abstract class PopupItemView extends FrameLayout
* Returns the position of the center of the icon relative to the container.
*/
public Point getIconCenter() {
sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
sTempPoint.y = getMeasuredHeight() / 2;
sTempPoint.x = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height) / 2;
if (Utilities.isRtl(getResources())) {
sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
}

View File

@@ -17,12 +17,15 @@
package com.android.launcher3.popup;
import android.content.ComponentName;
import android.content.res.Resources;
import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.view.View;
import android.widget.ImageView;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
@@ -52,13 +55,17 @@ public class PopupPopulator {
@VisibleForTesting static final int NUM_DYNAMIC = 2;
public enum Item {
SHORTCUT(R.layout.deep_shortcut),
NOTIFICATION(R.layout.notification);
SHORTCUT(R.layout.deep_shortcut, true),
NOTIFICATION(R.layout.notification, false),
SYSTEM_SHORTCUT(R.layout.system_shortcut, true),
SYSTEM_SHORTCUT_ICON(R.layout.system_shortcut_icon_only, true);
public final int layoutId;
public final boolean isShortcut;
Item(int layoutId) {
Item(int layoutId, boolean isShortcut) {
this.layoutId = layoutId;
this.isShortcut = isShortcut;
}
}
@@ -66,7 +73,8 @@ public class PopupPopulator {
@NonNull List<NotificationKeyData> notificationKeys) {
boolean hasNotifications = notificationKeys.size() > 0;
int numNotificationItems = hasNotifications ? 1 : 0;
int numItems = Math.min(MAX_ITEMS, shortcutIds.size() + numNotificationItems);
int numItems = Math.min(MAX_ITEMS, shortcutIds.size() + numNotificationItems)
+ PopupDataProvider.SYSTEM_SHORTCUTS.length;
Item[] items = new Item[numItems];
for (int i = 0; i < numItems; i++) {
items[i] = Item.SHORTCUT;
@@ -75,6 +83,11 @@ public class PopupPopulator {
// The notification layout is always first.
items[0] = Item.NOTIFICATION;
}
// The system shortcuts are always last.
boolean iconsOnly = !shortcutIds.isEmpty();
for (int i = 0; i < PopupDataProvider.SYSTEM_SHORTCUTS.length; i++) {
items[numItems - 1 - i] = iconsOnly ? Item.SYSTEM_SHORTCUT_ICON : Item.SYSTEM_SHORTCUT;
}
return items;
}
@@ -159,11 +172,11 @@ public class PopupPopulator {
return filteredShortcuts;
}
public static Runnable createUpdateRunnable(final Launcher launcher, ItemInfo originalInfo,
public static Runnable createUpdateRunnable(final Launcher launcher, final ItemInfo originalInfo,
final Handler uiHandler, final PopupContainerWithArrow container,
final List<String> shortcutIds, final List<DeepShortcutView> shortcutViews,
final List<NotificationKeyData> notificationKeys,
final NotificationItemView notificationView) {
final NotificationItemView notificationView, final List<View> systemShortcutViews) {
final ComponentName activity = originalInfo.getTargetComponent();
final UserHandle user = originalInfo.user;
return new Runnable() {
@@ -195,11 +208,20 @@ public class PopupPopulator {
uiHandler.post(new UpdateShortcutChild(container, shortcutViews.get(i),
si, shortcut));
}
// This ensures that mLauncher.getWidgetsForPackageUser()
// doesn't return null (it puts all the widgets in memory).
launcher.notifyWidgetProvidersChanged(true /* force */);
for (int i = 0; i < PopupDataProvider.SYSTEM_SHORTCUTS.length; i++) {
final SystemShortcut systemShortcut = PopupDataProvider.SYSTEM_SHORTCUTS[i];
uiHandler.post(new UpdateSystemShortcutChild(container,
systemShortcutViews.get(i), systemShortcut, launcher, originalInfo));
}
}
};
}
/** Updates the child of this container at the given index based on the given shortcut info. */
/** Updates the shortcut child of this container based on the given shortcut info. */
private static class UpdateShortcutChild implements Runnable {
private final PopupContainerWithArrow mContainer;
private final DeepShortcutView mShortcutChild;
@@ -221,7 +243,7 @@ public class PopupPopulator {
}
}
/** Updates the child of this container at the given index based on the given shortcut info. */
/** Updates the notification child based on the given notification info. */
private static class UpdateNotificationChild implements Runnable {
private NotificationItemView mNotificationView;
private List<NotificationInfo> mNotificationInfos;
@@ -237,4 +259,50 @@ public class PopupPopulator {
mNotificationView.applyNotificationInfos(mNotificationInfos);
}
}
/** Updates the system shortcut child based on the given shortcut info. */
private static class UpdateSystemShortcutChild implements Runnable {
private static final float DISABLED_ALPHA = 0.38f;
private final PopupContainerWithArrow mContainer;
private final View mSystemShortcutChild;
private final SystemShortcut mSystemShortcutInfo;
private final Launcher mLauncher;
private final ItemInfo mItemInfo;
public UpdateSystemShortcutChild(PopupContainerWithArrow container, View systemShortcutChild,
SystemShortcut systemShortcut, Launcher launcher, ItemInfo originalInfo) {
mContainer = container;
mSystemShortcutChild = systemShortcutChild;
mSystemShortcutInfo = systemShortcut;
mLauncher = launcher;
mItemInfo = originalInfo;
}
@Override
public void run() {
final Resources res = mSystemShortcutChild.getResources();
if (mSystemShortcutChild instanceof DeepShortcutView) {
final DeepShortcutView shortcutView = (DeepShortcutView) mSystemShortcutChild;
shortcutView.getIconView().setBackground(mSystemShortcutInfo.getIcon(res));
shortcutView.getBubbleText().setText(mSystemShortcutInfo.getLabel(res));
} else if (mSystemShortcutChild instanceof ImageView) {
final ImageView shortcutIcon = (ImageView) mSystemShortcutChild;
shortcutIcon.setImageDrawable(mSystemShortcutInfo.getIcon(res));
shortcutIcon.setContentDescription(mSystemShortcutInfo.getLabel(res));
}
if (!(mSystemShortcutInfo instanceof SystemShortcut.Widgets)) {
mSystemShortcutChild.setOnClickListener(mSystemShortcutInfo
.getOnClickListener(mLauncher, mItemInfo));
} else {
mSystemShortcutChild.setTag(mSystemShortcutInfo);
// Widgets might not be enabled right away.
if (mContainer.enableWidgets()) {
return;
}
// Disable Widgets (we might be able to re-enable when widgets are bound).
mSystemShortcutChild.setAlpha(DISABLED_ALPHA);
}
}
}
}

View File

@@ -0,0 +1,89 @@
package com.android.launcher3.popup;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.view.View;
import com.android.launcher3.InfoDropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetsAndMore;
import java.util.List;
/**
* Represents a system shortcut for a given app. The shortcut should have a static label and
* icon, and an onClickListener that depends on the item that the shortcut services.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
*/
public abstract class SystemShortcut {
private final int mIconResId;
private final int mLabelResId;
public SystemShortcut(int iconResId, int labelResId) {
mIconResId = iconResId;
mLabelResId = labelResId;
}
public Drawable getIcon(Resources resources) {
Drawable icon = resources.getDrawable(mIconResId);
icon.setTint(resources.getColor(R.color.system_shortcuts_icon_color));
return icon;
}
public String getLabel(Resources resources) {
return resources.getString(mLabelResId);
}
public abstract View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo);
public static class Widgets extends SystemShortcut {
public Widgets() {
super(R.drawable.ic_widget, R.string.widgets_and_more);
}
@Override
public View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo) {
final List<WidgetItem> widgets = launcher.getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
if (widgets == null) {
return null;
}
return new View.OnClickListener() {
@Override
public void onClick(View view) {
PopupContainerWithArrow.getOpen(launcher).close(true);
WidgetsAndMore widgetsAndMore =
(WidgetsAndMore) launcher.getLayoutInflater().inflate(
R.layout.widgets_and_more, launcher.getDragLayer(), false);
widgetsAndMore.populateAndShow(itemInfo);
}
};
}
}
public static class AppInfo extends SystemShortcut {
public AppInfo() {
super(R.drawable.ic_info_launcher, R.string.app_info_drop_target_label);
}
@Override
public View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo) {
return new View.OnClickListener() {
@Override
public void onClick(View view) {
InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null);
}
};
}
}
}

View File

@@ -24,6 +24,7 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
@@ -39,7 +40,7 @@ public class DeepShortcutView extends FrameLayout {
private final Rect mPillRect;
private DeepShortcutTextView mBubbleText;
private BubbleTextView mBubbleText;
private View mIconView;
private ShortcutInfo mInfo;
@@ -62,11 +63,11 @@ public class DeepShortcutView extends FrameLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mBubbleText = (DeepShortcutTextView) findViewById(R.id.deep_shortcut);
mBubbleText = findViewById(R.id.bubble_text);
mIconView = findViewById(R.id.icon);
}
public DeepShortcutTextView getBubbleText() {
public BubbleTextView getBubbleText() {
return mBubbleText;
}

View File

@@ -36,21 +36,28 @@ import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupItemView;
import com.android.launcher3.popup.PopupPopulator;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A {@link PopupItemView} that contains all of the {@link DeepShortcutView}s for an app.
* A {@link PopupItemView} that contains all of the {@link DeepShortcutView}s for an app,
* as well as the system shortcuts such as Widgets and App Info.
*/
public class ShortcutsItemView extends PopupItemView implements View.OnLongClickListener,
View.OnTouchListener, LogContainerProvider {
private Launcher mLauncher;
private LinearLayout mDeepShortcutsLayout;
private LinearLayout mShortcutsLayout;
private LinearLayout mSystemShortcutIcons;
private final Point mIconShift = new Point();
private final Point mIconLastTouchPos = new Point();
private final List<DeepShortcutView> mDeepShortcutViews = new ArrayList<>();
private final List<View> mSystemShortcutViews = new ArrayList<>();
public ShortcutsItemView(Context context) {
this(context, null, 0);
@@ -69,7 +76,7 @@ public class ShortcutsItemView extends PopupItemView implements View.OnLongClick
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDeepShortcutsLayout = (LinearLayout) findViewById(R.id.deep_shortcuts);
mShortcutsLayout = findViewById(R.id.deep_shortcuts);
}
@Override
@@ -111,42 +118,81 @@ public class ShortcutsItemView extends PopupItemView implements View.OnLongClick
return false;
}
public void addDeepShortcutView(DeepShortcutView deepShortcutView) {
if (getNumDeepShortcuts() > 0) {
getDeepShortcutAt(getNumDeepShortcuts() - 1).findViewById(R.id.divider)
.setVisibility(VISIBLE);
public void addShortcutView(View shortcutView, PopupPopulator.Item shortcutType,
boolean isAboveIcon) {
if (shortcutType == PopupPopulator.Item.SHORTCUT) {
mDeepShortcutViews.add((DeepShortcutView) shortcutView);
} else {
mSystemShortcutViews.add(shortcutView);
}
if (shortcutType == PopupPopulator.Item.SYSTEM_SHORTCUT_ICON) {
// System shortcut icons are added to a header that is separate from the full shortcuts.
if (mSystemShortcutIcons == null) {
mSystemShortcutIcons = (LinearLayout) mLauncher.getLayoutInflater().inflate(
R.layout.system_shortcut_icons, mShortcutsLayout, false);
if (isAboveIcon) {
mShortcutsLayout.addView(mSystemShortcutIcons, 0);
} else {
mShortcutsLayout.addView(mSystemShortcutIcons);
}
}
mSystemShortcutIcons.addView(shortcutView);
} else {
if (mShortcutsLayout.getChildCount() > 0) {
View prevChild = mShortcutsLayout.getChildAt(mShortcutsLayout.getChildCount() - 1);
if (prevChild instanceof DeepShortcutView) {
prevChild.findViewById(R.id.divider).setVisibility(VISIBLE);
}
}
mShortcutsLayout.addView(shortcutView);
}
mDeepShortcutsLayout.addView(deepShortcutView);
}
private DeepShortcutView getDeepShortcutAt(int index) {
return (DeepShortcutView) mDeepShortcutsLayout.getChildAt(index);
}
private int getNumDeepShortcuts() {
return mDeepShortcutsLayout.getChildCount();
}
public List<DeepShortcutView> getDeepShortcutViews(boolean reverseOrder) {
int numDeepShortcuts = getNumDeepShortcuts();
List<DeepShortcutView> deepShortcutViews = new ArrayList<>(numDeepShortcuts);
for (int i = 0; i < numDeepShortcuts; i++) {
DeepShortcutView deepShortcut = getDeepShortcutAt(i);
if (reverseOrder) {
deepShortcutViews.add(0, deepShortcut);
} else {
deepShortcutViews.add(deepShortcut);
if (reverseOrder) {
Collections.reverse(mDeepShortcutViews);
}
return mDeepShortcutViews;
}
public List<View> getSystemShortcutViews(boolean reverseOrder) {
if (reverseOrder) {
Collections.reverse(mSystemShortcutViews);
}
return mSystemShortcutViews;
}
/**
* Sets the onClickListener on widgets system shortcut child, and updates alpha to 1.
* @return whether widgets is enabled, i.e. the onClickListener is not null.
*/
public boolean enableWidgets(ItemInfo itemInfo) {
for (View systemShortcut : mSystemShortcutViews) {
if (systemShortcut.getTag() instanceof SystemShortcut.Widgets) {
View.OnClickListener onClickListener =
((SystemShortcut.Widgets) systemShortcut.getTag()).getOnClickListener(
mLauncher, itemInfo);
if (onClickListener != null) {
systemShortcut.setAlpha(1f);
systemShortcut.setOnClickListener(onClickListener);
return true;
}
return false;
}
}
return deepShortcutViews;
return false;
}
@Override
public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
openAnimation.play(super.createOpenAnimation(isContainerAboveIcon, pivotLeft));
for (int i = 0; i < getNumDeepShortcuts(); i++) {
View deepShortcutIcon = getDeepShortcutAt(i).getIconView();
for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
continue;
}
DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
View deepShortcutIcon = shortcutView.getIconView();
deepShortcutIcon.setScaleX(0);
deepShortcutIcon.setScaleY(0);
openAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
@@ -160,8 +206,12 @@ public class ShortcutsItemView extends PopupItemView implements View.OnLongClick
long duration) {
AnimatorSet closeAnimation = LauncherAnimUtils.createAnimatorSet();
closeAnimation.play(super.createCloseAnimation(isContainerAboveIcon, pivotLeft, duration));
for (int i = 0; i < getNumDeepShortcuts(); i++) {
View deepShortcutIcon = getDeepShortcutAt(i).getIconView();
for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
continue;
}
DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
View deepShortcutIcon = shortcutView.getIconView();
deepShortcutIcon.setScaleX(1);
deepShortcutIcon.setScaleY(1);
closeAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(

View File

@@ -11,8 +11,8 @@ import java.util.Arrays;
/** Creates a hash key based on package name and user. */
public class PackageUserKey {
private String mPackageName;
private UserHandle mUser;
public String mPackageName;
public UserHandle mUser;
private int mHashCode;
public static PackageUserKey fromItemInfo(ItemInfo info) {

View File

@@ -155,6 +155,10 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
}
public void applyPreview(Bitmap bitmap) {
applyPreview(bitmap, true);
}
public void applyPreview(Bitmap bitmap, boolean animate) {
if (bitmap != null) {
mWidgetImage.setBitmap(bitmap,
DrawableFactory.get(getContext()).getBadgeForUser(mItem.user, getContext()));
@@ -169,11 +173,15 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
}
public void ensurePreview() {
ensurePreview(true);
}
public void ensurePreview(boolean animate) {
if (mActiveRequest != null) {
return;
}
mActiveRequest = mWidgetPreviewLoader.getPreview(
mItem, mPresetPreviewSize, mPresetPreviewSize, this);
mItem, mPresetPreviewSize, mPresetPreviewSize, this, animate);
}
@Override

View File

@@ -46,12 +46,15 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.TouchController;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static android.R.attr.bottom;
/**
* Bottom sheet for the "Widgets & more" long-press option.
*/
@@ -64,6 +67,7 @@ public class WidgetsAndMore extends AbstractFloatingView implements Insettable,
private float mTranslationYRange;
private Launcher mLauncher;
private ItemInfo mOriginalItemInfo;
private ObjectAnimator mOpenCloseAnimator;
private Interpolator mFastOutSlowInInterpolator;
private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
@@ -95,9 +99,25 @@ public class WidgetsAndMore extends AbstractFloatingView implements Insettable,
mTranslationYRange = mTranslationYClosed - mTranslationYOpen;
}
public void populateAndShow(ItemInfo itemInfo, List<WidgetItem> widgets) {
((TextView) findViewById(R.id.title)).setText(itemInfo.title);
public void populateAndShow(ItemInfo itemInfo) {
mOriginalItemInfo = itemInfo;
((TextView) findViewById(R.id.title)).setText(mOriginalItemInfo.title);
onWidgetsBound();
mWasNavBarLight = (mLauncher.getWindow().getDecorView().getSystemUiVisibility()
& View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;
mLauncher.getDragLayer().addView(this);
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
setTranslationY(mTranslationYClosed);
mIsOpen = false;
open(true);
}
@Override
protected void onWidgetsBound() {
List<WidgetItem> widgets = mLauncher.getWidgetsForPackageUser(new PackageUserKey(
mOriginalItemInfo.getTargetComponent().getPackageName(), mOriginalItemInfo.user));
List<WidgetItem> shortcuts = new ArrayList<>();
// Transfer configurable widgets to shortcuts
Iterator<WidgetItem> widgetsIter = widgets.iterator();
@@ -116,6 +136,9 @@ public class WidgetsAndMore extends AbstractFloatingView implements Insettable,
ViewGroup shortcutRow = (ViewGroup) findViewById(R.id.shortcuts);
ViewGroup shortcutCells = (ViewGroup) shortcutRow.findViewById(R.id.widgets_cell_list);
widgetCells.removeAllViews();
shortcutCells.removeAllViews();
for (int i = 0; i < widgets.size(); i++) {
addItemCell(widgetCells);
if (i < widgets.size() - 1) {
@@ -152,14 +175,6 @@ public class WidgetsAndMore extends AbstractFloatingView implements Insettable,
} else {
removeView(findViewById(R.id.shortcuts_header));
}
mWasNavBarLight = (mLauncher.getWindow().getDecorView().getSystemUiVisibility()
& View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;
mLauncher.getDragLayer().addView(this);
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
setTranslationY(mTranslationYClosed);
mIsOpen = false;
open(true);
}
private void addDivider(ViewGroup parent) {

View File

@@ -40,8 +40,11 @@ import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import java.util.List;
/**
* The widgets list view container.
*/
@@ -243,6 +246,10 @@ public class WidgetsContainerView extends BaseContainerView
return mAdapter.getItemCount() == 0;
}
public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
return mAdapter.copyWidgetsForPackageUser(packageUserKey);
}
@Override
public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
targetParent.containerType = ContainerType.WIDGETS;

View File

@@ -31,10 +31,12 @@ import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.LabelComparator;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -98,6 +100,26 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
return mEntries.get(pos).titleSectionName;
}
/**
* Copies and returns the widgets associated with the package and user of the ComponentKey.
*/
public List<WidgetItem> copyWidgetsForPackageUser(PackageUserKey packageUserKey) {
for (WidgetListRowEntry entry : mEntries) {
if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) {
ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets);
// Remove widgets not associated with the correct user.
Iterator<WidgetItem> iterator = widgets.iterator();
while (iterator.hasNext()) {
if (!iterator.next().user.equals(packageUserKey.mUser)) {
iterator.remove();
}
}
return widgets.isEmpty() ? null : widgets;
}
}
return null;
}
@Override
public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
WidgetListRowEntry entry = mEntries.get(pos);

View File

@@ -60,7 +60,7 @@ public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase {
// Verify that launching a shortcut opens a page with the same text
assertTrue(deepShortcutsContainer.getChildCount() > 0);
UiObject2 shortcut = deepShortcutsContainer.getChildren().get(0)
.findObject(getSelectorForId(R.id.deep_shortcut));
.findObject(getSelectorForId(R.id.bubble_text));
shortcut.click();
assertTrue(mDevice.wait(Until.hasObject(By.pkg(
mSettingsApp.getComponentName().getPackageName())

View File

@@ -61,7 +61,7 @@ public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase {
// Drag the first shortcut to the home screen.
assertTrue(deepShortcutsContainer.getChildCount() > 0);
UiObject2 shortcut = deepShortcutsContainer.getChildren().get(0)
.findObject(getSelectorForId(R.id.deep_shortcut));
.findObject(getSelectorForId(R.id.bubble_text));
String shortcutName = shortcut.getText();
dragToWorkspace(shortcut, false);