Merge "Make sure we set the QSB bar on the SearchDropTargetBar" into ub-launcher3-burnaby
diff --git a/res/layout/user_folder_scroll.xml b/res/layout/user_folder_scroll.xml
index 421e426..12e5097 100644
--- a/res/layout/user_folder_scroll.xml
+++ b/res/layout/user_folder_scroll.xml
@@ -45,7 +45,9 @@
android:id="@+id/folder_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ android:orientation="horizontal"
+ android:paddingStart="12dp"
+ android:paddingEnd="8dp" >
<com.android.launcher3.FolderEditText
android:id="@+id/folder_name"
@@ -55,7 +57,7 @@
android:layout_weight="1"
android:background="#00000000"
android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
+ android:gravity="start"
android:hint="@string/folder_hint_text"
android:imeOptions="flagNoExtractUi"
android:paddingBottom="@dimen/folder_name_padding"
@@ -71,8 +73,34 @@
android:id="@+id/folder_page_indicator"
android:layout_width="wrap_content"
android:layout_height="12dp"
- android:layout_gravity="center_vertical"
+ android:layout_gravity="top"
+ android:layout_marginTop="5dp"
layout="@layout/page_indicator" />
+
+ <LinearLayout
+ android:id="@+id/folder_sort"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:gravity="end|center_vertical" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:text="@string/sort_alphabetical"
+ android:textColor="#ff777777"
+ android:textSize="14sp" />
+
+ <Switch
+ android:id="@+id/folder_sort_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:duplicateParentState="true"
+ android:focusable="false" />
+ </LinearLayout>
</LinearLayout>
</com.android.launcher3.Folder>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a1e4601..0b34d00 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -89,6 +89,8 @@
<string name="rename_action">OK</string>
<!-- Buttons in Rename folder dialog box -->
<string name="cancel_action">Cancel</string>
+ <!-- Label for button to sort folder contents. [CHAR_LIMIT=10] -->
+ <string name="sort_alphabetical">A-Z</string>
<!-- Shortcuts -->
<skip />
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 5ed7a62..dd646bb 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -148,7 +148,7 @@
if (applicationInfo == null) {
add(new AppInfo(context, info, user, mIconCache));
} else {
- mIconCache.getTitleAndIcon(applicationInfo, info);
+ 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 455c6d1..a1391b2 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -46,6 +46,11 @@
Bitmap iconBitmap;
/**
+ * Indicates whether we're using a low res icon
+ */
+ boolean usingLowResIcon;
+
+ /**
* The time at which the app was first installed.
*/
long firstInstallTime;
@@ -79,7 +84,7 @@
flags = initFlags(info);
firstInstallTime = info.getFirstInstallTime();
- iconCache.getTitleAndIcon(this, info);
+ iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
intent = makeLaunchIntent(context, info, user);
this.user = user;
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 8ef234b..50549ca 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -34,6 +34,8 @@
import android.view.ViewConfiguration;
import android.widget.TextView;
+import com.android.launcher3.IconCache.IconLoadRequest;
+
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
* because we want to make the bubble taller than the text and TextView's clip is
@@ -74,6 +76,8 @@
private boolean mStayPressed;
private boolean mIgnorePressedStateChange;
+ private IconLoadRequest mIconLoadRequest;
+
public BubbleTextView(Context context) {
this(context, null, 0);
}
@@ -163,6 +167,9 @@
}
// We don't need to check the info since it's not a ShortcutInfo
super.setTag(info);
+
+ // Verify high res immediately
+ verifyHighRes();
}
@Override
@@ -450,4 +457,42 @@
}
return icon;
}
+
+ /**
+ * Applies the item info if it is same as what the view is pointing to currently.
+ */
+ public void reapplyItemInfo(final ItemInfo info) {
+ if (getTag() == info) {
+ mIconLoadRequest = null;
+ if (info instanceof AppInfo) {
+ applyFromApplicationInfo((AppInfo) info);
+ } else if (info instanceof ShortcutInfo) {
+ applyFromShortcutInfo((ShortcutInfo) info,
+ LauncherAppState.getInstance().getIconCache(), false);
+ }
+ }
+ }
+
+ /**
+ * Verifies that the current icon is high-res otherwise posts a request to load the icon.
+ */
+ public void verifyHighRes() {
+ if (mIconLoadRequest != null) {
+ 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);
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index ddd3002..bc9ef76 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -81,6 +81,7 @@
boolean isTablet;
boolean isLargeTablet;
boolean isLayoutRtl;
+
boolean transposeLayoutWithOrientation;
int desiredWorkspaceLeftRightMarginPx;
@@ -699,6 +700,10 @@
return isLargeTablet;
}
+ /**
+ * When {@code true}, hotseat is on the bottom row when in landscape mode.
+ * If {@code false}, hotseat is on the right column when in landscape mode.
+ */
boolean isVerticalBarLayout() {
return isLandscape && transposeLayoutWithOrientation;
}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index b090a7c..fc68952 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -207,12 +207,13 @@
if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
return consume;
}
- int orientation = v.getResources().getConfiguration().orientation;
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
if (DEBUG) {
Log.v(TAG, String.format(
- "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, orientation=%d",
- KeyEvent.keyCodeToString(keyCode), orientation));
+ "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
+ KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
}
// Initialize the variables.
@@ -226,6 +227,8 @@
int countX = -1;
int countY = -1;
int iconIndex = findIndexOfView(hotseatParent, v);
+ int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
+ .getChildAt(iconIndex).getLayoutParams()).cellX;
final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
@@ -234,23 +237,25 @@
int[][] matrix = null;
if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
- orientation == Configuration.ORIENTATION_PORTRAIT) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
- hotseat.getAllAppsButtonRank(), true /* include all apps icon */);
+ !profile.isVerticalBarLayout()) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
+ true /* hotseat horizontal */, hotseat.getAllAppsButtonRank(),
+ iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */);
iconIndex += iconParent.getChildCount();
countX = iconLayout.getCountX();
countY = iconLayout.getCountY() + hotseatLayout.getCountY();
parent = iconParent;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
- orientation == Configuration.ORIENTATION_LANDSCAPE) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
- hotseat.getAllAppsButtonRank(), true /* include all apps icon */);
+ profile.isVerticalBarLayout()) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
+ false /* hotseat horizontal */, hotseat.getAllAppsButtonRank(),
+ iconRank == hotseat.getAllAppsButtonRank() /* include all apps icon */);
iconIndex += iconParent.getChildCount();
countX = iconLayout.getCountX() + hotseatLayout.getCountX();
countY = iconLayout.getCountY();
parent = iconParent;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
- orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ profile.isVerticalBarLayout()) {
keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
}else {
// For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
@@ -296,10 +301,13 @@
if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
return consume;
}
- int orientation = v.getResources().getConfiguration().orientation;
+
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
+
if (DEBUG) {
- Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] orientation=%d",
- KeyEvent.keyCodeToString(keyCode), orientation));
+ Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s",
+ KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
}
// Initialize the variables.
@@ -322,14 +330,13 @@
// KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed
// to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
// with the hotseat.
- if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN &&
- orientation == Configuration.ORIENTATION_PORTRAIT) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
+ if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */,
hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
countY = countY + 1;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
- orientation == Configuration.ORIENTATION_LANDSCAPE) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, orientation,
+ profile.isVerticalBarLayout()) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */,
hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
countX = countX + 1;
} else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 7ff60de..5d8a865 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -86,6 +86,12 @@
public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY;
/**
+ * Time in milliseconds for which an icon sticks to the target position
+ * in case of a sorted folder.
+ */
+ private static final int SORTED_STICKY_REORDER_DELAY = 1500;
+
+ /**
* Fraction of icon width which behave as scroll region.
*/
private static final float ICON_OVERSCROLL_WIDTH_FACTOR = 0.45f;
@@ -423,8 +429,11 @@
if (!(getParent() instanceof DragLayer)) return;
if (ALLOW_FOLDER_SCROLL) {
- // Always open on the first page.
- mPagedView.snapToPageImmediately(0);
+ mPagedView.completePendingPageChanges();
+ if (!(mDragInProgress && mPagedView.mIsSorted)) {
+ // Open on the first page.
+ mPagedView.snapToPageImmediately(0);
+ }
}
Animator openFolderAnim = null;
@@ -527,13 +536,24 @@
if (mDragController.isDragging()) {
mDragController.forceTouchMove();
}
+
+ if (ALLOW_FOLDER_SCROLL) {
+ FolderPagedView pages = (FolderPagedView) mContent;
+ pages.verifyVisibleHighResIcons(pages.getNextPage());
+ }
}
public void beginExternalDrag(ShortcutInfo item) {
mCurrentDragInfo = item;
- mEmptyCellRank = mContent.allocateNewLastItemRank();
+ mEmptyCellRank = mContent.allocateRankForNewItem(item);
mIsExternalDrag = true;
mDragInProgress = true;
+ if (ALLOW_FOLDER_SCROLL && mPagedView.mIsSorted) {
+ mScrollPauseAlarm.setOnAlarmListener(null);
+ mScrollPauseAlarm.cancelAlarm();
+ mScrollPauseAlarm.setAlarm(SORTED_STICKY_REORDER_DELAY);
+ }
+
}
private void sendCustomAccessibilityEvent(int type, String text) {
@@ -747,6 +767,7 @@
if (!successfulDrop) {
mSuppressFolderDeletion = true;
}
+ mScrollPauseAlarm.cancelAlarm();
completeDragExit();
}
}
@@ -1151,11 +1172,11 @@
}
public void onAdd(ShortcutInfo item) {
- mItemsInvalidated = true;
// If the item was dropped onto this open folder, we have done the work associated
// with adding the item to the folder, as indicated by mSuppressOnAdd being set
if (mSuppressOnAdd) return;
- mContent.createAndAddViewForRank(item, mContent.allocateNewLastItemRank());
+ mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item));
+ mItemsInvalidated = true;
LauncherModel.addOrMoveItemInDatabase(
mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
}
@@ -1304,10 +1325,10 @@
ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> children);
/**
- * Create space for a new item at the end, and returns the rank for that item.
+ * Create space for a new item, and returns the rank for that item.
* Resizes the content if necessary.
*/
- int allocateNewLastItemRank();
+ int allocateRankForNewItem(ShortcutInfo info);
View createAndAddViewForRank(ShortcutInfo item, int rank);
diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java
index 1566912..8585add 100644
--- a/src/com/android/launcher3/FolderCellLayout.java
+++ b/src/com/android/launcher3/FolderCellLayout.java
@@ -133,7 +133,7 @@
}
@Override
- public int allocateNewLastItemRank() {
+ public int allocateRankForNewItem(ShortcutInfo info) {
int rank = getItemCount();
mFolder.rearrangeChildren(rank + 1);
return rank;
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index a3e8295..dbfedaa 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -58,7 +58,7 @@
private CheckLongPressHelper mLongPressHelper;
// The number of icons to display in the
- private static final int NUM_ITEMS_IN_PREVIEW = 3;
+ public static final int NUM_ITEMS_IN_PREVIEW = 3;
private static final int CONSUMPTION_ANIMATION_DURATION = 100;
private static final int DROP_IN_ANIMATION_DURATION = 400;
private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 85a792f..3240cbf 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -29,11 +29,20 @@
*/
public class FolderInfo extends ItemInfo {
+ public static final int NO_FLAGS = 0x00000000;
+
+ /**
+ * The folder is locked in sorted mode
+ */
+ public static final int FLAG_ITEMS_SORTED = 0x00000001;
+
/**
* Whether this folder has been opened
*/
boolean opened;
+ public int options;
+
/**
* The apps and shortcuts
*/
@@ -83,6 +92,8 @@
void onAddToDatabase(Context context, ContentValues values) {
super.onAddToDatabase(context, values);
values.put(LauncherSettings.Favorites.TITLE, title.toString());
+ values.put(LauncherSettings.Favorites.OPTIONS, options);
+
}
void addListener(FolderListener listener) {
@@ -121,4 +132,25 @@
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
+ " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
}
+
+ public boolean hasOption(int optionFlag) {
+ return (options & optionFlag) != 0;
+ }
+
+ /**
+ * @param option flag to set or clear
+ * @param isEnabled whether to set or clear the flag
+ * @param context if not null, save changes to the db.
+ */
+ public void setOption(int option, boolean isEnabled, Context context) {
+ int oldOptions = options;
+ if (isEnabled) {
+ options |= option;
+ } else {
+ options &= ~option;
+ }
+ if (context != null && oldOptions != options) {
+ LauncherModel.updateItemInDatabase(context, this);
+ }
+ }
}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index b4a7a75..21158b4 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -20,15 +20,22 @@
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.OvershootInterpolator;
+import android.widget.Switch;
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
import com.android.launcher3.PageIndicator.PageMarkerResources;
import com.android.launcher3.Workspace.ItemOperator;
+import java.text.Collator;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -40,14 +47,19 @@
private static final int REORDER_ANIMATION_DURATION = 230;
private static final int START_VIEW_REORDER_DELAY = 30;
private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
+
+ private static final int SPAN_TO_PAGE_DURATION = 350;
+ private static final int SORT_ANIM_HIDE_DURATION = 130;
+ private static final int SORT_ANIM_SHOW_DURATION = 160;
+
private static final int[] sTempPosArray = new int[2];
// TODO: Remove this restriction
- private static final int MAX_ITEMS_PER_PAGE = 3;
+ private static final int MAX_ITEMS_PER_PAGE = 4;
private final LayoutInflater mInflater;
private final IconCache mIconCache;
- private final HashMap<View, Runnable> mPageChangingViews = new HashMap<>();
+ private final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
private final int mMaxCountX;
private final int mMaxCountY;
@@ -61,6 +73,13 @@
private FocusIndicatorView mFocusIndicatorView;
private PagedFolderKeyEventListener mKeyListener;
+ private View mSortButton;
+ private Switch mSortSwitch;
+ private View mPageIndicator;
+
+ private boolean mSortOperationPending;
+ boolean mIsSorted;
+
public FolderPagedView(Context context, AttributeSet attrs) {
super(context, attrs);
LauncherAppState app = LauncherAppState.getInstance();
@@ -80,6 +99,134 @@
mFolder = folder;
mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
mKeyListener = new PagedFolderKeyEventListener(folder);
+
+ mSortButton = folder.findViewById(R.id.folder_sort);
+ mSortButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ onSortClicked();
+ }
+ });
+ mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
+ mSortSwitch = (Switch) folder.findViewById(R.id.folder_sort_switch);
+ }
+
+ private void onSortClicked() {
+ if (mSortOperationPending) {
+ return;
+ }
+ if (mIsSorted) {
+ setIsSorted(false, true);
+ } else {
+ mSortOperationPending = true;
+ doSort();
+ }
+ }
+
+ private void setIsSorted(boolean isSorted, boolean saveChanges) {
+ mIsSorted = isSorted;
+ mSortSwitch.setChecked(isSorted);
+ mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted,
+ saveChanges ? mFolder.mLauncher : null);
+ }
+
+ /**
+ * Sorts the contents of the folder and animates the icons on the first page to reflect
+ * the changes.
+ * Steps:
+ * 1. Scroll to first page
+ * 2. Sort all icons in one go
+ * 3. Re-apply the old IconInfos on the first page (so that there is no instant change)
+ * 4. Animate each view individually to reflect the new icon.
+ */
+ private void doSort() {
+ if (!mSortOperationPending) {
+ return;
+ }
+ if (getNextPage() != 0) {
+ snapToPage(0, SPAN_TO_PAGE_DURATION, new DecelerateInterpolator());
+ return;
+ }
+
+ mSortOperationPending = false;
+ ShortcutInfo[][] oldItems = new ShortcutInfo[mGridCountX][mGridCountY];
+ CellLayout currentPage = getCurrentCellLayout();
+ for (int x = 0; x < mGridCountX; x++) {
+ for (int y = 0; y < mGridCountY; y++) {
+ View v = currentPage.getChildAt(x, y);
+ if (v != null) {
+ oldItems[x][y] = (ShortcutInfo) v.getTag();
+ }
+ }
+ }
+
+ ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
+ Collections.sort(views, new ViewComparator());
+ arrangeChildren(views, views.size());
+
+ int delay = 0;
+ float delayAmount = START_VIEW_REORDER_DELAY;
+ final Interpolator hideInterpolator = new DecelerateInterpolator(2);
+ final Interpolator showInterpolator = new OvershootInterpolator(0.8f);
+
+ currentPage = getCurrentCellLayout();
+ for (int x = 0; x < mGridCountX; x++) {
+ for (int y = 0; y < mGridCountY; y++) {
+ final BubbleTextView v = (BubbleTextView) currentPage.getChildAt(x, y);
+ if (v != null) {
+ final ShortcutInfo info = (ShortcutInfo) v.getTag();
+ final Runnable clearPending = new Runnable() {
+
+ @Override
+ public void run() {
+ mPendingAnimations.remove(v);
+ v.setScaleX(1);
+ v.setScaleY(1);
+ }
+ };
+ if (oldItems[x][y] == null) {
+ v.setScaleX(0);
+ v.setScaleY(0);
+ v.animate().setDuration(SORT_ANIM_SHOW_DURATION)
+ .setStartDelay(SORT_ANIM_HIDE_DURATION + delay)
+ .scaleX(1).scaleY(1).setInterpolator(showInterpolator)
+ .withEndAction(clearPending);
+ mPendingAnimations.put(v, clearPending);
+ } else {
+ // Apply the old iconInfo so that there is no sudden change.
+ v.applyFromShortcutInfo(oldItems[x][y], mIconCache, false);
+ v.animate().setStartDelay(delay).setDuration(SORT_ANIM_HIDE_DURATION)
+ .scaleX(0).scaleY(0)
+ .setInterpolator(hideInterpolator)
+ .withEndAction(new Runnable() {
+
+ @Override
+ public void run() {
+ // Apply the new iconInfo as part of the animation.
+ v.applyFromShortcutInfo(info, mIconCache, false);
+ v.animate().scaleX(1).scaleY(1)
+ .setDuration(SORT_ANIM_SHOW_DURATION).setStartDelay(0)
+ .setInterpolator(showInterpolator)
+ .withEndAction(clearPending);
+ }
+ });
+ mPendingAnimations.put(v, new Runnable() {
+
+ @Override
+ public void run() {
+ clearPending.run();
+ v.applyFromShortcutInfo(info, mIconCache, false);
+ }
+ });
+ }
+ delay += delayAmount;
+ delayAmount *= VIEW_REORDER_DELAY_FACTOR;
+ }
+ }
+ }
+
+ setIsSorted(true, true);
}
/**
@@ -125,6 +272,7 @@
@Override
public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
+ mIsSorted = mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED);
ArrayList<View> icons = new ArrayList<View>();
for (ShortcutInfo item : items) {
icons.add(createNewView(item));
@@ -138,20 +286,33 @@
* Also sets the current page to the last page.
*/
@Override
- public int allocateNewLastItemRank() {
+ public int allocateRankForNewItem(ShortcutInfo info) {
int rank = getItemCount();
- int total = rank + 1;
- // Rearrange the items as the grid size might change.
- mFolder.rearrangeChildren(total);
+ ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
+ if (mIsSorted) {
+ View tmp = new View(getContext());
+ tmp.setTag(info);
+ int index = Collections.binarySearch(views, tmp, new ViewComparator());
+ if (index < 0) {
+ rank = -index - 1;
+ } else {
+ // Item with same name already exists.
+ // We will just insert it before that item.
+ rank = index;
+ }
- setCurrentPage(getChildCount() - 1);
+ }
+
+ views.add(rank, null);
+ arrangeChildren(views, views.size(), false);
+ setCurrentPage(rank / mMaxItemsPerPage);
return rank;
}
@Override
public View createAndAddViewForRank(ShortcutInfo item, int rank) {
View icon = createNewView(item);
- addViewForRank(createNewView(item), item, rank);
+ addViewForRank(icon, item, rank);
return icon;
}
@@ -259,6 +420,10 @@
int position = 0;
int newX, newY, rank;
+ boolean isSorted = mIsSorted;
+
+ ViewComparator comparator = new ViewComparator();
+ View lastView = null;
rank = 0;
for (int i = 0; i < itemCount; i++) {
View v = list.size() > i ? list.get(i) : null;
@@ -273,6 +438,10 @@
}
if (v != null) {
+ if (lastView != null) {
+ isSorted &= comparator.compare(lastView, v) <= 0;
+ }
+
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
newX = position % mGridCountX;
newY = position / mGridCountX;
@@ -292,6 +461,7 @@
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
}
+ lastView = v;
rank ++;
position++;
}
@@ -305,6 +475,19 @@
if (removed) {
setCurrentPage(0);
}
+
+ setIsSorted(isSorted, saveChanges);
+
+ // Update footer
+ if (getPageCount() > 1) {
+ mPageIndicator.setVisibility(View.VISIBLE);
+ mSortButton.setVisibility(View.VISIBLE);
+ mFolder.mFolderName.setGravity(Gravity.START);
+ } else {
+ mPageIndicator.setVisibility(View.GONE);
+ mSortButton.setVisibility(View.GONE);
+ mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL);
+ }
}
@Override
@@ -407,6 +590,17 @@
if (mFolder != null) {
mFolder.updateTextViewFocus();
}
+ if (mSortOperationPending && getNextPage() == 0) {
+ post(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mSortOperationPending) {
+ doSort();
+ }
+ }
+ });
+ }
}
/**
@@ -433,8 +627,8 @@
* Finish animation all the views which are animating across pages
*/
public void completePendingPageChanges() {
- if (!mPageChangingViews.isEmpty()) {
- HashMap<View, Runnable> pendingViews = new HashMap<>(mPageChangingViews);
+ if (!mPendingAnimations.isEmpty()) {
+ HashMap<View, Runnable> pendingViews = new HashMap<>(mPendingAnimations);
for (Map.Entry<View, Runnable> e : pendingViews.entrySet()) {
e.getKey().animate().cancel();
e.getValue().run();
@@ -448,6 +642,28 @@
}
@Override
+ protected void onPageBeginMoving() {
+ super.onPageBeginMoving();
+ getVisiblePages(sTempPosArray);
+ for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) {
+ verifyVisibleHighResIcons(i);
+ }
+ }
+
+ /**
+ * Ensures that all the icons on the given page are of high-res
+ */
+ public void verifyVisibleHighResIcons(int pageNo) {
+ CellLayout page = getPageAt(pageNo);
+ if (page != null) {
+ ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
+ for (int i = parent.getChildCount() - 1; i >= 0; i--) {
+ ((BubbleTextView) parent.getChildAt(i)).verifyHighRes();
+ }
+ }
+ }
+
+ @Override
public void realTimeReorder(int empty, int target) {
completePendingPageChanges();
int delay = 0;
@@ -533,7 +749,7 @@
@Override
public void run() {
- mPageChangingViews.remove(v);
+ mPendingAnimations.remove(v);
v.setTranslationX(oldTranslateX);
((CellLayout) v.getParent().getParent()).removeView(v);
addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
@@ -544,7 +760,7 @@
.setDuration(REORDER_ANIMATION_DURATION)
.setStartDelay(0)
.withEndAction(endAction);
- mPageChangingViews.put(v, endAction);
+ mPendingAnimations.put(v, endAction);
}
}
moveStart = rankToMove;
@@ -569,4 +785,14 @@
}
}
}
+
+ private static class ViewComparator implements Comparator<View> {
+ private final Collator mCollator = Collator.getInstance();
+
+ @Override
+ public int compare(View lhs, View rhs) {
+ return mCollator.compare( ((ShortcutInfo) lhs.getTag()).title.toString(),
+ ((ShortcutInfo) rhs.getTag()).title.toString());
+ }
+ }
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 43f838e..39a80be 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -31,8 +31,10 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
@@ -62,14 +64,18 @@
private static final boolean DEBUG = false;
+ private static final int LOW_RES_SCALE_FACTOR = 8;
+
private static class CacheEntry {
public Bitmap icon;
public CharSequence title;
public CharSequence contentDescription;
+ public boolean isLowResIcon;
}
- private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons =
- new HashMap<UserHandleCompat, Bitmap>();
+ private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons = new HashMap<>();
+ private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
+
private final Context mContext;
private final PackageManager mPackageManager;
private final UserManagerCompat mUserManager;
@@ -79,6 +85,8 @@
private final int mIconDpi;
private final IconDB mIconDb;
+ private final Handler mWorkerHandler;
+
public IconCache(Context context) {
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
@@ -89,6 +97,8 @@
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mIconDpi = activityManager.getLauncherLargeIconDensity();
mIconDb = new IconDB(context);
+
+ mWorkerHandler = new Handler(LauncherModel.sWorkerThread.getLooper());
}
private Drawable getFullResDefaultActivityIcon() {
@@ -306,10 +316,7 @@
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry);
- ContentValues values = new ContentValues();
- values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(entry.icon));
- values.put(IconDB.COLUMN_LABEL, entry.title.toString());
- return values;
+ return mIconDb.newContentValues(entry.icon, entry.title.toString());
}
@@ -335,16 +342,52 @@
}
/**
+ * 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) {
+ Runnable request = new Runnable() {
+
+ @Override
+ public void run() {
+ if (info instanceof AppInfo) {
+ getTitleAndIcon((AppInfo) info, null, false);
+ } else if (info instanceof ShortcutInfo) {
+ ShortcutInfo st = (ShortcutInfo) info;
+ getTitleAndIcon(st,
+ st.promisedIntent != null ? st.promisedIntent : st.intent,
+ st.user, false);
+ }
+ mMainThreadExecutor.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ caller.reapplyItemInfo(info);
+ }
+ });
+ }
+ };
+ mWorkerHandler.post(request);
+ return new IconLoadRequest(request, mWorkerHandler);
+ }
+
+ /**
* Fill in "application" with the icon and label for "info."
*/
- public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info) {
- CacheEntry entry = cacheLocked(application.componentName, info, info.getUser(), false);
-
+ public synchronized void getTitleAndIcon(AppInfo application,
+ LauncherActivityInfoCompat info, boolean useLowResIcon) {
+ CacheEntry entry = cacheLocked(application.componentName, info,
+ info == null ? application.user : info.getUser(),
+ false, useLowResIcon);
application.title = entry.title;
application.iconBitmap = entry.icon;
application.contentDescription = entry.contentDescription;
+ application.usingLowResIcon = entry.isLowResIcon;
}
+ /**
+ * Returns a high res icon for the given intent and user
+ */
public synchronized Bitmap getIcon(Intent intent, UserHandleCompat user) {
ComponentName component = intent.getComponent();
// null info means not installed, but if we have a component from the intent then
@@ -354,7 +397,7 @@
}
LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);
- CacheEntry entry = cacheLocked(component, launcherActInfo, user, true);
+ CacheEntry entry = cacheLocked(component, launcherActInfo, user, true, true);
return entry.icon;
}
@@ -363,7 +406,7 @@
* corresponding activity is not found, it reverts to the package icon.
*/
public synchronized void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent,
- UserHandleCompat user) {
+ UserHandleCompat user, boolean useLowResIcon) {
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.
@@ -371,9 +414,10 @@
shortcutInfo.setIcon(getDefaultIcon(user));
shortcutInfo.title = "";
shortcutInfo.usingFallbackIcon = true;
+ shortcutInfo.usingLowResIcon = false;
} else {
LauncherActivityInfoCompat info = mLauncherApps.resolveActivity(intent, user);
- getTitleAndIcon(shortcutInfo, component, info, user, true);
+ getTitleAndIcon(shortcutInfo, component, info, user, true, useLowResIcon);
}
}
@@ -382,11 +426,12 @@
*/
public synchronized void getTitleAndIcon(
ShortcutInfo shortcutInfo, ComponentName component, LauncherActivityInfoCompat info,
- UserHandleCompat user, boolean usePkgIcon) {
- CacheEntry entry = cacheLocked(component, info, user, usePkgIcon);
+ UserHandleCompat user, boolean usePkgIcon, boolean useLowResIcon) {
+ CacheEntry entry = cacheLocked(component, info, user, usePkgIcon, useLowResIcon);
shortcutInfo.setIcon(entry.icon);
shortcutInfo.title = entry.title;
shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
+ shortcutInfo.usingLowResIcon = entry.isLowResIcon;
}
public synchronized Bitmap getDefaultIcon(UserHandleCompat user) {
@@ -405,15 +450,15 @@
* This method is not thread safe, it must be called from a synchronized method.
*/
private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
- UserHandleCompat user, boolean usePackageIcon) {
+ UserHandleCompat user, boolean usePackageIcon, boolean useLowResIcon) {
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
- if (entry == null) {
+ if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
entry = new CacheEntry();
mCache.put(cacheKey, entry);
// Check the DB first.
- if (!getEntryFromDB(componentName, user, entry)) {
+ if (!getEntryFromDB(componentName, user, entry, useLowResIcon)) {
if (info != null) {
entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext);
} else {
@@ -509,25 +554,26 @@
// pass
}
- ContentValues values = new ContentValues();
+ ContentValues values = mIconDb.newContentValues(icon, label);
values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
- values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(icon));
- values.put(IconDB.COLUMN_LABEL, label);
mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values,
SQLiteDatabase.CONFLICT_REPLACE);
}
- private boolean getEntryFromDB(ComponentName component, UserHandleCompat user, CacheEntry entry) {
+ private boolean getEntryFromDB(ComponentName component, UserHandleCompat user,
+ CacheEntry entry, boolean lowRes) {
Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME,
- new String[] {IconDB.COLUMN_ICON, IconDB.COLUMN_LABEL},
+ new String[] {lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
+ IconDB.COLUMN_LABEL},
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
new String[] {component.flattenToString(),
Long.toString(mUserManager.getSerialNumberForUser(user))},
null, null, null);
try {
if (c.moveToNext()) {
- entry.icon = Utilities.createIconBitmap(c, 0, mContext);
+ entry.icon = loadIconNoResize(c, 0);
+ entry.isLowResIcon = lowRes;
entry.title = c.getString(1);
if (entry.title == null) {
entry.title = "";
@@ -543,8 +589,22 @@
return false;
}
+ public static class IconLoadRequest {
+ private final Runnable mRunnable;
+ private final Handler mHandler;
+
+ IconLoadRequest(Runnable runnable, Handler handler) {
+ mRunnable = runnable;
+ mHandler = handler;
+ }
+
+ public void cancel() {
+ mHandler.removeCallbacks(mRunnable);
+ }
+ }
+
private static final class IconDB extends SQLiteOpenHelper {
- private final static int DB_VERSION = 1;
+ private final static int DB_VERSION = 2;
private final static String TABLE_NAME = "icons";
private final static String COLUMN_ROWID = "rowid";
@@ -553,6 +613,7 @@
private final static String COLUMN_LAST_UPDATED = "lastUpdated";
private final static String COLUMN_VERSION = "version";
private final static String COLUMN_ICON = "icon";
+ private final static String COLUMN_ICON_LOW_RES = "icon_low_res";
private final static String COLUMN_LABEL = "label";
public IconDB(Context context) {
@@ -567,6 +628,7 @@
COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_ICON + " BLOB, " +
+ COLUMN_ICON_LOW_RES + " BLOB, " +
COLUMN_LABEL + " TEXT, " +
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
");");
@@ -590,5 +652,25 @@
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
+
+ public ContentValues newContentValues(Bitmap icon, String label) {
+ ContentValues values = new ContentValues();
+ values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(icon));
+ values.put(IconDB.COLUMN_ICON_LOW_RES, ItemInfo.flattenBitmap(
+ Bitmap.createScaledBitmap(icon,
+ icon.getWidth() / LOW_RES_SCALE_FACTOR,
+ icon.getHeight() / LOW_RES_SCALE_FACTOR, true)));
+ values.put(IconDB.COLUMN_LABEL, label);
+ return values;
+ }
+ }
+
+ private static Bitmap loadIconNoResize(Cursor c, int iconIndex) {
+ byte[] data = c.getBlob(iconIndex);
+ try {
+ return BitmapFactory.decodeByteArray(data, 0, data.length);
+ } catch (Exception e) {
+ return null;
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 2fd9db2..1e16baf 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -117,7 +117,7 @@
private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
- private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
+ static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
@@ -961,6 +961,7 @@
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 optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
FolderInfo folderInfo = null;
switch (c.getInt(itemTypeIndex)) {
@@ -975,6 +976,7 @@
folderInfo.screenId = c.getInt(screenIndex);
folderInfo.cellX = c.getInt(cellXIndex);
folderInfo.cellY = c.getInt(cellYIndex);
+ folderInfo.options = c.getInt(optionsIndex);
return folderInfo;
}
@@ -1862,9 +1864,8 @@
LauncherSettings.Favorites.RESTORED);
final int profileIdIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.PROFILE_ID);
- //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
- //final int displayModeIndex = c.getColumnIndexOrThrow(
- // LauncherSettings.Favorites.DISPLAY_MODE);
+ final int optionsIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.OPTIONS);
ShortcutInfo info;
String intentDescription;
@@ -2017,10 +2018,14 @@
continue;
}
+ container = c.getInt(containerIndex);
+ boolean useLowResIcon = container >= 0 &&
+ c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
+
if (itemReplaced) {
if (user.equals(UserHandleCompat.myUserHandle())) {
info = getAppShortcutInfo(manager, intent, user, context, null,
- iconIndex, titleIndex, false);
+ iconIndex, titleIndex, false, useLowResIcon);
} else {
// Don't replace items for other profiles.
itemsToRemove.add(id);
@@ -2031,7 +2036,8 @@
Launcher.addDumpLog(TAG,
"constructing info for partially restored package",
true);
- info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
+ info = getRestoredItemInfo(c, titleIndex, intent,
+ promiseType, useLowResIcon);
intent = getRestoredItemIntent(c, context, intent);
} else {
// Don't restore items for other profiles.
@@ -2041,7 +2047,7 @@
} else if (itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
info = getAppShortcutInfo(manager, intent, user, context, c,
- iconIndex, titleIndex, allowMissingTarget);
+ iconIndex, titleIndex, allowMissingTarget, useLowResIcon);
} else {
info = getShortcutInfo(c, context, iconTypeIndex,
iconPackageIndex, iconResourceIndex, iconIndex,
@@ -2063,7 +2069,6 @@
if (info != null) {
info.id = id;
info.intent = intent;
- container = c.getInt(containerIndex);
info.container = container;
info.screenId = c.getInt(screenIndex);
info.cellX = c.getInt(cellXIndex);
@@ -2114,6 +2119,7 @@
folderInfo.cellY = c.getInt(cellYIndex);
folderInfo.spanX = 1;
folderInfo.spanY = 1;
+ folderInfo.options = c.getInt(optionsIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, folderInfo)) {
@@ -3350,10 +3356,10 @@
* to a package that is not yet installed on the system.
*/
public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
- int promiseType) {
+ int promiseType, boolean useLowResIcon) {
final ShortcutInfo info = new ShortcutInfo();
info.user = UserHandleCompat.myUserHandle();
- mIconCache.getTitleAndIcon(info, intent, info.user);
+ mIconCache.getTitleAndIcon(info, intent, info.user, useLowResIcon);
if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
String title = (cursor != null) ? cursor.getString(titleIndex) : null;
@@ -3402,7 +3408,7 @@
*/
public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent,
UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
- boolean allowMissingTarget) {
+ boolean allowMissingTarget, boolean useLowResIcon) {
if (user == null) {
Log.d(TAG, "Null user found in getShortcutInfo");
return null;
@@ -3424,7 +3430,7 @@
}
final ShortcutInfo info = new ShortcutInfo();
- mIconCache.getTitleAndIcon(info, componentName, lai, user, false);
+ mIconCache.getTitleAndIcon(info, componentName, lai, user, false, useLowResIcon);
if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) {
Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context);
info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index b7a271e..59c8d92 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -57,7 +57,7 @@
private static final String TAG = "Launcher.LauncherProvider";
private static final boolean LOGD = false;
- private static final int DATABASE_VERSION = 22;
+ private static final int DATABASE_VERSION = 23;
static final String OLD_AUTHORITY = "com.android.launcher2.settings";
static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -413,7 +413,8 @@
"modified INTEGER NOT NULL DEFAULT 0," +
"restored INTEGER NOT NULL DEFAULT 0," +
"profileId INTEGER DEFAULT " + userSerialNumber + "," +
- "rank INTEGER NOT NULL DEFAULT 0" +
+ "rank INTEGER NOT NULL DEFAULT 0," +
+ "options INTEGER NOT NULL DEFAULT 0" +
");");
addWorkspacesTable(db);
@@ -524,18 +525,9 @@
}
}
case 15: {
- db.beginTransaction();
- try {
- // Insert new column for holding restore status
- db.execSQL("ALTER TABLE favorites " +
- "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;");
- db.setTransactionSuccessful();
- } catch (SQLException ex) {
- Log.e(TAG, ex.getMessage(), ex);
+ if (!addIntegerColumn(db, Favorites.RESTORED, 0)) {
// Old version remains, which means we wipe old data
break;
- } finally {
- db.endTransaction();
}
}
case 16: {
@@ -573,6 +565,12 @@
break;
}
case 22: {
+ if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) {
+ // Old version remains, which means we wipe old data
+ break;
+ }
+ }
+ case 23: {
// DB Upgraded successfully
return;
}
@@ -682,20 +680,21 @@
}
private boolean addProfileColumn(SQLiteDatabase db) {
+ UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
+ // Default to the serial number of this user, for older
+ // shortcuts.
+ long userSerialNumber = userManager.getSerialNumberForUser(
+ UserHandleCompat.myUserHandle());
+ return addIntegerColumn(db, Favorites.PROFILE_ID, userSerialNumber);
+ }
+
+ private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
db.beginTransaction();
try {
- UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
- // Default to the serial number of this user, for older
- // shortcuts.
- long userSerialNumber = userManager.getSerialNumberForUser(
- UserHandleCompat.myUserHandle());
- // Insert new column for holding user serial number
- db.execSQL("ALTER TABLE favorites " +
- "ADD COLUMN profileId INTEGER DEFAULT "
- + userSerialNumber + ";");
+ db.execSQL("ALTER TABLE favorites ADD COLUMN "
+ + columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";");
db.setTransactionSuccessful();
} catch (SQLException ex) {
- // Old version remains, which means we wipe old data
Log.e(TAG, ex.getMessage(), ex);
return false;
} finally {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 13fd7ee..d161fbb 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -309,5 +309,11 @@
* <p>Type: INTEGER</p>
*/
static final String RANK = "rank";
+
+ /**
+ * Stores general flag based options for {@link ItemInfo}s.
+ * <p>Type: INTEGER</p>
+ */
+ static final String OPTIONS = "options";
}
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 08ffaa2..9f7da6c 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -84,6 +84,11 @@
boolean usingFallbackIcon;
/**
+ * Indicates whether we're using a low res icon
+ */
+ boolean usingLowResIcon;
+
+ /**
* If isShortcut=true and customIcon=false, this contains a reference to the
* shortcut icon as an application's resource.
*/
@@ -192,7 +197,8 @@
public void updateIcon(IconCache iconCache) {
if (itemType == Favorites.ITEM_TYPE_APPLICATION) {
- iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user);
+ iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user,
+ shouldUseLowResIcon());
}
}
@@ -264,5 +270,9 @@
mInstallProgress = progress;
status |= FLAG_INSTALL_SESSION_ACTIVE;
}
+
+ public boolean shouldUseLowResIcon() {
+ return usingLowResIcon && container >= 0 && rank >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a59e25e..7ebdf3a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -4817,7 +4817,8 @@
if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
// For auto install apps update the icon as well as label.
mIconCache.getTitleAndIcon(shortcutInfo,
- shortcutInfo.promisedIntent, user);
+ shortcutInfo.promisedIntent, user,
+ shortcutInfo.shouldUseLowResIcon());
} else {
// Only update the icon for restored apps.
shortcutInfo.updateIcon(mIconCache);
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index 0c6bfbf..6e80c2f 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -184,21 +184,18 @@
*/
// TODO: get rid of the dynamic matrix creation
public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout,
- int orientation, int allappsiconRank, boolean includeAllappsicon) {
+ boolean isHorizontal, int allappsiconRank, boolean includeAllappsicon) {
ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
int m, n;
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (isHorizontal) {
m = iconLayout.getCountX();
n = iconLayout.getCountY() + hotseatLayout.getCountY();
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ } else {
m = iconLayout.getCountX() + hotseatLayout.getCountX();
n = iconLayout.getCountY();
- } else {
- throw new IllegalStateException(String.format(
- "orientation type=%d is not supported for key board events.", orientation));
}
int[][] matrix = createFullMatrix(m, n, false /* set all cell to empty */);
@@ -215,7 +212,7 @@
// is truncated. If it is negative, then all apps icon index is not inserted.
for(int i = hotseatParent.getChildCount() - 1; i >= (includeAllappsicon ? 0 : 1); i--) {
int delta = 0;
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (isHorizontal) {
int cx = ((CellLayout.LayoutParams)
hotseatParent.getChildAt(i).getLayoutParams()).cellX;
if ((includeAllappsicon && cx >= allappsiconRank) ||
@@ -223,7 +220,7 @@
delta = -1;
}
matrix[cx + delta][iconLayout.getCountY()] = iconParent.getChildCount() + i;
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ } else {
int cy = ((CellLayout.LayoutParams)
hotseatParent.getChildAt(i).getLayoutParams()).cellY;
if ((includeAllappsicon && cy >= allappsiconRank) ||