Merge "Reset initial animation values before completing reorder animation." into ub-launcher3-master
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a8a25af..1a09fa0 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -81,8 +81,6 @@
<dimen name="all_apps_divider_margin_vertical">8dp</dimen>
- <dimen name="all_apps_bezel_swipe_height">24dp</dimen>
-
<!-- Widget tray -->
<dimen name="widget_preview_label_vertical_padding">8dp</dimen>
<dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
diff --git a/res/xml/app_target_browser.xml b/res/xml/app_target_browser.xml
deleted file mode 100644
index d7c3ed5..0000000
--- a/res/xml/app_target_browser.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
- <favorite launcher:uri="http://www.example.com/" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_camera.xml b/res/xml/app_target_camera.xml
deleted file mode 100644
index f65a2b1..0000000
--- a/res/xml/app_target_camera.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
- <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
- <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_email.xml b/res/xml/app_target_email.xml
deleted file mode 100644
index 44f0a40..0000000
--- a/res/xml/app_target_email.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
- <favorite launcher:uri="mailto:" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_gallery.xml b/res/xml/app_target_gallery.xml
deleted file mode 100644
index c9d3492..0000000
--- a/res/xml/app_target_gallery.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
- <favorite launcher:uri="#Intent;type=images/*;end" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_messenger.xml b/res/xml/app_target_messenger.xml
deleted file mode 100644
index 278eb5c..0000000
--- a/res/xml/app_target_messenger.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
- <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
- <favorite launcher:uri="sms:" />
- <favorite launcher:uri="smsto:" />
- <favorite launcher:uri="mms:" />
- <favorite launcher:uri="mmsto:" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_phone.xml b/res/xml/app_target_phone.xml
deleted file mode 100644
index 5d6ca31..0000000
--- a/res/xml/app_target_phone.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
- <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
- <favorite launcher:uri="tel:123" />
- <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
-
-</resolve>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 34a44fc..2bf014a 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -65,13 +65,15 @@
*
* If the app is already in the list, doesn't add it.
*/
- public void add(AppInfo info) {
+ public void add(AppInfo info, LauncherActivityInfoCompat activityInfo) {
if (!mAppFilter.shouldShowApp(info.componentName)) {
return;
}
if (findActivity(data, info.componentName, info.user)) {
return;
}
+ mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
+
data.add(info);
added.add(info);
}
@@ -101,7 +103,7 @@
user);
for (LauncherActivityInfoCompat info : matches) {
- add(new AppInfo(context, info, user, mIconCache));
+ add(new AppInfo(context, info, user), info);
}
}
@@ -171,7 +173,7 @@
info.getComponentName().getPackageName(), user,
info.getComponentName().getClassName());
if (applicationInfo == null) {
- add(new AppInfo(context, info, user, mIconCache));
+ add(new AppInfo(context, info, user), info);
} else {
mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
modified.add(applicationInfo);
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 9c9dcc5..9b23df3 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -58,19 +58,12 @@
/**
* Must not hold the Context.
*/
- public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandle user,
- IconCache iconCache) {
- this(context, info, user, iconCache,
- UserManagerCompat.getInstance(context).isQuietModeEnabled(user));
+ public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandle user) {
+ this(context, info, user, UserManagerCompat.getInstance(context).isQuietModeEnabled(user));
}
public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandle user,
- IconCache iconCache, boolean quietModeEnabled) {
- this(context, info, user, iconCache, quietModeEnabled, true /* useLowResIcon */);
- }
-
- public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandle user,
- IconCache iconCache, boolean quietModeEnabled, boolean useLowResIcon) {
+ boolean quietModeEnabled) {
this.componentName = info.getComponentName();
this.container = ItemInfo.NO_ID;
this.user = user;
@@ -82,7 +75,6 @@
}
intent = makeLaunchIntent(context, info, user);
- iconCache.getTitleAndIcon(this, info, useLowResIcon);
}
public AppInfo(AppInfo info) {
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index f879216..92da9b7 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -212,6 +212,23 @@
lp.height = mTempRange1.size();
resizeWidgetIfNeeded(false);
+
+ // When the widget resizes in multi-window mode, the translation value changes to maintain
+ // a center fit. These overrides ensure the resize frame always aligns with the widget view.
+ getSnappedRectRelativeToDragLayer(sTmpRect);
+ if (mLeftBorderActive) {
+ lp.width = sTmpRect.width() + sTmpRect.left - lp.x;
+ }
+ if (mTopBorderActive) {
+ lp.height = sTmpRect.height() + sTmpRect.top - lp.y;
+ }
+ if (mRightBorderActive) {
+ lp.x = sTmpRect.left;
+ }
+ if (mBottomBorderActive) {
+ lp.y = sTmpRect.top;
+ }
+
requestLayout();
}
@@ -340,8 +357,8 @@
int xThreshold = mCellLayout.getCellWidth();
int yThreshold = mCellLayout.getCellHeight();
- mDeltaXAddOn = mRunningHInc * xThreshold;
- mDeltaYAddOn = mRunningVInc * yThreshold;
+ mDeltaXAddOn = mRunningHInc * xThreshold;
+ mDeltaYAddOn = mRunningVInc * yThreshold;
mDeltaX = 0;
mDeltaY = 0;
@@ -353,18 +370,35 @@
});
}
- public void snapToWidget(boolean animate) {
+ /**
+ * Returns the rect of this view when the frame is snapped around the widget, with the bounds
+ * relative to the {@link DragLayer}.
+ */
+ private void getSnappedRectRelativeToDragLayer(Rect out) {
float scale = mWidgetView.getScaleToFit();
- mDragLayer.getViewRectRelativeToSelf(mWidgetView, sTmpRect);
+ mDragLayer.getViewRectRelativeToSelf(mWidgetView, out);
- int newWidth = 2 * mBackgroundPadding
- + (int) (scale * (sTmpRect.width() - mWidgetPadding.left - mWidgetPadding.right));
- int newHeight = 2 * mBackgroundPadding
- + (int) (scale * (sTmpRect.height() - mWidgetPadding.top - mWidgetPadding.bottom));
+ int width = 2 * mBackgroundPadding
+ + (int) (scale * (out.width() - mWidgetPadding.left - mWidgetPadding.right));
+ int height = 2 * mBackgroundPadding
+ + (int) (scale * (out.height() - mWidgetPadding.top - mWidgetPadding.bottom));
- int newX = (int) (sTmpRect.left - mBackgroundPadding + scale * mWidgetPadding.left);
- int newY = (int) (sTmpRect.top - mBackgroundPadding + scale * mWidgetPadding.top);
+ int x = (int) (out.left - mBackgroundPadding + scale * mWidgetPadding.left);
+ int y = (int) (out.top - mBackgroundPadding + scale * mWidgetPadding.top);
+
+ out.left = x;
+ out.top = y;
+ out.right = out.left + width;
+ out.bottom = out.top + height;
+ }
+
+ public void snapToWidget(boolean animate) {
+ getSnappedRectRelativeToDragLayer(sTmpRect);
+ int newWidth = sTmpRect.width();
+ int newHeight = sTmpRect.height();
+ int newX = sTmpRect.left;
+ int newY = sTmpRect.top;
// We need to make sure the frame's touchable regions lie fully within the bounds of the
// DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index c5b3104..c6f1c48 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -5,13 +5,13 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.util.ContentWriter;
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -50,14 +50,13 @@
state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
}
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]);
- values.put(LauncherSettings.Favorites.RESTORED, state);
-
String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
+ int result = new ContentWriter(context, new ContentWriter.CommitParams(
+ "appWidgetId=? and (restored & 1) = 1", widgetIdParams))
+ .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
+ .put(LauncherSettings.Favorites.RESTORED, state)
+ .commit();
- int result = cr.update(Favorites.CONTENT_URI, values,
- "appWidgetId=? and (restored & 1) = 1", widgetIdParams);
if (result == 0) {
Cursor cursor = cr.query(Favorites.CONTENT_URI,
new String[] {Favorites.APPWIDGET_ID},
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index 40c5ed6..4fecc3d 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -28,6 +28,8 @@
import android.view.ViewConfiguration;
import android.widget.TextView;
+import com.android.launcher3.config.FeatureFlags;
+
/**
* The track and scrollbar that shows when you scroll the list.
*/
@@ -198,6 +200,11 @@
case MotionEvent.ACTION_DOWN:
if (isNearThumb(downX, downY)) {
mTouchOffsetY = downY - mThumbOffsetY;
+ } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
+ && mRv.supportsFastScrolling()
+ && isNearScrollBar(downX)) {
+ calcTouchOffsetAndPrepToFastScroll(downY, lastY);
+ updateFastScrollSectionNameAndThumbOffset(lastY, y);
}
break;
case MotionEvent.ACTION_MOVE:
@@ -207,28 +214,10 @@
if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
isNearThumb(downX, lastY) &&
Math.abs(y - downY) > config.getScaledTouchSlop()) {
- mRv.getParent().requestDisallowInterceptTouchEvent(true);
- mIsDragging = true;
- if (mCanThumbDetach) {
- mIsThumbDetached = true;
- }
- mTouchOffsetY += (lastY - downY);
- animatePopupVisibility(true);
- showActiveScrollbar(true);
+ calcTouchOffsetAndPrepToFastScroll(downY, lastY);
}
if (mIsDragging) {
- // Update the fastscroller section name at this touch position
- int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
- float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
- String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
- if (!sectionName.equals(mPopupSectionName)) {
- mPopupSectionName = sectionName;
- mPopupView.setText(sectionName);
- }
- animatePopupVisibility(!sectionName.isEmpty());
- updatePopupY(lastY);
- mLastTouchY = boundedY;
- setThumbOffsetY((int) mLastTouchY);
+ updateFastScrollSectionNameAndThumbOffset(lastY, y);
}
break;
case MotionEvent.ACTION_UP:
@@ -245,6 +234,32 @@
}
}
+ private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
+ mRv.getParent().requestDisallowInterceptTouchEvent(true);
+ mIsDragging = true;
+ if (mCanThumbDetach) {
+ mIsThumbDetached = true;
+ }
+ mTouchOffsetY += (lastY - downY);
+ animatePopupVisibility(true);
+ showActiveScrollbar(true);
+ }
+
+ private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
+ // Update the fastscroller section name at this touch position
+ int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
+ float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
+ String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
+ if (!sectionName.equals(mPopupSectionName)) {
+ mPopupSectionName = sectionName;
+ mPopupView.setText(sectionName);
+ }
+ animatePopupVisibility(!sectionName.isEmpty());
+ updatePopupY(lastY);
+ mLastTouchY = boundedY;
+ setThumbOffsetY((int) mLastTouchY);
+ }
+
public void draw(Canvas canvas) {
if (mThumbOffsetY < 0) {
return;
@@ -277,7 +292,7 @@
}
/**
- * Returns whether the specified points are near the scroll bar bounds.
+ * Returns whether the specified point is inside the thumb bounds.
*/
public boolean isNearThumb(int x, int y) {
int left = getDrawLeft();
@@ -286,6 +301,14 @@
return mTmpRect.contains(x, y);
}
+ /**
+ * Returns whether the specified x position is near the scroll bar.
+ */
+ public boolean isNearScrollBar(int x) {
+ int left = getDrawLeft();
+ return x >= left && x <= left + mMaxWidth;
+ }
+
private void animatePopupVisibility(boolean visible) {
if (mPopupVisible != visible) {
mPopupVisible = visible;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5e9e7e2..b8b43c9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -38,6 +38,7 @@
import android.widget.TextView;
import com.android.launcher3.IconCache.IconLoadRequest;
+import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.HolographicOutlineHelper;
@@ -51,7 +52,7 @@
* too aggressive.
*/
public class BubbleTextView extends TextView
- implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView {
+ implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView, ItemInfoUpdateReceiver {
private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
@@ -540,7 +541,8 @@
/**
* Applies the item info if it is same as what the view is pointing to currently.
*/
- public void reapplyItemInfo(final ItemInfo info) {
+ @Override
+ public void reapplyItemInfo(ItemInfoWithIcon info) {
if (getTag() == info) {
FastBitmapDrawable.State prevState = FastBitmapDrawable.State.NORMAL;
if (mIcon instanceof FastBitmapDrawable) {
@@ -582,20 +584,8 @@
mIconLoadRequest.cancel();
mIconLoadRequest = null;
}
- if (getTag() instanceof AppInfo) {
- AppInfo info = (AppInfo) getTag();
- if (info.usingLowResIcon) {
- mIconLoadRequest = LauncherAppState.getInstance().getIconCache()
- .updateIconInBackground(BubbleTextView.this, info);
- }
- } else if (getTag() instanceof ShortcutInfo) {
- ShortcutInfo info = (ShortcutInfo) getTag();
- if (info.usingLowResIcon) {
- mIconLoadRequest = LauncherAppState.getInstance().getIconCache()
- .updateIconInBackground(BubbleTextView.this, info);
- }
- } else if (getTag() instanceof PackageItemInfo) {
- PackageItemInfo info = (PackageItemInfo) getTag();
+ if (getTag() instanceof ItemInfoWithIcon) {
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
if (info.usingLowResIcon) {
mIconLoadRequest = LauncherAppState.getInstance().getIconCache()
.updateIconInBackground(BubbleTextView.this, info);
diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java
deleted file mode 100644
index c2bd883..0000000
--- a/src/com/android/launcher3/CommonAppTypeParser.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.XmlResourceParser;
-import android.database.sqlite.SQLiteDatabase;
-import android.util.Log;
-
-import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * A class that parses content values corresponding to some common app types.
- */
-public class CommonAppTypeParser implements LayoutParserCallback {
- private static final String TAG = "CommonAppTypeParser";
-
- // Including TARGET_NONE
- public static final int SUPPORTED_TYPE_COUNT = 7;
-
- private static final int RESTORE_FLAG_BIT_SHIFT = 4;
-
- public static final int TARGET_PHONE = 1;
- public static final int TARGET_MESSENGER = 2;
- public static final int TARGET_EMAIL = 3;
- public static final int TARGET_BROWSER = 4;
- public static final int TARGET_GALLERY = 5;
- public static final int TARGET_CAMERA = 6;
-
- private final long mItemId;
- @Thunk final int mResId;
- @Thunk final Context mContext;
-
- ContentValues parsedValues;
- Intent parsedIntent;
- String parsedTitle;
-
- public CommonAppTypeParser(long itemId, int itemType, Context context) {
- mItemId = itemId;
- mContext = context;
- mResId = getResourceForItemType(itemType);
- }
-
- @Override
- public long generateNewItemId() {
- return mItemId;
- }
-
- @Override
- public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
- parsedValues = values;
-
- // Remove unwanted values
- values.put(Favorites.ICON_PACKAGE, (String) null);
- values.put(Favorites.ICON_RESOURCE, (String) null);
- values.put(Favorites.ICON, (byte[]) null);
- return 1;
- }
-
- /**
- * Tries to find a suitable app to the provided app type.
- */
- public boolean findDefaultApp() {
- if (mResId == 0) {
- return false;
- }
-
- parsedIntent = null;
- parsedValues = null;
- new MyLayoutParser().parseValues();
- return (parsedValues != null) && (parsedIntent != null);
- }
-
- private class MyLayoutParser extends DefaultLayoutParser {
-
- public MyLayoutParser() {
- super(CommonAppTypeParser.this.mContext, null, CommonAppTypeParser.this,
- CommonAppTypeParser.this.mContext.getResources(), mResId, TAG_RESOLVE);
- }
-
- @Override
- protected long addShortcut(String title, Intent intent, int type) {
- if (type == Favorites.ITEM_TYPE_APPLICATION) {
- parsedIntent = intent;
- parsedTitle = title;
- }
- return super.addShortcut(title, intent, type);
- }
-
- public void parseValues() {
- XmlResourceParser parser = mSourceRes.getXml(mLayoutId);
- try {
- beginDocument(parser, mRootTag);
- new ResolveParser().parseAndAdd(parser);
- } catch (IOException | XmlPullParserException e) {
- Log.e(TAG, "Unable to parse default app info", e);
- }
- parser.close();
- }
- }
-
- public static int getResourceForItemType(int type) {
- switch (type) {
- case TARGET_PHONE:
- return R.xml.app_target_phone;
-
- case TARGET_MESSENGER:
- return R.xml.app_target_messenger;
-
- case TARGET_EMAIL:
- return R.xml.app_target_email;
-
- case TARGET_BROWSER:
- return R.xml.app_target_browser;
-
- case TARGET_GALLERY:
- return R.xml.app_target_gallery;
-
- case TARGET_CAMERA:
- return R.xml.app_target_camera;
-
- default:
- return 0;
- }
- }
-
- public static int encodeItemTypeToFlag(int itemType) {
- return itemType << RESTORE_FLAG_BIT_SHIFT;
- }
-
- public static int decodeItemTypeFromFlag(int flag) {
- return (flag & ShortcutInfo.FLAG_RESTORED_APP_TYPE) >> RESTORE_FLAG_BIT_SHIFT;
- }
-
-}
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index ef28d1e..05911ab 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -54,11 +54,6 @@
super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
}
- public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
- LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag) {
- super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag);
- }
-
@Override
protected HashMap<String, TagParser> getFolderElementsMap() {
return getFolderElementsMap(mSourceRes);
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index b22cb7c..603d25a 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -52,6 +52,7 @@
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
import com.android.launcher3.util.Thunk;
@@ -188,7 +189,7 @@
return getFullResDefaultActivityIcon();
}
- private Bitmap makeDefaultIcon(UserHandle user) {
+ protected Bitmap makeDefaultIcon(UserHandle user) {
Drawable unbadged = getFullResDefaultActivityIcon();
return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext);
}
@@ -406,13 +407,14 @@
* Fetches high-res icon for the provided ItemInfo and updates the caller when done.
* @return a request ID that can be used to cancel the request.
*/
- public IconLoadRequest updateIconInBackground(final BubbleTextView caller, final ItemInfo info) {
+ public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+ final ItemInfoWithIcon info) {
Runnable request = new Runnable() {
@Override
public void run() {
if (info instanceof AppInfo || info instanceof ShortcutInfo) {
- getTitleAndIcon((ItemInfoWithIcon) info, false);
+ getTitleAndIcon(info, false);
} else if (info instanceof PackageItemInfo) {
getTitleAndIconForApp((PackageItemInfo) info, false);
}
@@ -430,20 +432,6 @@
}
/**
- * Returns a high res icon for the given intent and user
- */
- public synchronized Bitmap getIcon(Intent intent, UserHandle user) {
- ComponentName component = intent.getComponent();
- // null info means not installed, but if we have a component from the intent then
- // we should still look in the cache for restored app icons.
- if (component == null) {
- return getDefaultIcon(user);
- }
- return cacheLocked(component, new ActivityInfoProvider(intent, user),
- user, true, false /* useLowRes */).icon;
- }
-
- /**
* Updates {@param application} only if a valid entry is found.
*/
public synchronized void updateTitleAndIcon(AppInfo application) {
@@ -528,8 +516,9 @@
*/
protected CacheEntry cacheLocked(
@NonNull ComponentName componentName,
- @NonNull Provider<LauncherActivityInfoCompat> infoProfider,
+ @NonNull Provider<LauncherActivityInfoCompat> infoProvider,
UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
+ Preconditions.assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
@@ -541,7 +530,7 @@
boolean providerFetchedOnce = false;
if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
- info = infoProfider.get();
+ info = infoProvider.get();
providerFetchedOnce = true;
if (info != null) {
@@ -570,7 +559,7 @@
if (TextUtils.isEmpty(entry.title)) {
if (info == null && !providerFetchedOnce) {
- info = infoProfider.get();
+ info = infoProvider.get();
providerFetchedOnce = true;
}
if (info != null) {
@@ -617,6 +606,7 @@
*/
private CacheEntry getEntryForPackageLocked(String packageName, UserHandle user,
boolean useLowResIcon) {
+ Preconditions.assertWorkerThread();
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
@@ -868,4 +858,12 @@
return mLauncherApps.resolveActivity(mIntent, mUser);
}
}
+
+ /**
+ * Interface for receiving itemInfo with high-res icon.
+ */
+ public interface ItemInfoUpdateReceiver {
+
+ void reapplyItemInfo(ItemInfoWithIcon info);
+ }
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index df2deb8..cba7cfe 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -26,6 +26,8 @@
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.os.Looper;
+import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -35,6 +37,7 @@
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -434,10 +437,25 @@
public ItemInfo getItemInfo() {
if (activityInfo != null) {
- return new AppInfo(mContext, activityInfo, user,
- LauncherAppState.getInstance().getIconCache(),
- UserManagerCompat.getInstance(mContext).isQuietModeEnabled(user),
- false /* useLowResIcon */).makeShortcut();
+ AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
+ final LauncherAppState app = LauncherAppState.getInstance();
+ // Set default values until proper values is loaded.
+ appInfo.title = "";
+ appInfo.iconBitmap = app.getIconCache().getDefaultIcon(user);
+ final ShortcutInfo si = appInfo.makeShortcut();
+ if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
+ app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
+ } else {
+ app.getModel().updateAndBindShortcutInfo(new Provider<ShortcutInfo>() {
+ @Override
+ public ShortcutInfo get() {
+ app.getIconCache().getTitleAndIcon(
+ si, activityInfo, false /* useLowResIcon */);
+ return si;
+ }
+ });
+ }
+ return si;
} else if (shortcutInfo != null) {
return new ShortcutInfo(shortcutInfo, mContext);
} else if (providerInfo != null) {
@@ -454,7 +472,7 @@
widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
return widgetInfo;
} else {
- return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data);
+ return createShortcutInfo(data, LauncherAppState.getInstance());
}
}
@@ -598,4 +616,42 @@
return installQueue;
}
}
+
+ private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
+ Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+ String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+ Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+
+ if (intent == null) {
+ // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
+ Log.e(TAG, "Can't construct ShorcutInfo with null intent");
+ return null;
+ }
+
+ final ShortcutInfo info = new ShortcutInfo();
+
+ // Only support intents for current user for now. Intents sent from other
+ // users wouldn't get here without intent forwarding anyway.
+ info.user = Process.myUserHandle();
+
+ if (bitmap instanceof Bitmap) {
+ info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
+ } else {
+ Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
+ if (extra instanceof Intent.ShortcutIconResource) {
+ info.iconResource = (Intent.ShortcutIconResource) extra;
+ info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
+ }
+ }
+ if (info.iconBitmap == null) {
+ info.iconBitmap = app.getIconCache().getDefaultIcon(info.user);
+ }
+
+ info.title = Utilities.trim(name);
+ info.contentDescription = UserManagerCompat.getInstance(app.getContext())
+ .getBadgedLabelForUser(info.title, info.user);
+ info.intent = intent;
+ return info;
+ }
+
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8e28912..d6c8cfb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2337,13 +2337,14 @@
showBrokenAppInstallDialog(packageName,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+ startActivitySafely(
+ v, PackageManagerHelper.getMarketIntent(packageName), info);
}
});
} else {
// Download has started.
final String packageName = info.providerName.getPackageName();
- startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+ startActivitySafely(v, PackageManagerHelper.getMarketIntent(packageName), info);
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 49bbfd0..1429df5 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -33,9 +33,11 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.AdapterView;
import android.widget.Advanceable;
import android.widget.RemoteViews;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
import java.lang.reflect.Method;
@@ -45,7 +47,8 @@
/**
* {@inheritDoc}
*/
-public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
+public class LauncherAppWidgetHostView extends AppWidgetHostView
+ implements TouchCompleteListener, View.OnLongClickListener {
private static final String TAG = "LauncherWidgetHostView";
@@ -56,11 +59,12 @@
// Maintains a list of widget ids which are supposed to be auto advanced.
private static final SparseBooleanArray sAutoAdvanceWidgetIds = new SparseBooleanArray();
- LayoutInflater mInflater;
+ protected final LayoutInflater mInflater;
- private CheckLongPressHelper mLongPressHelper;
- private StylusEventHelper mStylusEventHelper;
- private Context mContext;
+ private final CheckLongPressHelper mLongPressHelper;
+ private final StylusEventHelper mStylusEventHelper;
+ private final Context mContext;
+
@ViewDebug.ExportedProperty(category = "launcher")
private int mPreviousOrientation;
@@ -69,6 +73,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
+ private boolean mIsScrollable;
private boolean mIsAttachedToWindow;
private boolean mIsAutoAdvanceRegistered;
private Runnable mAutoAdvanceRunnable;
@@ -86,7 +91,7 @@
public LauncherAppWidgetHostView(Context context) {
super(context);
mContext = context;
- mLongPressHelper = new CheckLongPressHelper(this);
+ mLongPressHelper = new CheckLongPressHelper(this, this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mInflater = LayoutInflater.from(context);
setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
@@ -104,6 +109,16 @@
}
@Override
+ public boolean onLongClick(View view) {
+ if (mIsScrollable) {
+ DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+ dragLayer.requestDisallowInterceptTouchEvent(false);
+ }
+ view.performLongClick();
+ return true;
+ }
+
+ @Override
protected View getErrorView() {
return mInflater.inflate(R.layout.appwidget_error, this, false);
}
@@ -120,6 +135,24 @@
// The provider info or the views might have changed.
checkIfAutoAdvance();
+
+ mIsScrollable = checkScrollableRecursively(this);
+ }
+
+ private boolean checkScrollableRecursively(ViewGroup viewGroup) {
+ if (viewGroup instanceof AdapterView) {
+ return true;
+ } else {
+ for (int i=0; i < viewGroup.getChildCount(); i++) {
+ View child = viewGroup.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ if (checkScrollableRecursively((ViewGroup) child)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
public boolean isReinflateRequired() {
@@ -150,12 +183,18 @@
mLongPressHelper.cancelLongPress();
return true;
}
+
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
+ DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+
+ if (mIsScrollable) {
+ dragLayer.requestDisallowInterceptTouchEvent(true);
+ }
if (!mStylusEventHelper.inStylusButtonPressed()) {
mLongPressHelper.postCheckForLongPress();
}
- Launcher.getLauncher(getContext()).getDragLayer().setTouchCompleteListener(this);
+ dragLayer.setTouchCompleteListener(this);
break;
}
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
index b3e73f7..140794b 100644
--- a/src/com/android/launcher3/LauncherBackupAgent.java
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -5,11 +5,19 @@
import android.app.backup.BackupDataOutput;
import android.os.ParcelFileDescriptor;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.RestoreDbTask;
public class LauncherBackupAgent extends BackupAgent {
@Override
+ public void onCreate() {
+ super.onCreate();
+ // Set the log dir as LauncherAppState is not initialized during restore.
+ FileLog.setDir(getFilesDir());
+ }
+
+ @Override
public void onRestore(
BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
// Doesn't do incremental backup/restore
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 58a7495..56892ff 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -24,21 +24,16 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
-import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray;
@@ -50,18 +45,17 @@
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.CacheDataUpdatedTask;
import com.android.launcher3.model.ExtendedModelTask;
import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.model.LoaderCursor;
import com.android.launcher3.model.PackageInstallStateChangedTask;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.PackageUpdatedTask;
@@ -77,10 +71,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.CursorIconInfo;
-import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
@@ -91,7 +82,6 @@
import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
-import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -1096,96 +1086,6 @@
}
}
- // check & update map of what's occupied; used to discard overlapping/invalid items
- private boolean checkItemPlacement(LongArrayMap<GridOccupancy> occupied, ItemInfo item,
- ArrayList<Long> workspaceScreens) {
- LauncherAppState app = LauncherAppState.getInstance();
- InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-
- long containerIndex = item.screenId;
- if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- // Return early if we detect that an item is under the hotseat button
- if (!FeatureFlags.NO_ALL_APPS_ICON &&
- profile.isAllAppsButtonRank((int) item.screenId)) {
- Log.e(TAG, "Error loading shortcut into hotseat " + item
- + " into position (" + item.screenId + ":" + item.cellX + ","
- + item.cellY + ") occupied by all apps");
- return false;
- }
-
- final GridOccupancy hotseatOccupancy =
- occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
-
- if (item.screenId >= profile.numHotseatIcons) {
- Log.e(TAG, "Error loading shortcut " + item
- + " into hotseat position " + item.screenId
- + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1)
- + ")");
- return false;
- }
-
- if (hotseatOccupancy != null) {
- if (hotseatOccupancy.cells[(int) item.screenId][0]) {
- Log.e(TAG, "Error loading shortcut into hotseat " + item
- + " into position (" + item.screenId + ":" + item.cellX + ","
- + item.cellY + ") already occupied");
- return false;
- } else {
- hotseatOccupancy.cells[(int) item.screenId][0] = true;
- return true;
- }
- } else {
- final GridOccupancy occupancy = new GridOccupancy(profile.numHotseatIcons, 1);
- occupancy.cells[(int) item.screenId][0] = true;
- occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
- return true;
- }
- } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (!workspaceScreens.contains((Long) item.screenId)) {
- // The item has an invalid screen id.
- return false;
- }
- } else {
- // Skip further checking if it is not the hotseat or workspace container
- return true;
- }
-
- final int countX = profile.numColumns;
- final int countY = profile.numRows;
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- item.cellX < 0 || item.cellY < 0 ||
- item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
- Log.e(TAG, "Error loading shortcut " + item
- + " into cell (" + containerIndex + "-" + item.screenId + ":"
- + item.cellX + "," + item.cellY
- + ") out of screen bounds ( " + countX + "x" + countY + ")");
- return false;
- }
-
- if (!occupied.containsKey(item.screenId)) {
- GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
- if (item.screenId == Workspace.FIRST_SCREEN_ID) {
- // Mark the first row as occupied (if the feature is enabled)
- // in order to account for the QSB.
- screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
- }
- occupied.put(item.screenId, screen);
- }
- final GridOccupancy occupancy = occupied.get(item.screenId);
-
- // Check if any workspace icons overlap with each other
- if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
- occupancy.markCells(item, true);
- return true;
- } else {
- Log.e(TAG, "Error loading shortcut " + item
- + " into cell (" + containerIndex + "-" + item.screenId + ":"
- + item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
- + ") already occupied");
- return false;
- }
- }
-
private void loadWorkspace() {
if (LauncherAppState.PROFILE_STARTUP) {
Trace.beginSection("Loading Workspace");
@@ -1237,37 +1137,19 @@
.getInstance(mContext).updateAndGetActiveSessionCache();
sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
- final ArrayList<Long> itemsToRemove = new ArrayList<>();
- final ArrayList<Long> restoredRows = new ArrayList<>();
Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
- final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
- if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
- final Cursor c = contentResolver.query(contentUri, null, null, null, null);
+ final LoaderCursor c = new LoaderCursor(contentResolver.query(
+ LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
- // +1 for the hotseat (it can be larger than the workspace)
- // Load workspace in reverse order to ensure that latest items are loaded first (and
- // before any earlier duplicates)
- final LongArrayMap<GridOccupancy> occupied = new LongArrayMap<>();
HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
try {
- final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
final int intentIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.INTENT);
- final int containerIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.CONTAINER);
- final int itemTypeIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.ITEM_TYPE);
final int appWidgetIdIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_ID);
final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_PROVIDER);
- final int screenIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.SCREEN);
- final int cellXIndex = c.getColumnIndexOrThrow
- (LauncherSettings.Favorites.CELLX);
- final int cellYIndex = c.getColumnIndexOrThrow
- (LauncherSettings.Favorites.CELLY);
final int spanXIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.SPANX);
final int spanYIndex = c.getColumnIndexOrThrow(
@@ -1276,13 +1158,10 @@
LauncherSettings.Favorites.RANK);
final int restoredIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.RESTORED);
- final int profileIdIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.PROFILE_ID);
final int optionsIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.OPTIONS);
- final CursorIconInfo cursorIconInfo = new CursorIconInfo(mContext, c);
- final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
+ final LongSparseArray<UserHandle> allUsers = c.allUsers;
final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
for (UserHandle user : mUserManager.getUserProfiles()) {
@@ -1314,45 +1193,42 @@
ShortcutInfo info;
String intentDescription;
LauncherAppWidgetInfo appWidgetInfo;
- int container;
- long id;
- long serialNumber;
Intent intent;
- UserHandle user;
String targetPackage;
while (!mStopped && c.moveToNext()) {
try {
- int itemType = c.getInt(itemTypeIndex);
+ if (c.user == null) {
+ // User has been deleted, remove the item.
+ c.markDeleted("User has been deleted");
+ continue;
+ }
+
boolean restored = 0 != c.getInt(restoredIndex);
boolean allowMissingTarget = false;
- container = c.getInt(containerIndex);
-
- switch (itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- id = c.getLong(idIndex);
- intentDescription = c.getString(intentIndex);
- serialNumber = c.getInt(profileIdIndex);
- user = allUsers.get(serialNumber);
- int promiseType = c.getInt(restoredIndex);
- int disabledState = 0;
- boolean itemReplaced = false;
- targetPackage = null;
- if (user == null) {
- // User has been deleted remove the item.
- itemsToRemove.add(id);
+ switch (c.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: {
+ if (!Process.myUserHandle().equals(c.user)) {
+ c.markDeleted("Legacy shortcuts are only allowed for default user");
continue;
}
+ // Follow through.
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ intentDescription = c.getString(intentIndex);
+ int promiseType = c.getInt(restoredIndex);
+ int disabledState = 0;
+ targetPackage = null;
+
try {
intent = Intent.parseUri(intentDescription, 0);
ComponentName cn = intent.getComponent();
if (cn != null && cn.getPackageName() != null) {
boolean validPkg = launcherApps.isPackageEnabledForProfile(
- cn.getPackageName(), user);
+ cn.getPackageName(), c.user);
boolean validComponent = validPkg &&
- launcherApps.isActivityEnabledForProfile(cn, user);
+ launcherApps.isActivityEnabledForProfile(cn, c.user);
if (validPkg) {
targetPackage = cn.getPackageName();
}
@@ -1360,10 +1236,10 @@
if (validComponent) {
if (restored) {
// no special handling necessary for this item
- restoredRows.add(id);
+ c.markRestored();
restored = false;
}
- if (quietMode.get(serialNumber)) {
+ if (quietMode.get(c.serialNumber)) {
disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
}
} else if (validPkg) {
@@ -1374,22 +1250,20 @@
intent = manager.getLaunchIntentForPackage(
cn.getPackageName());
if (intent != null) {
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.INTENT,
- intent.toUri(0));
- updateItem(id, values);
+ c.updater().put(
+ LauncherSettings.Favorites.INTENT,
+ intent.toUri(0)).commit();
}
}
if (intent == null) {
// The app is installed but the component is no
// longer available.
- FileLog.d(TAG, "Invalid component removed: " + cn);
- itemsToRemove.add(id);
+ c.markDeleted("Invalid component removed: " + cn);
continue;
} else {
// no special handling necessary for this item
- restoredRows.add(id);
+ c.markRestored();
restored = false;
}
} else if (restored) {
@@ -1402,32 +1276,11 @@
} else if (installingPkgs.containsKey(cn.getPackageName())) {
// App restore has started. Update the flag
promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.RESTORED,
- promiseType);
- updateItem(id, values);
- } else if ((promiseType & ShortcutInfo.FLAG_RESTORED_APP_TYPE) != 0) {
- // This is a common app. Try to replace this.
- int appType = CommonAppTypeParser.decodeItemTypeFromFlag(promiseType);
- CommonAppTypeParser parser = new CommonAppTypeParser(id, appType, context);
- if (parser.findDefaultApp()) {
- // Default app found. Replace it.
- intent = parser.parsedIntent;
- cn = intent.getComponent();
- ContentValues values = parser.parsedValues;
- values.put(LauncherSettings.Favorites.RESTORED, 0);
- updateItem(id, values);
- restored = false;
- itemReplaced = true;
-
- } else {
- FileLog.d(TAG, "Unrestored package removed: " + cn);
- itemsToRemove.add(id);
- continue;
- }
+ c.updater().put(
+ LauncherSettings.Favorites.RESTORED,
+ promiseType).commit();
} else {
- FileLog.d(TAG, "Unrestored package removed: " + cn);
- itemsToRemove.add(id);
+ c.markDeleted("Unrestored package removed: " + cn);
continue;
}
} else if (PackageManagerHelper.isAppOnSdcard(
@@ -1439,79 +1292,64 @@
// SdCard is not ready yet. Package might get available,
// once it is ready.
Log.d(TAG, "Invalid package: " + cn + " (check again later)");
- pendingPackages.addToList(user, cn.getPackageName());
+ pendingPackages.addToList(c.user, cn.getPackageName());
allowMissingTarget = true;
// Add the icon on the workspace anyway.
} else {
// Do not wait for external media load anymore.
// Log the invalid package, and remove it
- FileLog.d(TAG, "Invalid package removed: " + cn);
- itemsToRemove.add(id);
+ c.markDeleted("Invalid package removed: " + cn);
continue;
}
} else if (cn == null) {
// For shortcuts with no component, keep them as they are
- restoredRows.add(id);
+ c.markRestored();
restored = false;
}
} catch (URISyntaxException e) {
- FileLog.d(TAG, "Invalid uri: " + intentDescription);
- itemsToRemove.add(id);
+ c.markDeleted("Invalid uri: " + intentDescription);
continue;
}
- boolean useLowResIcon = container >= 0 &&
+ boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
- if (itemReplaced) {
- if (user.equals(Process.myUserHandle())) {
- info = getAppShortcutInfo(intent, user, null,
- cursorIconInfo, false, useLowResIcon);
- } else {
- // Don't replace items for other profiles.
- itemsToRemove.add(id);
- continue;
- }
- } else if (restored) {
- if (user.equals(Process.myUserHandle())) {
- info = getRestoredItemInfo(c, intent,
- promiseType, itemType, cursorIconInfo);
- intent = getRestoredItemIntent(c, context, intent);
+ if (restored) {
+ if (c.user.equals(Process.myUserHandle())) {
+ info = c.getRestoredItemInfo(intent, promiseType);
+ intent = PackageManagerHelper.getMarketIntent(
+ intent.getComponent().getPackageName());
} else {
// Don't restore items for other profiles.
- itemsToRemove.add(id);
+ c.markDeleted("Restore from managed profile not supported");
continue;
}
- } else if (itemType ==
+ } else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- info = getAppShortcutInfo(intent, user, c,
- cursorIconInfo, allowMissingTarget, useLowResIcon);
- } else if (itemType ==
+ info = c.getAppShortcutInfo(
+ intent, allowMissingTarget, useLowResIcon);
+ } else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- ShortcutKey key = ShortcutKey.fromIntent(intent, user);
- if (unlockedUsers.get(serialNumber)) {
+ ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
+ if (unlockedUsers.get(c.serialNumber)) {
ShortcutInfoCompat pinnedShortcut =
shortcutKeyToPinnedShortcuts.get(key);
if (pinnedShortcut == null) {
// The shortcut is no longer valid.
- itemsToRemove.add(id);
+ c.markDeleted("Pinned shortcut not found");
continue;
}
info = new ShortcutInfo(pinnedShortcut, context);
intent = info.intent;
} else {
// Create a shortcut info in disabled mode for now.
- info = new ShortcutInfo();
- info.user = user;
- info.itemType = itemType;
- loadInfoFromCursor(info, c, cursorIconInfo);
-
+ info = c.loadSimpleShortcut();
info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
}
} else { // item type == ITEM_TYPE_SHORTCUT
- info = getShortcutInfo(c, cursorIconInfo);
+ info = c.loadSimpleShortcut();
// Shortcuts are only available on the primary profile
if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
@@ -1532,30 +1370,23 @@
}
if (info != null) {
- info.id = id;
+ c.applyCommonProperties(info);
+
info.intent = intent;
- info.container = container;
- info.screenId = c.getInt(screenIndex);
- info.cellX = c.getInt(cellXIndex);
- info.cellY = c.getInt(cellYIndex);
info.rank = c.getInt(rankIndex);
info.spanX = 1;
info.spanY = 1;
- info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
+ // TODO: Remove this extra. Instead we should be using
+ // itemInfo#user.
+ info.intent.putExtra(ItemInfo.EXTRA_PROFILE, c.serialNumber);
if (info.promisedIntent != null) {
- info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
+ info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, c.serialNumber);
}
info.isDisabled |= disabledState;
if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
}
- // check & update map of what's occupied
- if (!checkItemPlacement(occupied, info, sBgDataModel.workspaceScreens)) {
- itemsToRemove.add(id);
- break;
- }
-
if (restored) {
ComponentName cn = info.getTargetComponent();
if (cn != null) {
@@ -1568,55 +1399,38 @@
}
}
- sBgDataModel.addItem(info, false);
+ c.checkAndAddItem(info, sBgDataModel);
} else {
throw new RuntimeException("Unexpected null ShortcutInfo");
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- id = c.getLong(idIndex);
- FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(id);
+ FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
+ c.applyCommonProperties(folderInfo);
// Do not trim the folder label, as is was set by the user.
- folderInfo.title = c.getString(cursorIconInfo.titleIndex);
- folderInfo.id = id;
- folderInfo.container = container;
- folderInfo.screenId = c.getInt(screenIndex);
- folderInfo.cellX = c.getInt(cellXIndex);
- folderInfo.cellY = c.getInt(cellYIndex);
+ folderInfo.title = c.getString(c.titleIndex);
folderInfo.spanX = 1;
folderInfo.spanY = 1;
folderInfo.options = c.getInt(optionsIndex);
- // check & update map of what's occupied
- if (!checkItemPlacement(occupied, folderInfo, sBgDataModel.workspaceScreens)) {
- itemsToRemove.add(id);
- break;
- }
if (restored) {
// no special handling required for restored folders
- restoredRows.add(id);
+ c.markRestored();
}
- sBgDataModel.addItem(folderInfo, false);
+ c.checkAndAddItem(folderInfo, sBgDataModel);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
// Read all Launcher-specific widget details
- boolean customWidget = itemType ==
+ boolean customWidget = c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
int appWidgetId = c.getInt(appWidgetIdIndex);
- serialNumber = c.getLong(profileIdIndex);
String savedProvider = c.getString(appWidgetProviderIndex);
- id = c.getLong(idIndex);
- user = allUsers.get(serialNumber);
- if (user == null) {
- itemsToRemove.add(id);
- continue;
- }
final ComponentName component =
ComponentName.unflattenFromString(savedProvider);
@@ -1634,14 +1448,14 @@
final AppWidgetProviderInfo provider = widgetProvidersMap.get(
new ComponentKey(
ComponentName.unflattenFromString(savedProvider),
- user));
+ c.user));
final boolean isProviderReady = isValidProvider(provider);
if (!isSafeMode && !customWidget &&
wasProviderReady && !isProviderReady) {
- FileLog.d(TAG, "Deleting widget that isn't installed anymore: "
+ c.markDeleted(
+ "Deleting widget that isn't installed anymore: "
+ provider);
- itemsToRemove.add(id);
} else {
if (isProviderReady) {
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
@@ -1666,7 +1480,7 @@
}
appWidgetInfo.restoreStatus = status;
} else {
- Log.v(TAG, "Widget restore pending id=" + id
+ Log.v(TAG, "Widget restore pending id=" + c.id
+ " appWidgetId=" + appWidgetId
+ " status =" + restoreStatus);
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
@@ -1681,8 +1495,7 @@
appWidgetInfo.restoreStatus |=
LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
} else if (!isSafeMode) {
- FileLog.d(TAG, "Unrestored widget removed: " + component);
- itemsToRemove.add(id);
+ c.markDeleted("Unrestored widget removed: " + component);
continue;
}
@@ -1698,44 +1511,31 @@
}
}
- appWidgetInfo.id = id;
- appWidgetInfo.screenId = c.getInt(screenIndex);
- appWidgetInfo.cellX = c.getInt(cellXIndex);
- appWidgetInfo.cellY = c.getInt(cellYIndex);
+ c.applyCommonProperties(appWidgetInfo);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
- appWidgetInfo.user = user;
+ appWidgetInfo.user = c.user;
- if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- Log.e(TAG, "Widget found where container != " +
+ if (!c.isOnWorkspaceOrHotseat()) {
+ c.markDeleted("Widget found where container != " +
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
- itemsToRemove.add(id);
continue;
}
- appWidgetInfo.container = container;
- // check & update map of what's occupied
- if (!checkItemPlacement(occupied, appWidgetInfo, sBgDataModel.workspaceScreens)) {
- itemsToRemove.add(id);
- break;
- }
-
if (!customWidget) {
String providerName =
appWidgetInfo.providerName.flattenToString();
if (!providerName.equals(savedProvider) ||
(appWidgetInfo.restoreStatus != restoreStatus)) {
- ContentValues values = new ContentValues();
- values.put(
- LauncherSettings.Favorites.APPWIDGET_PROVIDER,
- providerName);
- values.put(LauncherSettings.Favorites.RESTORED,
- appWidgetInfo.restoreStatus);
- updateItem(id, values);
+ c.updater()
+ .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+ providerName)
+ .put(LauncherSettings.Favorites.RESTORED,
+ appWidgetInfo.restoreStatus)
+ .commit();
}
}
- sBgDataModel.addItem(appWidgetInfo, false);
+ c.checkAndAddItem(appWidgetInfo, sBgDataModel);
}
break;
}
@@ -1753,16 +1553,8 @@
return;
}
- if (itemsToRemove.size() > 0) {
- // Remove dead items
- contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI,
- Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, itemsToRemove), null);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, itemsToRemove));
- }
-
+ // Remove dead items
+ if (c.commitDeleted()) {
// Remove any empty folder
ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
.call(contentResolver,
@@ -1803,15 +1595,7 @@
}
}
- if (restoredRows.size() > 0) {
- // Update restored items that no longer require special handling
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.RESTORED, 0);
- contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
- Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, restoredRows), null);
- }
-
+ c.commitRestoredItems();
if (!isSdCardReady && !pendingPackages.isEmpty()) {
context.registerReceiver(
new SdCardAvailableReceiver(
@@ -1822,7 +1606,7 @@
}
// Remove any empty screens
- ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgDataModel.workspaceScreens);
+ ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
for (ItemInfo item: sBgDataModel.itemsIdMap) {
long screenId = item.screenId;
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
@@ -1836,40 +1620,12 @@
sBgDataModel.workspaceScreens.removeAll(unusedScreens);
updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
}
-
- if (DEBUG_LOADERS) {
- Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
- Log.d(TAG, "workspace layout: ");
- int nScreens = occupied.size();
- for (int y = 0; y < countY; y++) {
- String line = "";
-
- for (int i = 0; i < nScreens; i++) {
- long screenId = occupied.keyAt(i);
- if (screenId > 0) {
- line += " | ";
- }
- }
- Log.d(TAG, "[ " + line + " ]");
- }
- }
}
if (LauncherAppState.PROFILE_STARTUP) {
Trace.endSection();
}
}
- /**
- * Partially updates the item without any notification. Must be called on the worker thread.
- */
- private void updateItem(long itemId, ContentValues update) {
- mContext.getContentResolver().update(
- LauncherSettings.Favorites.CONTENT_URI,
- update,
- BaseColumns._ID + "= ?",
- new String[]{Long.toString(itemId)});
- }
-
/** Filters the set of items who are directly or indirectly (via another container) on the
* specified screen. */
private void filterCurrentWorkspaceItems(long currentScreenId,
@@ -2273,7 +2029,7 @@
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfoCompat app = apps.get(i);
// This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode));
+ mBgAllAppsList.add(new AppInfo(mContext, app, user, quietMode), app);
}
final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
@@ -2465,17 +2221,16 @@
}
/**
- * Repopulates the shortcut info, possibly updating any icon already on the workspace.
+ * Utility method to update a shortcut on the background thread.
*/
- public void updateShortcutInfo(final ShortcutInfoCompat fullDetail, final ShortcutInfo info) {
+ public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) {
enqueueModelUpdateTask(new ExtendedModelTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- info.updateFromDeepShortcutInfo(fullDetail, app.getContext());
-
+ ShortcutInfo info = shortcutProvider.get();
ArrayList<ShortcutInfo> update = new ArrayList<>();
update.add(info);
- bindUpdatedShortcuts(update, fullDetail.getUserHandle());
+ bindUpdatedShortcuts(update, info.user);
}
});
}
@@ -2511,179 +2266,6 @@
});
}
- /**
- * Make an ShortcutInfo object for a restored application or shortcut item that points
- * to a package that is not yet installed on the system.
- */
- public ShortcutInfo getRestoredItemInfo(Cursor c, Intent intent,
- int promiseType, int itemType, CursorIconInfo iconInfo) {
- final ShortcutInfo info = new ShortcutInfo();
- info.user = Process.myUserHandle();
- info.promisedIntent = intent;
-
- info.iconBitmap = iconInfo.loadIcon(c, info);
- // the fallback icon
- if (info.iconBitmap == null) {
- mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
- }
-
- if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
- String title = iconInfo.getTitle(c);
- if (!TextUtils.isEmpty(title)) {
- info.title = Utilities.trim(title);
- }
- } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
- if (TextUtils.isEmpty(info.title)) {
- info.title = iconInfo.getTitle(c);
- }
- } else {
- throw new InvalidParameterException("Invalid restoreType " + promiseType);
- }
-
- info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
- info.itemType = itemType;
- info.status = promiseType;
- return info;
- }
-
- /**
- * Make an Intent object for a restored application or shortcut item that points
- * to the market page for the item.
- */
- @Thunk Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
- ComponentName componentName = intent.getComponent();
- return getMarketIntent(componentName.getPackageName());
- }
-
- static Intent getMarketIntent(String packageName) {
- return new Intent(Intent.ACTION_VIEW)
- .setData(new Uri.Builder()
- .scheme("market")
- .authority("details")
- .appendQueryParameter("id", packageName)
- .build());
- }
-
- /**
- * Make an ShortcutInfo object for a shortcut that is an application.
- *
- * If c is not null, then it will be used to fill in missing data like the title and icon.
- */
- public ShortcutInfo getAppShortcutInfo(Intent intent, UserHandle user, Cursor c,
- CursorIconInfo iconInfo, boolean allowMissingTarget, boolean useLowResIcon) {
- if (user == null) {
- Log.d(TAG, "Null user found in getShortcutInfo");
- return null;
- }
-
- ComponentName componentName = intent.getComponent();
- if (componentName == null) {
- Log.d(TAG, "Missing component found in getShortcutInfo");
- return null;
- }
-
- Intent newIntent = new Intent(intent.getAction(), null);
- newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- newIntent.setComponent(componentName);
- LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
- if ((lai == null) && !allowMissingTarget) {
- Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
- return null;
- }
-
- final ShortcutInfo info = new ShortcutInfo();
- info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- info.user = user;
- info.intent = newIntent;
-
- mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
- if (mIconCache.isDefaultIcon(info.iconBitmap, user) && c != null) {
- Bitmap icon = iconInfo.loadIcon(c);
- info.iconBitmap = icon != null ? icon : mIconCache.getDefaultIcon(user);
- }
-
- if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
- info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
- }
-
- // from the db
- if (TextUtils.isEmpty(info.title) && c != null) {
- info.title = iconInfo.getTitle(c);
- }
-
- // fall back to the class name of the activity
- if (info.title == null) {
- info.title = componentName.getClassName();
- }
-
- info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
- return info;
- }
-
- /**
- * Make an ShortcutInfo object for a shortcut that isn't an application.
- */
- @Thunk ShortcutInfo getShortcutInfo(Cursor c, CursorIconInfo iconInfo) {
- final ShortcutInfo info = new ShortcutInfo();
- // Non-app shortcuts are only supported for current user.
- info.user = Process.myUserHandle();
- info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-
- // TODO: If there's an explicit component and we can't install that, delete it.
-
- loadInfoFromCursor(info, c, iconInfo);
- return info;
- }
-
- /**
- * Make an ShortcutInfo object for a shortcut that isn't an application.
- */
- public void loadInfoFromCursor(ShortcutInfo info, Cursor c, CursorIconInfo iconInfo) {
- info.title = iconInfo.getTitle(c);
- info.iconBitmap = iconInfo.loadIcon(c, info);
- // the fallback icon
- if (info.iconBitmap == null) {
- info.iconBitmap = mIconCache.getDefaultIcon(info.user);
- }
- }
-
- ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
- Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
- String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
- Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
-
- if (intent == null) {
- // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
- Log.e(TAG, "Can't construct ShorcutInfo with null intent");
- return null;
- }
-
- final ShortcutInfo info = new ShortcutInfo();
-
- // Only support intents for current user for now. Intents sent from other
- // users wouldn't get here without intent forwarding anyway.
- info.user = Process.myUserHandle();
-
- if (bitmap instanceof Bitmap) {
- info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, context);
- } else {
- Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
- if (extra instanceof ShortcutIconResource) {
- info.iconResource = (ShortcutIconResource) extra;
- info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, context);
- }
- }
- if (info.iconBitmap == null) {
- info.iconBitmap = mIconCache.getDefaultIcon(info.user);
- }
-
- info.title = Utilities.trim(name);
- info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
- info.intent = intent;
-
- return info;
- }
-
static boolean isValidProvider(AppWidgetProviderInfo provider) {
return (provider != null) && (provider.provider != null)
&& (provider.provider.getPackageName() != null);
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 2976807..3256df6 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -17,7 +17,6 @@
package com.android.launcher3;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -35,8 +34,11 @@
import android.view.View.OnClickListener;
import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.model.PackageItemInfo;
-public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
+public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
+ implements OnClickListener, ItemInfoUpdateReceiver {
private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
private static final float MIN_SATUNATION = 0.7f;
@@ -47,7 +49,6 @@
private OnClickListener mClickListener;
private final LauncherAppWidgetInfo mInfo;
private final int mStartState;
- private final Intent mIconLookupIntent;
private final boolean mDisabledForSafeMode;
private Launcher mLauncher;
@@ -68,7 +69,6 @@
mLauncher = Launcher.getLauncher(context);
mInfo = info;
mStartState = info.restoreStatus;
- mIconLookupIntent = new Intent().setComponent(info.providerName);
mDisabledForSafeMode = disabledForSafeMode;
mPaint = new TextPaint();
@@ -79,9 +79,13 @@
setWillNotDraw(false);
setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
- updateIcon(cache);
updateAppWidget(null);
setOnClickListener(mLauncher);
+
+ // Load icon
+ PackageItemInfo item = new PackageItemInfo(info.providerName.getPackageName());
+ item.user = info.user;
+ cache.updateIconInBackground(this, item);
}
@Override
@@ -117,8 +121,9 @@
mDrawableSizeChanged = true;
}
- private void updateIcon(IconCache cache) {
- Bitmap icon = cache.getIcon(mIconLookupIntent, mInfo.user);
+ @Override
+ public void reapplyItemInfo(ItemInfoWithIcon info) {
+ Bitmap icon = info.iconBitmap;
if (mIcon == icon) {
return;
}
@@ -157,6 +162,7 @@
}
mDrawableSizeChanged = true;
}
+ invalidate();
}
private void updateSettingColor() {
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 05fb1ed..77c6837 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -68,6 +68,7 @@
* Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}.
* Upto 15 different types supported.
*/
+ @Deprecated
public static final int FLAG_RESTORED_APP_TYPE = 0B0011110000;
/**
@@ -253,6 +254,9 @@
AppInfo appInfo = new AppInfo();
appInfo.user = user;
appInfo.componentName = shortcutInfo.getActivity();
+ appInfo.intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(shortcutInfo.getActivity());
cache.getTitleAndIcon(appInfo, false);
return LauncherIcons.badgeWithBitmap(unbadgedBitmap, appInfo.iconBitmap, context);
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 2cb9138..89ffd31 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -104,6 +104,12 @@
public static final boolean ATLEAST_LOLLIPOP_MR1 =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
+ /**
+ * Indicates if the device has a debug build. Should only be used to store additional info or
+ * add extra logging and not for changing the app behavior.
+ */
+ public static final boolean IS_DEBUG_DEVICE = Build.TYPE.toLowerCase().contains("debug");
+
// An intent extra to indicate the horizontal scroll of the wallpaper.
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index bc602f3..c71bc31 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -23,71 +23,73 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-
import android.view.Gravity;
+
import com.android.launcher3.R;
/**
- * A helper class to positon and orient a drawable to be drawn.
- */
-class TransformedImageDrawable {
- private Drawable mImage;
- private float mXPercent;
- private float mYPercent;
- private int mGravity;
- private int mAlpha;
-
- /**
- * @param gravity If one of the Gravity center values, the x and y offset will take the width
- * and height of the image into account to center the image to the offset.
- */
- public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
- int gravity) {
- mImage = res.getDrawable(resourceId);
- mXPercent = xPct;
- mYPercent = yPct;
- mGravity = gravity;
- }
-
- public void setAlpha(int alpha) {
- mImage.setAlpha(alpha);
- mAlpha = alpha;
- }
-
- public int getAlpha() {
- return mAlpha;
- }
-
- public void updateBounds(Rect bounds) {
- int width = mImage.getIntrinsicWidth();
- int height = mImage.getIntrinsicHeight();
- int left = bounds.left + (int) (mXPercent * bounds.width());
- int top = bounds.top + (int) (mYPercent * bounds.height());
- if ((mGravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
- left -= (width / 2);
- }
- if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
- top -= (height / 2);
- }
- mImage.setBounds(left, top, left + width, top + height);
- }
-
- public void draw(Canvas canvas) {
- int c = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- mImage.draw(canvas);
- canvas.restoreToCount(c);
- }
-}
-
-/**
* This is a custom composite drawable that has a fixed virtual size and dynamically lays out its
* children images relatively within its bounds. This way, we can reduce the memory usage of a
* single, large sparsely populated image.
*/
public class AllAppsBackgroundDrawable extends Drawable {
- private final TransformedImageDrawable mHand;
- private final TransformedImageDrawable[] mIcons;
+ /**
+ * A helper class to positon and orient a drawable to be drawn.
+ */
+ protected static class TransformedImageDrawable {
+ private Drawable mImage;
+ private float mXPercent;
+ private float mYPercent;
+ private int mGravity;
+ private int mAlpha;
+
+ /**
+ * @param gravity If one of the Gravity center values, the x and y offset will take the width
+ * and height of the image into account to center the image to the offset.
+ */
+ public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
+ int gravity) {
+ mImage = res.getDrawable(resourceId);
+ mXPercent = xPct;
+ mYPercent = yPct;
+ mGravity = gravity;
+ }
+
+ public void setAlpha(int alpha) {
+ mImage.setAlpha(alpha);
+ mAlpha = alpha;
+ }
+
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ public void updateBounds(Rect bounds) {
+ int width = mImage.getIntrinsicWidth();
+ int height = mImage.getIntrinsicHeight();
+ int left = bounds.left + (int) (mXPercent * bounds.width());
+ int top = bounds.top + (int) (mYPercent * bounds.height());
+ if ((mGravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
+ left -= (width / 2);
+ }
+ if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
+ top -= (height / 2);
+ }
+ mImage.setBounds(left, top, left + width, top + height);
+ }
+
+ public void draw(Canvas canvas) {
+ mImage.draw(canvas);
+ }
+
+ public Rect getBounds() {
+ return mImage.getBounds();
+ }
+ }
+
+ protected final TransformedImageDrawable mHand;
+ protected final TransformedImageDrawable[] mIcons;
private final int mWidth;
private final int mHeight;
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 10d4c15..a41d832 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -22,6 +22,7 @@
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.SparseIntArray;
+import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
@@ -29,6 +30,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import java.util.List;
@@ -207,7 +209,8 @@
if (mApps.hasNoFilteredResults()) {
if (mEmptySearchBackground == null) {
- mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext());
+ mEmptySearchBackground = DrawableFactory.get(getContext())
+ .getAllAppsBackground(getContext());
mEmptySearchBackground.setAlpha(0);
mEmptySearchBackground.setCallback(this);
updateEmptySearchBackgroundBounds();
@@ -220,6 +223,16 @@
}
}
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent e) {
+ boolean result = super.onInterceptTouchEvent(e);
+ if (!result && e.getAction() == MotionEvent.ACTION_DOWN
+ && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
+ mEmptySearchBackground.setHotspot(e.getX(), e.getY());
+ }
+ return result;
+ }
+
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c199fef..547ab2b 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -18,15 +18,12 @@
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
@@ -88,7 +85,6 @@
private static final float RECATCH_REJECTION_FRACTION = .0875f;
- private int mBezelSwipeUpHeight;
private long mAnimationDuration;
private AnimatorSet mCurrentAnimation;
@@ -104,8 +100,6 @@
mDetector.setListener(this);
mShiftRange = DEFAULT_SHIFT_RANGE;
mProgress = 1f;
- mBezelSwipeUpHeight = l.getResources().getDimensionPixelSize(
- R.dimen.all_apps_bezel_swipe_height);
mEvaluator = new ArgbEvaluator();
mAllAppsBackgroundColor = ContextCompat.getColor(l, R.color.all_apps_container_color);
@@ -120,8 +114,6 @@
} else if (mLauncher.isAllAppsVisible() &&
!mAppsView.shouldContainerScroll(ev)) {
mNoIntercept = true;
- } else if (!mLauncher.isAllAppsVisible() && !shouldPossiblyIntercept(ev)) {
- mNoIntercept = true;
} else if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
mNoIntercept = true;
} else {
@@ -150,6 +142,7 @@
ignoreSlopWhenSettling);
}
}
+
if (mNoIntercept) {
return false;
}
@@ -160,25 +153,6 @@
return mDetector.isDraggingOrSettling();
}
- private boolean shouldPossiblyIntercept(MotionEvent ev) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- if (mDetector.isIdleState()) {
- if (grid.isVerticalBarLayout()) {
- if (ev.getY() > mLauncher.getDeviceProfile().heightPx - mBezelSwipeUpHeight) {
- return true;
- }
- } else {
- if (mLauncher.getDragLayer().isEventOverHotseat(ev) ||
- mLauncher.getDragLayer().isEventOverPageIndicator(ev)) {
- return true;
- }
- }
- return false;
- } else {
- return true;
- }
- }
-
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
return mDetector.onTouchEvent(ev);
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index f6a4b84..693df7a 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -23,6 +23,7 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
/**
* Factory for creating new drawables.
@@ -50,4 +51,8 @@
d.setFilterBitmap(true);
return d;
}
+
+ public AllAppsBackgroundDrawable getAllAppsBackground(Context context) {
+ return new AllAppsBackgroundDrawable(context);
+ }
}
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 3f191bd..0619187 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -20,9 +20,7 @@
import android.content.Intent.ShortcutIconResource;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
@@ -51,16 +49,6 @@
Paint.FILTER_BITMAP_FLAG));
}
-
- public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
- byte[] data = c.getBlob(iconIndex);
- try {
- return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context);
- } catch (Exception e) {
- return null;
- }
- }
-
/**
* Returns a bitmap suitable for the all apps view. If the package or the resource do not
* exist, it returns null.
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 8629e92..ffb41b7 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -40,7 +40,7 @@
private static File sLogsDirectory = null;
public static void setDir(File logsDir) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
synchronized (DATE_FORMAT) {
// If the target directory changes, stop any active thread.
if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
new file mode 100644
index 0000000..99a6cdf
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -0,0 +1,445 @@
+/*
+ * 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 com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Intent.ShortcutIconResource;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.UserHandle;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.PackageManagerHelper;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+
+/**
+ * Extension of {@link Cursor} with utility methods for workspace loading.
+ */
+public class LoaderCursor extends CursorWrapper {
+
+ private static final String TAG = "LoaderCursor";
+
+ public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
+
+ private final Context mContext;
+ private final UserManagerCompat mUserManager;
+ private final IconCache mIconCache;
+ private final InvariantDeviceProfile mIDP;
+
+ private final ArrayList<Long> itemsToRemove = new ArrayList<>();
+ private final ArrayList<Long> restoredRows = new ArrayList<>();
+ private final LongArrayMap<GridOccupancy> occupied = new LongArrayMap<>();
+
+ private final int iconPackageIndex;
+ private final int iconResourceIndex;
+ private final int iconIndex;
+ public final int titleIndex;
+
+ private final int idIndex;
+ private final int containerIndex;
+ private final int itemTypeIndex;
+ private final int screenIndex;
+ private final int cellXIndex;
+ private final int cellYIndex;
+ private final int profileIdIndex;
+
+ // Properties loaded per iteration
+ public long serialNumber;
+ public UserHandle user;
+ public long id;
+ public long container;
+ public int itemType;
+
+ public LoaderCursor(Cursor c, LauncherAppState app) {
+ super(c);
+ mContext = app.getContext();
+ mIconCache = app.getIconCache();
+ mIDP = app.getInvariantDeviceProfile();
+ mUserManager = UserManagerCompat.getInstance(mContext);
+
+ // Init column indices
+ iconIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
+ iconPackageIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
+ iconResourceIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
+ titleIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
+
+ idIndex = getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
+ containerIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
+ itemTypeIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+ screenIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+ cellXIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+ cellYIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+ profileIdIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
+ }
+
+ @Override
+ public boolean moveToNext() {
+ boolean result = super.moveToNext();
+ if (result) {
+ // Load common properties.
+ itemType = getInt(itemTypeIndex);
+ container = getInt(containerIndex);
+ id = getLong(idIndex);
+ serialNumber = getInt(profileIdIndex);
+ user = allUsers.get(serialNumber);
+ }
+ return result;
+ }
+
+ public ShortcutInfo loadSimpleShortcut() {
+ final ShortcutInfo info = new ShortcutInfo();
+ // Non-app shortcuts are only supported for current user.
+ info.user = user;
+ info.itemType = itemType;
+ info.title = getTitle();
+ info.iconBitmap = loadIcon(info);
+ // the fallback icon
+ if (info.iconBitmap == null) {
+ info.iconBitmap = mIconCache.getDefaultIcon(info.user);
+ }
+
+ // TODO: If there's an explicit component and we can't install that, delete it.
+
+ return info;
+ }
+
+ /**
+ * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
+ */
+ protected Bitmap loadIcon(ShortcutInfo info) {
+ Bitmap icon = null;
+ if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ String packageName = getString(iconPackageIndex);
+ String resourceName = getString(iconResourceIndex);
+ if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
+ info.iconResource = new ShortcutIconResource();
+ info.iconResource.packageName = packageName;
+ info.iconResource.resourceName = resourceName;
+ icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
+ }
+ }
+ if (icon == null) {
+ // Failed to load from resource, try loading from DB.
+ byte[] data = getBlob(iconIndex);
+ try {
+ icon = LauncherIcons.createIconBitmap(
+ BitmapFactory.decodeByteArray(data, 0, data.length), mContext);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ return icon;
+ }
+
+ /**
+ * Returns the title or empty string
+ */
+ private String getTitle() {
+ String title = getString(titleIndex);
+ return TextUtils.isEmpty(title) ? "" : Utilities.trim(title);
+ }
+
+
+ /**
+ * Make an ShortcutInfo object for a restored application or shortcut item that points
+ * to a package that is not yet installed on the system.
+ */
+ public ShortcutInfo getRestoredItemInfo(Intent intent, int promiseType) {
+ final ShortcutInfo info = new ShortcutInfo();
+ info.user = user;
+ info.promisedIntent = intent;
+
+ info.iconBitmap = loadIcon(info);
+ // the fallback icon
+ if (info.iconBitmap == null) {
+ mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
+ }
+
+ if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
+ String title = getTitle();
+ if (!TextUtils.isEmpty(title)) {
+ info.title = Utilities.trim(title);
+ }
+ } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
+ if (TextUtils.isEmpty(info.title)) {
+ info.title = getTitle();
+ }
+ } else {
+ throw new InvalidParameterException("Invalid restoreType " + promiseType);
+ }
+
+ info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
+ info.itemType = itemType;
+ info.status = promiseType;
+ return info;
+ }
+
+ /**
+ * Make an ShortcutInfo object for a shortcut that is an application.
+ */
+ public ShortcutInfo getAppShortcutInfo(
+ Intent intent, boolean allowMissingTarget, boolean useLowResIcon) {
+ if (user == null) {
+ Log.d(TAG, "Null user found in getShortcutInfo");
+ return null;
+ }
+
+ ComponentName componentName = intent.getComponent();
+ if (componentName == null) {
+ Log.d(TAG, "Missing component found in getShortcutInfo");
+ return null;
+ }
+
+ Intent newIntent = new Intent(Intent.ACTION_MAIN, null);
+ newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ newIntent.setComponent(componentName);
+ LauncherActivityInfoCompat lai = LauncherAppsCompat.getInstance(mContext)
+ .resolveActivity(newIntent, user);
+ if ((lai == null) && !allowMissingTarget) {
+ Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
+ return null;
+ }
+
+ final ShortcutInfo info = new ShortcutInfo();
+ info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ info.user = user;
+ info.intent = newIntent;
+
+ mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
+ if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
+ Bitmap icon = loadIcon(info);
+ info.iconBitmap = icon != null ? icon : info.iconBitmap;
+ }
+
+ if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
+ info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+
+ // from the db
+ if (TextUtils.isEmpty(info.title)) {
+ info.title = getTitle();
+ }
+
+ // fall back to the class name of the activity
+ if (info.title == null) {
+ info.title = componentName.getClassName();
+ }
+
+ info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
+ return info;
+ }
+
+ /**
+ * Returns a {@link ContentWriter} which can be used to update the current item.
+ */
+ public ContentWriter updater() {
+ return new ContentWriter(mContext, new ContentWriter.CommitParams(
+ BaseColumns._ID + "= ?", new String[]{Long.toString(id)}));
+ }
+
+ /**
+ * Marks the current item for removal
+ */
+ public void markDeleted(String reason) {
+ FileLog.e(TAG, reason);
+ itemsToRemove.add(id);
+ }
+
+ /**
+ * Removes any items marked for removal.
+ * @return true is any item was removed.
+ */
+ public boolean commitDeleted() {
+ if (itemsToRemove.size() > 0) {
+ // Remove dead items
+ mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
+ Utilities.createDbSelectionQuery(
+ LauncherSettings.Favorites._ID, itemsToRemove), null);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Marks the current item as restored
+ */
+ public void markRestored() {
+ restoredRows.add(id);
+ }
+
+ public void commitRestoredItems() {
+ if (restoredRows.size() > 0) {
+ // Update restored items that no longer require special handling
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites.RESTORED, 0);
+ mContext.getContentResolver().update(LauncherSettings.Favorites.CONTENT_URI, values,
+ Utilities.createDbSelectionQuery(
+ LauncherSettings.Favorites._ID, restoredRows), null);
+ }
+ }
+
+ /**
+ * Returns true is the item is on workspace or hotseat
+ */
+ public boolean isOnWorkspaceOrHotseat() {
+ return container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
+ container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ }
+
+ /**
+ * Applies the following properties:
+ * {@link ItemInfo#id}
+ * {@link ItemInfo#container}
+ * {@link ItemInfo#screenId}
+ * {@link ItemInfo#cellX}
+ * {@link ItemInfo#cellY}
+ */
+ public void applyCommonProperties(ItemInfo info) {
+ info.id = id;
+ info.container = container;
+ info.screenId = getInt(screenIndex);
+ info.cellX = getInt(cellXIndex);
+ info.cellY = getInt(cellYIndex);
+ }
+
+ /**
+ * Adds the {@param info} to {@param dataModel} if it does not overlap with any other item,
+ * otherwise marks it for deletion.
+ */
+ public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
+ if (checkItemPlacement(info, dataModel.workspaceScreens)) {
+ dataModel.addItem(info, false);
+ } else {
+ markDeleted("Item position overlap");
+ }
+ }
+
+ /**
+ * check & update map of what's occupied; used to discard overlapping/invalid items
+ */
+ protected boolean checkItemPlacement(ItemInfo item, ArrayList<Long> workspaceScreens) {
+ long containerIndex = item.screenId;
+ if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ // Return early if we detect that an item is under the hotseat button
+ if (!FeatureFlags.NO_ALL_APPS_ICON &&
+ mIDP.isAllAppsButtonRank((int) item.screenId)) {
+ Log.e(TAG, "Error loading shortcut into hotseat " + item
+ + " into position (" + item.screenId + ":" + item.cellX + ","
+ + item.cellY + ") occupied by all apps");
+ return false;
+ }
+
+ final GridOccupancy hotseatOccupancy =
+ occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+
+ if (item.screenId >= mIDP.numHotseatIcons) {
+ Log.e(TAG, "Error loading shortcut " + item
+ + " into hotseat position " + item.screenId
+ + ", position out of bounds: (0 to " + (mIDP.numHotseatIcons - 1)
+ + ")");
+ return false;
+ }
+
+ if (hotseatOccupancy != null) {
+ if (hotseatOccupancy.cells[(int) item.screenId][0]) {
+ Log.e(TAG, "Error loading shortcut into hotseat " + item
+ + " into position (" + item.screenId + ":" + item.cellX + ","
+ + item.cellY + ") already occupied");
+ return false;
+ } else {
+ hotseatOccupancy.cells[(int) item.screenId][0] = true;
+ return true;
+ }
+ } else {
+ final GridOccupancy occupancy = new GridOccupancy(mIDP.numHotseatIcons, 1);
+ occupancy.cells[(int) item.screenId][0] = true;
+ occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
+ return true;
+ }
+ } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ if (!workspaceScreens.contains((Long) item.screenId)) {
+ // The item has an invalid screen id.
+ return false;
+ }
+ } else {
+ // Skip further checking if it is not the hotseat or workspace container
+ return true;
+ }
+
+ final int countX = mIDP.numColumns;
+ final int countY = mIDP.numRows;
+ if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+ item.cellX < 0 || item.cellY < 0 ||
+ item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
+ Log.e(TAG, "Error loading shortcut " + item
+ + " into cell (" + containerIndex + "-" + item.screenId + ":"
+ + item.cellX + "," + item.cellY
+ + ") out of screen bounds ( " + countX + "x" + countY + ")");
+ return false;
+ }
+
+ if (!occupied.containsKey(item.screenId)) {
+ GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
+ if (item.screenId == Workspace.FIRST_SCREEN_ID) {
+ // Mark the first row as occupied (if the feature is enabled)
+ // in order to account for the QSB.
+ screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
+ }
+ occupied.put(item.screenId, screen);
+ }
+ final GridOccupancy occupancy = occupied.get(item.screenId);
+
+ // Check if any workspace icons overlap with each other
+ if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
+ occupancy.markCells(item, true);
+ return true;
+ } else {
+ Log.e(TAG, "Error loading shortcut " + item
+ + " into cell (" + containerIndex + "-" + item.screenId + ":"
+ + item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
+ + ") already occupied");
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index e05bf1e..baeaa94 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -28,7 +28,7 @@
*/
public String packageName;
- PackageItemInfo(String packageName) {
+ public PackageItemInfo(String packageName) {
this.packageName = packageName;
}
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index 9d8e62a..808e6e3 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -89,10 +89,12 @@
ArrayList<Long> allScreens = LauncherDbUtils.getScreenIdsFromCursor(
mContext.getContentResolver().query(mOtherScreensUri, null, null, null,
LauncherSettings.WorkspaceScreens.SCREEN_RANK));
+ FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri);
// During import we reset the screen IDs to 0-indexed values.
if (allScreens.isEmpty()) {
// No thing to migrate
+ FileLog.e(TAG, "No data found to import");
return false;
}
@@ -293,6 +295,7 @@
}
}
}
+ FileLog.d(TAG, totalItemsOnWorkspace + " items imported from external source");
if (totalItemsOnWorkspace < MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION) {
throw new Exception("Insufficient data");
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 47bee06..a200a85 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -142,6 +142,7 @@
}
public static void setPending(Context context, boolean isPending) {
+ FileLog.d(TAG, "Restore data received through full backup");
Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
}
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 6796137..04c71ec 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -34,6 +34,7 @@
import com.android.launcher3.shortcuts.DeepShortcutsContainer.UnbadgedShortcutInfo;
import com.android.launcher3.util.PillRevealOutlineProvider;
import com.android.launcher3.util.PillWidthRevealOutlineProvider;
+import com.android.launcher3.util.Provider;
/**
* A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -114,10 +115,17 @@
* Returns the shortcut info that is suitable to be added on the homescreen
*/
public ShortcutInfo getFinalInfo() {
- ShortcutInfo badged = new ShortcutInfo(mInfo);
+ final ShortcutInfo badged = new ShortcutInfo(mInfo);
// Queue an update task on the worker thread. This ensures that the badged
// shortcut eventually gets its icon updated.
- Launcher.getLauncher(getContext()).getModel().updateShortcutInfo(mInfo.mDetail, badged);
+ Launcher.getLauncher(getContext()).getModel().updateAndBindShortcutInfo(
+ new Provider<ShortcutInfo>() {
+ @Override
+ public ShortcutInfo get() {
+ badged.updateFromDeepShortcutInfo(mInfo.mDetail, getContext());
+ return badged;
+ }
+ });
return badged;
}
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 1c347c0..76ba9d6 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
@@ -35,9 +36,15 @@
private final ContentValues mValues;
private final Context mContext;
+ private CommitParams mCommitParams;
private Bitmap mIcon;
private UserHandle mUser;
+ public ContentWriter(Context context, CommitParams commitParams) {
+ this(context);
+ mCommitParams = commitParams;
+ }
+
public ContentWriter(Context context) {
this(new ContentValues(), context);
}
@@ -95,4 +102,25 @@
}
return mValues;
}
+
+ public int commit() {
+ if (mCommitParams != null) {
+ return mContext.getContentResolver().update(mCommitParams.mUri, getValues(),
+ mCommitParams.mWhere, mCommitParams.mSelectionArgs);
+ }
+ return 0;
+ }
+
+ public static final class CommitParams {
+
+ final Uri mUri = LauncherSettings.Favorites.CONTENT_URI;
+ String mWhere;
+ String[] mSelectionArgs;
+
+ public CommitParams(String where, String[] selectionArgs) {
+ mWhere = where;
+ mSelectionArgs = selectionArgs;
+ }
+
+ }
}
diff --git a/src/com/android/launcher3/util/CursorIconInfo.java b/src/com/android/launcher3/util/CursorIconInfo.java
deleted file mode 100644
index 3bc4eab..0000000
--- a/src/com/android/launcher3/util/CursorIconInfo.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.content.Context;
-import android.content.Intent.ShortcutIconResource;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.text.TextUtils;
-
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.graphics.LauncherIcons;
-
-/**
- * Utility class to load icon from a cursor.
- */
-public class CursorIconInfo {
- public final int iconPackageIndex;
- public final int iconResourceIndex;
- public final int iconIndex;
-
- public final int titleIndex;
-
- private final Context mContext;
-
- public CursorIconInfo(Context context, Cursor c) {
- mContext = context;
-
- iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
- iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
- iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
-
- titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
- }
-
- /**
- * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
- */
- public Bitmap loadIcon(Cursor c, ShortcutInfo info) {
- Bitmap icon = null;
- String packageName = c.getString(iconPackageIndex);
- String resourceName = c.getString(iconResourceIndex);
- if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
- info.iconResource = new ShortcutIconResource();
- info.iconResource.packageName = packageName;
- info.iconResource.resourceName = resourceName;
- icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
- }
- if (icon == null) {
- // Failed to load from resource, try loading from DB.
- icon = loadIcon(c);
- }
- return icon;
- }
-
- /**
- * Loads the fixed bitmap from the icon if available.
- */
- public Bitmap loadIcon(Cursor c) {
- return LauncherIcons.createIconBitmap(c, iconIndex, mContext);
- }
-
- /**
- * Returns the title or empty string
- */
- public String getTitle(Cursor c) {
- String title = c.getString(titleIndex);
- return TextUtils.isEmpty(title) ? "" : Utilities.trim(c.getString(titleIndex));
- }
-}
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index ce42bcd..c27a3b5 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -116,8 +116,9 @@
.isQuietModeEnabled(user);
for (int i = 0; i < count; i++) {
LauncherActivityInstallInfo info = apps.get(i);
- ShortcutInfo si = new AppInfo(mContext, info.info, user, mIconCache,
- quietModeEnabled, false /* useLowResIcon */).makeShortcut();
+ AppInfo appInfo = new AppInfo(mContext, info.info, user, quietModeEnabled);
+ mIconCache.getTitleAndIcon(appInfo, info.info, false /* useLowResIcon */);
+ ShortcutInfo si = appInfo.makeShortcut();
((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index b61d609..33a9fc6 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -23,6 +23,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
@@ -124,4 +125,13 @@
return false;
}
+
+ public static Intent getMarketIntent(String packageName) {
+ return new Intent(Intent.ACTION_VIEW)
+ .setData(new Uri.Builder()
+ .scheme("market")
+ .authority("details")
+ .appendQueryParameter("id", packageName)
+ .build());
+ }
}
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
index 99d2654..4cad836 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -37,4 +37,6 @@
public static final boolean PULLDOWN_SEARCH = false;
// When enabled the status bar may show dark icons based on the top of the wallpaper.
public static final boolean LIGHT_STATUS_BAR = false;
+ // When enabled allows to use any point on the fast scrollbar to start dragging.
+ public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
}
diff --git a/tests/Android.mk b/tests/Android.mk
index 466146e..5103ced 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -17,9 +17,9 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-#LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
-#LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_MIN_SDK_VERSION := 21
diff --git a/tests/res/raw/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt
index 9095476..8199687 100644
--- a/tests/res/raw/cache_data_updated_task_data.txt
+++ b/tests/res/raw/cache_data_updated_task_data.txt
@@ -22,7 +22,7 @@
bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9
bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10
-allApps componentName=app1/class1
-allApps componentName=app1/class2
-allApps componentName=app2/class1
-allApps componentName=app2/class2
\ No newline at end of file
+allApps componentName=app1/class1 intent=component=app1/class1
+allApps componentName=app1/class2 intent=component=app1/class2
+allApps componentName=app2/class1 intent=component=app2/class1
+allApps componentName=app2/class2 intent=component=app2/class2
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index f23a574..3a27d0d 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -8,10 +8,12 @@
import android.graphics.Bitmap.Config;
import android.os.Process;
import android.os.UserHandle;
+import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.test.ProviderTestCase2;
import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppFilter;
import com.android.launcher3.AppInfo;
import com.android.launcher3.DeferredHandler;
import com.android.launcher3.IconCache;
@@ -24,6 +26,7 @@
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Provider;
import com.android.launcher3.util.TestLauncherProvider;
import org.mockito.ArgumentCaptor;
@@ -74,7 +77,7 @@
idp = new InvariantDeviceProfile();
iconCache = new MyIconCache(targetContext, idp);
- allAppsList = new AllAppsList(iconCache, null);
+ allAppsList = new AllAppsList(iconCache, new AppFilter());
when(appState.getIconCache()).thenReturn(iconCache);
when(appState.getInvariantDeviceProfile()).thenReturn(idp);
@@ -129,7 +132,7 @@
(ItemInfo) initItem(classMap.get(commands[1]), commands, 2), false);
break;
case "allApps":
- allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1));
+ allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
break;
}
}
@@ -184,9 +187,10 @@
}
@Override
- protected CacheEntry cacheLocked(ComponentName componentName,
- LauncherActivityInfoCompat info, UserHandle user,
- boolean usePackageIcon, boolean useLowResIcon) {
+ protected CacheEntry cacheLocked(
+ @NonNull ComponentName componentName,
+ @NonNull Provider<LauncherActivityInfoCompat> infoProvider,
+ UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
if (entry == null) {
entry = new CacheEntry();
@@ -205,5 +209,10 @@
public Bitmap newIcon() {
return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
}
+
+ @Override
+ protected Bitmap makeDefaultIcon(UserHandle user) {
+ return newIcon();
+ }
}
}
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 646ef27..d595e6c 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -30,6 +30,11 @@
}
public void testCacheUpdate_update_apps() throws Exception {
+ // Clear all icons from apps list so that its easy to check what was updated
+ for (AppInfo info : allAppsList.data) {
+ info.iconBitmap = null;
+ }
+
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
// Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
new file mode 100644
index 0000000..d40e6c2
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -0,0 +1,234 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherAppsCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
+import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON_PACKAGE;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON_RESOURCE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
+import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
+import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
+import static com.android.launcher3.LauncherSettings.Favorites._ID;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link LoaderCursor}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LoaderCursorTest {
+
+ private LauncherAppState mMockApp;
+ private IconCache mMockIconCache;
+
+ private MatrixCursor mCursor;
+ private InvariantDeviceProfile mIDP;
+ private Context mContext;
+ private LauncherAppsCompat mLauncherApps;
+
+ private LoaderCursor mLoaderCursor;
+
+ @Before
+ public void setup() {
+ mIDP = new InvariantDeviceProfile();
+ mCursor = new MatrixCursor(new String[] {
+ ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE,
+ _ID, CONTAINER, ITEM_TYPE, PROFILE_ID,
+ SCREEN, CELLX, CELLY,
+ });
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ mMockApp = mock(LauncherAppState.class);
+ mMockIconCache = mock(IconCache.class);
+ when(mMockApp.getIconCache()).thenReturn(mMockIconCache);
+ when(mMockApp.getInvariantDeviceProfile()).thenReturn(mIDP);
+ when(mMockApp.getContext()).thenReturn(mContext);
+ mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+
+ mLoaderCursor = new LoaderCursor(mCursor, mMockApp);
+ mLoaderCursor.allUsers.put(0, Process.myUserHandle());
+ }
+
+ private void initCursor(int itemType, String title) {
+ mCursor.newRow()
+ .add(_ID, 1)
+ .add(PROFILE_ID, 0)
+ .add(ITEM_TYPE, itemType)
+ .add(TITLE, title)
+ .add(CONTAINER, CONTAINER_DESKTOP);
+ }
+
+ @Test
+ public void getAppShortcutInfo_dontAllowMissing_invalidComponent() {
+ initCursor(ITEM_TYPE_APPLICATION, "");
+ assertTrue(mLoaderCursor.moveToNext());
+ ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+ assertNull(mLoaderCursor.getAppShortcutInfo(
+ new Intent().setComponent(cn), false /* allowMissingTarget */, true));
+ }
+
+ @Test
+ public void getAppShortcutInfo_dontAllowMissing_validComponent() {
+ initCursor(ITEM_TYPE_APPLICATION, "");
+ assertTrue(mLoaderCursor.moveToNext());
+
+ ComponentName cn = mLauncherApps.getActivityList(null, mLoaderCursor.user)
+ .get(0).getComponentName();
+ ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+ new Intent().setComponent(cn), false /* allowMissingTarget */, true);
+ assertNotNull(info);
+ assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ }
+
+ @Test
+ public void getAppShortcutInfo_allowMissing_invalidComponent() {
+ initCursor(ITEM_TYPE_APPLICATION, "");
+ assertTrue(mLoaderCursor.moveToNext());
+
+ ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+ ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+ new Intent().setComponent(cn), true /* allowMissingTarget */, true);
+ assertNotNull(info);
+ assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ }
+
+ @Test
+ public void loadSimpleShortcut() {
+ initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
+ assertTrue(mLoaderCursor.moveToNext());
+
+ Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user))).thenReturn(icon);
+ ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
+ assertEquals(icon, info.iconBitmap);
+ assertEquals("my-shortcut", info.title);
+ assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
+ }
+
+ @Test
+ public void checkItemPlacement_wrongWorkspaceScreen() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 3L));
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Item on unknown screen are not placed
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 4L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 5L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 3L), workspaceScreens));
+
+ }
+ @Test
+ public void checkItemPlacement_outsideBounds() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Item outside screen bounds are not placed
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ }
+
+ @Test
+ public void checkItemPlacement_overlappingItems() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Overlapping items are not placed
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ }
+
+ @Test
+ public void checkItemPlacement_hotseat() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>();
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Hotseat items are only placed based on screenId
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1L), workspaceScreens));
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2L), workspaceScreens));
+
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3L), workspaceScreens));
+ }
+
+ private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
+ long container, long screenId) {
+ ItemInfo info = new ItemInfo();
+ info.cellX = cellX;
+ info.cellY = cellY;
+ info.spanX = spanX;
+ info.spanY = spanY;
+ info.container = container;
+ info.screenId = screenId;
+ return info;
+ }
+}