summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorTyson Miller <tmiller@cyngn.com>2015-11-10 08:59:15 -0800
committerGerrit Code Review <gerrit@cyanogenmod.org>2016-01-28 17:00:28 -0800
commit7df0227f6a835641a3d41327a65845806ef070fb (patch)
tree3fcf92a1fba468facc675ad47accb08ec2f9f907 /src/com
parent84dfd2a560442953af78aa77dbd6c909aed63589 (diff)
downloadandroid_packages_apps_Trebuchet-7df0227f6a835641a3d41327a65845806ef070fb.tar.gz
android_packages_apps_Trebuchet-7df0227f6a835641a3d41327a65845806ef070fb.tar.bz2
android_packages_apps_Trebuchet-7df0227f6a835641a3d41327a65845806ef070fb.zip
Port Remote Folder from 12.1 to 13.
Change-Id: If8cf9d5f054e8948ead702883b79f28db26c4d8b
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/launcher3/AppInfo.java41
-rw-r--r--src/com/android/launcher3/BaseRecyclerView.java14
-rw-r--r--src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java125
-rw-r--r--src/com/android/launcher3/BaseRecyclerViewScrubber.java3
-rw-r--r--src/com/android/launcher3/BubbleTextView.java106
-rw-r--r--src/com/android/launcher3/CellLayout.java2
-rw-r--r--src/com/android/launcher3/CheckLongPressHelper.java2
-rw-r--r--src/com/android/launcher3/DeleteDropTarget.java10
-rw-r--r--src/com/android/launcher3/DragLayer.java1
-rw-r--r--src/com/android/launcher3/Folder.java192
-rw-r--r--src/com/android/launcher3/FolderIcon.java53
-rw-r--r--src/com/android/launcher3/FolderInfo.java63
-rw-r--r--src/com/android/launcher3/FolderPagedView.java47
-rw-r--r--src/com/android/launcher3/InsettableFrameLayout.java1
-rw-r--r--src/com/android/launcher3/ItemInfo.java7
-rw-r--r--src/com/android/launcher3/Launcher.java59
-rw-r--r--src/com/android/launcher3/LauncherApplication.java2
-rw-r--r--src/com/android/launcher3/LauncherModel.java45
-rw-r--r--src/com/android/launcher3/LauncherProvider.java21
-rw-r--r--src/com/android/launcher3/LauncherSettings.java4
-rw-r--r--src/com/android/launcher3/OverviewSettingsPanel.java36
-rw-r--r--src/com/android/launcher3/PagedView.java2
-rw-r--r--src/com/android/launcher3/ShortcutInfo.java10
-rw-r--r--src/com/android/launcher3/Utilities.java13
-rw-r--r--src/com/android/launcher3/Workspace.java14
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java115
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java166
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java187
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java184
-rw-r--r--src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java16
-rw-r--r--src/com/android/launcher3/settings/SettingsProvider.java1
-rw-r--r--src/com/android/launcher3/stats/LauncherStats.java35
-rw-r--r--src/com/android/launcher3/stats/internal/model/TrackingEvent.java8
33 files changed, 1039 insertions, 546 deletions
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index a03e6081a..ca8ae255c 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -60,6 +60,7 @@ public class AppInfo extends ItemInfo {
public static final int DOWNLOADED_FLAG = 1;
static final int UPDATED_SYSTEM_APP_FLAG = 2;
+ static final int REMOTE_APP_FLAG = 4;
public int flags = 0;
@@ -91,6 +92,15 @@ public class AppInfo extends ItemInfo {
this.user = user;
}
+ public AppInfo(Intent intent, String title, UserHandleCompat user) {
+ this.componentName = intent.getComponent();
+ this.container = ItemInfo.NO_ID;
+
+ this.intent = intent;
+ this.title = title;
+ this.user = user;
+ }
+
public static int initFlags(LauncherActivityInfoCompat info) {
int appFlags = info.getApplicationInfo().flags;
int flags = 0;
@@ -114,6 +124,23 @@ public class AppInfo extends ItemInfo {
iconBitmap = info.iconBitmap;
}
+ /**
+ * Check if this app has a specific flag.
+ * @param flag flag to check.
+ * @return true if the flag is present, false otherwise.
+ */
+ public boolean hasFlag(int flag) {
+ return (flags & flag) != 0;
+ }
+
+ /**
+ * Set a flag for this app
+ * @param flag flag to apply.
+ */
+ public void setFlag(int flag) {
+ flags |= flag;
+ }
+
@Override
public String toString() {
return "ApplicationInfo(title=" + title + " id=" + this.id
@@ -152,4 +179,18 @@ public class AppInfo extends ItemInfo {
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
.putExtra(EXTRA_PROFILE, serialNumber);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof AppInfo) {
+ return componentName.equals(((AppInfo) o).componentName);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return componentName.hashCode();
+ }
}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 77925b5b3..cdf9aa2aa 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -77,7 +77,7 @@ public abstract class BaseRecyclerView extends RecyclerView
mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
ScrollListener listener = new ScrollListener();
- setOnScrollListener(listener);
+ addOnScrollListener(listener);
}
private class ScrollListener extends OnScrollListener {
@@ -286,8 +286,7 @@ public abstract class BaseRecyclerView extends RecyclerView
// Calculate the current scroll position, the scrollY of the recycler view accounts for the
// view padding, while the scrollBarY is drawn right up to the background padding (ignoring
// padding)
- int scrollY = getPaddingTop() +
- (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
+ int scrollY = getCurrentScroll(scrollPosState);
int scrollBarY = mBackgroundPadding.top +
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
@@ -302,6 +301,15 @@ public abstract class BaseRecyclerView extends RecyclerView
}
/**
+ * @param scrollPosState current state of view scrolling.
+ * @return the vertical scroll position
+ */
+ protected int getCurrentScroll(ScrollPositionState scrollPosState) {
+ return getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) -
+ scrollPosState.rowTopOffset;
+ }
+
+ /**
* Maps the touch (from 0..1) to the adapter position that should be visible.
* <p>Override in each subclass of this base class.
*/
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index e7b79927a..e70be9989 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -18,6 +18,7 @@ package com.android.launcher3;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -27,8 +28,11 @@ import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewConfiguration;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.util.Thunk;
/**
@@ -36,11 +40,130 @@ import com.android.launcher3.util.Thunk;
*/
public class BaseRecyclerViewFastScrollBar {
- public interface FastScrollFocusableView {
+ public interface FastScrollFocusable {
+ int FAST_SCROLL_FOCUS_DIMMABLE = 1;
+ int FAST_SCROLL_FOCUS_SCALABLE = 2;
+
void setFastScrollFocused(boolean focused, boolean animated);
void setFastScrollDimmed(boolean dimmed, boolean animated);
}
+ /**
+ * Helper class to apply fast scroll focus functionality to any view.
+ */
+ public static class FastScrollFocusApplicator implements FastScrollFocusable {
+ private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175;
+ private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125;
+ private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f;
+
+ private final View mView;
+ private final int mFastScrollMode;
+
+ private ObjectAnimator mFastScrollFocusAnimator;
+ private ObjectAnimator mFastScrollDimAnimator;
+ private boolean mFastScrollFocused;
+ private boolean mFastScrollDimmed;
+
+ public static void createApplicator(final View v, int mode) {
+ FastScrollFocusApplicator applicator = new FastScrollFocusApplicator(v, mode);
+ v.setTag(R.id.fast_scroll_focus_applicator_tag, applicator);
+ }
+
+ public static void setFastScrollFocused(final View v, boolean focused, boolean animated) {
+ FastScrollFocusable focusable = getFromView(v);
+ if (focusable == null) return;
+
+ focusable.setFastScrollFocused(focused, animated);
+ }
+
+ public static void setFastScrollDimmed(final View v, boolean dimmed, boolean animated) {
+ FastScrollFocusable focusable = getFromView(v);
+ if (focusable == null) return;
+
+ focusable.setFastScrollDimmed(dimmed, animated);
+ }
+
+ private static FastScrollFocusable getFromView(final View v) {
+ Object tag = v.getTag(R.id.fast_scroll_focus_applicator_tag);
+ if (tag != null) {
+ return (FastScrollFocusApplicator) tag;
+ }
+ return null;
+ }
+
+ private FastScrollFocusApplicator(final View v, final int mode) {
+ mView = v;
+ mFastScrollMode = mode;
+ }
+
+ public void setFastScrollFocused(boolean focused, boolean animated) {
+ if ((mFastScrollMode & FAST_SCROLL_FOCUS_SCALABLE) == 0) {
+ return;
+ }
+
+ if (mFastScrollFocused != focused) {
+ mFastScrollFocused = focused;
+
+ if (animated) {
+ // Clean up the previous focus animator
+ if (mFastScrollFocusAnimator != null) {
+ mFastScrollFocusAnimator.cancel();
+ }
+
+ // Setup animator for bi-directional scaling.
+ float value = focused ? FAST_SCROLL_FOCUS_MAX_SCALE : 1f;
+ PropertyValuesHolder pvhScaleX =
+ PropertyValuesHolder.ofFloat(View.SCALE_X, value);
+ PropertyValuesHolder pvhScaleY =
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, value);
+ mFastScrollFocusAnimator = ObjectAnimator.ofPropertyValuesHolder(mView,
+ pvhScaleX, pvhScaleY);
+
+ if (focused) {
+ mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator());
+ } else {
+ mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator());
+ }
+ mFastScrollFocusAnimator.setDuration(focused ?
+ FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
+ mFastScrollFocusAnimator.start();
+ }
+ }
+
+ // Let the view do any additional operations if it wants.
+ if (mView instanceof FastScrollFocusable) {
+ ((FastScrollFocusable) mView).setFastScrollFocused(focused, animated);
+ }
+ }
+
+ public void setFastScrollDimmed(boolean dimmed, boolean animated) {
+ if ((mFastScrollMode & FAST_SCROLL_FOCUS_DIMMABLE) == 0) {
+ return;
+ }
+
+ if (!animated) {
+ mFastScrollDimmed = dimmed;
+ mView.setAlpha(dimmed ? 0.4f : 1f);
+ } else if (mFastScrollDimmed != dimmed) {
+ mFastScrollDimmed = dimmed;
+
+ // Clean up the previous dim animator
+ if (mFastScrollDimAnimator != null) {
+ mFastScrollDimAnimator.cancel();
+ }
+ mFastScrollDimAnimator = ObjectAnimator.ofFloat(mView, View.ALPHA, dimmed ? 0.4f : 1f);
+ mFastScrollDimAnimator.setDuration(dimmed ?
+ FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
+ mFastScrollDimAnimator.start();
+ }
+
+ // Let the view do any additional operations if it wants.
+ if (mView instanceof FastScrollFocusable) {
+ ((FastScrollFocusable) mView).setFastScrollDimmed(dimmed, animated);
+ }
+ }
+ }
+
private final static int MAX_TRACK_ALPHA = 30;
private final static int SCROLL_BAR_VIS_DURATION = 150;
diff --git a/src/com/android/launcher3/BaseRecyclerViewScrubber.java b/src/com/android/launcher3/BaseRecyclerViewScrubber.java
index 4299b4457..e3e08aa7c 100644
--- a/src/com/android/launcher3/BaseRecyclerViewScrubber.java
+++ b/src/com/android/launcher3/BaseRecyclerViewScrubber.java
@@ -400,6 +400,9 @@ public class BaseRecyclerViewScrubber extends LinearLayout {
}
private void progressChanged(SeekBar seekBar, int index, boolean fromUser) {
+ if (!fromUser) {
+ return;
+ }
sendAnimatePickMessage(index, seekBar.getWidth(), mLastIndex);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index adc153f2e..facafd354 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -37,13 +36,10 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.TextView;
import com.android.launcher3.IconCache.IconLoadRequest;
import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.settings.SettingsProvider;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -51,7 +47,7 @@ import com.android.launcher3.settings.SettingsProvider;
* too aggressive.
*/
public class BubbleTextView extends TextView
- implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView {
+ implements BaseRecyclerViewFastScrollBar.FastScrollFocusable {
private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
@@ -64,12 +60,9 @@ public class BubbleTextView extends TextView
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
- private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f;
private static final int FAST_SCROLL_FOCUS_MODE_NONE = 0;
private static final int FAST_SCROLL_FOCUS_MODE_SCALE_ICON = 1;
private static final int FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG = 2;
- private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175;
- private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125;
private final Launcher mLauncher;
private Drawable mIcon;
@@ -94,12 +87,8 @@ public class BubbleTextView extends TextView
private boolean mIgnorePressedStateChange;
private boolean mDisableRelayout = false;
- private ObjectAnimator mFastScrollFocusAnimator;
- private ObjectAnimator mFastScrollDimAnimator;
private Paint mFastScrollFocusBgPaint;
private float mFastScrollFocusFraction;
- private boolean mFastScrollFocused;
- private boolean mFastScrollDimmed;
private final int mFastScrollMode = FAST_SCROLL_FOCUS_MODE_SCALE_ICON;
private IconLoadRequest mIconLoadRequest;
@@ -170,16 +159,21 @@ public class BubbleTextView extends TextView
public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
boolean promiseStateChanged) {
- Bitmap b = info.getIcon(iconCache);
+ Drawable iconDrawable;
+ if (info.customDrawable != null) {
+ iconDrawable = info.customDrawable;
+ } else {
+ Bitmap b = info.getIcon(iconCache);
- if (b.getWidth() > mIconSize || b.getHeight() > mIconSize) {
- b = Bitmap.createScaledBitmap(b, mIconSize, mIconSize, false);
- info.setIcon(b);
- info.updateIcon(iconCache);
- }
+ if (b.getWidth() > mIconSize || b.getHeight() > mIconSize) {
+ b = Bitmap.createScaledBitmap(b, mIconSize, mIconSize, false);
+ info.setIcon(b);
+ info.updateIcon(iconCache);
+ }
- FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
- iconDrawable.setGhostModeEnabled(info.isDisabled != 0);
+ iconDrawable = mLauncher.createIconDrawable(b);
+ ((FastBitmapDrawable) iconDrawable).setGhostModeEnabled(info.isDisabled != 0);
+ }
setIcon(iconDrawable, mIconSize);
if (info.contentDescription != null) {
@@ -194,7 +188,13 @@ public class BubbleTextView extends TextView
}
public void applyFromApplicationInfo(AppInfo info) {
- setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
+ Drawable iconDrawable;
+ if (info.customDrawable != null) {
+ iconDrawable = info.customDrawable;
+ } else {
+ iconDrawable = mLauncher.createIconDrawable(info.iconBitmap);
+ }
+ setIcon(iconDrawable, mIconSize);
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
@@ -567,6 +567,11 @@ public class BubbleTextView extends TextView
* Verifies that the current icon is high-res otherwise posts a request to load the icon.
*/
public void verifyHighRes() {
+ // Custom drawables cannot be verified.
+ if (getTag() instanceof ItemInfo && ((ItemInfo) getTag()).customDrawable != null) {
+ return;
+ }
+
if (mIconLoadRequest != null) {
mIconLoadRequest.cancel();
mIconLoadRequest = null;
@@ -592,73 +597,20 @@ public class BubbleTextView extends TextView
}
}
- // Setters & getters for the animation
- public void setFastScrollFocus(float fraction) {
- mFastScrollFocusFraction = fraction;
- if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_SCALE_ICON) {
- setScaleX(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
- setScaleY(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
- } else {
- invalidate();
- }
- }
-
- public float getFastScrollFocus() {
- return mFastScrollFocusFraction;
- }
-
@Override
public void setFastScrollFocused(final boolean focused, boolean animated) {
if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_NONE) {
return;
}
- if (mFastScrollFocused != focused) {
- mFastScrollFocused = focused;
-
- if (animated) {
- // Clean up the previous focus animator
- if (mFastScrollFocusAnimator != null) {
- mFastScrollFocusAnimator.cancel();
- }
- mFastScrollFocusAnimator = ObjectAnimator.ofFloat(this, "fastScrollFocus",
- focused ? 1f : 0f);
- if (focused) {
- mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator());
- } else {
- mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator());
- }
- mFastScrollFocusAnimator.setDuration(focused ?
- FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
- mFastScrollFocusAnimator.start();
- } else {
- mFastScrollFocusFraction = focused ? 1f : 0f;
- }
+ if (!animated) {
+ mFastScrollFocusFraction = focused ? 1f : 0f;
}
}
@Override
public void setFastScrollDimmed(boolean dimmed, boolean animated) {
- if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_NONE) {
- return;
- }
-
- if (!animated) {
- mFastScrollDimmed = dimmed;
- setAlpha(dimmed ? 0.4f : 1f);
- } else if (mFastScrollDimmed != dimmed) {
- mFastScrollDimmed = dimmed;
-
- // Clean up the previous dim animator
- if (mFastScrollDimAnimator != null) {
- mFastScrollDimAnimator.cancel();
- }
- mFastScrollDimAnimator = ObjectAnimator.ofFloat(this, View.ALPHA,
- dimmed ? 0.4f : 1f);
- mFastScrollDimAnimator.setDuration(dimmed ?
- FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
- mFastScrollDimAnimator.start();
- }
+ // No special functionality here.
}
/**
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index daf26411d..10599fa5e 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2224,7 +2224,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
}
- private void setUseTempCoords(boolean useTempCoords) {
+ public void setUseTempCoords(boolean useTempCoords) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 483c62249..bd1d84128 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -30,6 +30,8 @@ public class CheckLongPressHelper {
class CheckForLongPress implements Runnable {
public void run() {
+ if (!mView.isLongClickable()) return;
+
if ((mView.getParent() != null) && mView.hasWindowFocus()
&& !mHasPerformedLongPress) {
boolean handled;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9c8659c29..f51a43a91 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -74,8 +74,14 @@ public class DeleteDropTarget extends ButtonDropTarget {
LauncherModel.deleteItemFromDatabase(launcher, item);
} else if (item instanceof FolderInfo) {
FolderInfo folder = (FolderInfo) item;
- launcher.removeFolder(folder);
- LauncherModel.deleteFolderContentsFromDatabase(launcher, folder);
+
+ // Remote folder should not really be deleted. Let the manager handle it.
+ if (folder.isRemote()) {
+ launcher.getRemoteFolderManager().onFolderDeleted();
+ } else {
+ launcher.removeFolder(folder);
+ LauncherModel.deleteFolderContentsFromDatabase(launcher, folder);
+ }
} else if (item instanceof LauncherAppWidgetInfo) {
final LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) item;
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 1c18747c1..03c5a1491 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -823,6 +823,7 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public void onChildViewRemoved(View parent, View child) {
+ super.onChildViewRemoved(parent, child);
updateChildIndices();
}
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index ccb12b62a..bcc274d9e 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -227,10 +227,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mContent = (FolderPagedView) findViewById(R.id.folder_content);
mContent.setFolder(this);
- // We find out how tall footer wants to be (it is set to wrap_content), so that
- // we can allocate the appropriate amount of space for it.
- int measureSpec = MeasureSpec.UNSPECIFIED;
-
mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
mFolderName.setOnBackKeyListener(new ExtendedEditText.OnBackKeyListener() {
@Override
@@ -256,14 +252,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mFolderName.setVisibility(View.GONE);
}
mFolderLock = (ImageView) findViewById(R.id.folder_lock);
- mFolderLock.measure(measureSpec, measureSpec);
mFolderLock.setOnClickListener(this);
- mFolderLockHeight = mFolderLock.getMeasuredHeight();
mFooter = findViewById(R.id.folder_footer);
-
- mFooter.measure(measureSpec, measureSpec);
- mFooterHeight = mFooter.getMeasuredHeight();
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@@ -458,7 +449,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mInfo;
}
- void bind(FolderInfo info) {
+ void bind(final FolderInfo info) {
mInfo = info;
ArrayList<ShortcutInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
@@ -473,14 +464,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
LauncherModel.deleteItemFromDatabase(mLauncher, item);
}
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
- if (lp == null) {
- lp = new DragLayer.LayoutParams(0, 0);
- lp.customPosition = true;
- setLayoutParams(lp);
- }
- centerAboutIcon();
-
mItemsInvalidated = true;
updateTextViewFocus();
mInfo.addListener(this);
@@ -509,12 +492,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
* @param context The application's context.
+ * @param root The {@link View} parent of this folder.
*
* @return A new UserFolder.
*/
@SuppressLint("InflateParams")
- static Folder fromXml(Launcher launcher) {
- return (Folder) launcher.getLayoutInflater().inflate(R.layout.user_folder, null);
+ static Folder fromXml(Launcher launcher, ViewGroup root) {
+ return (Folder) launcher.getLayoutInflater().inflate(R.layout.user_folder, root, false);
}
/**
@@ -542,6 +526,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
private void prepareFakeFolderIcon() {
+ mFolderIcon.destroyDrawingCache();
mFolderIcon.buildDrawingCache(true);
Bitmap fakeFolderIcon = Bitmap.createBitmap(mFolderIcon.getDrawingCache());
@@ -568,7 +553,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
fakeFolderIconView.setVisibility(View.INVISIBLE);
}
- public void animateOpen(Workspace workspace, int[] folderTouch) {
+ public void animateOpen() {
if (!(getParent() instanceof DragLayer)) return;
mContent.completePendingPageChanges();
@@ -581,7 +566,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
final Runnable onCompleteRunnable;
if (!Utilities.ATLEAST_LOLLIPOP) {
positionAndSizeAsIcon();
- centerAboutIcon();
+ calculatePivot();
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
@@ -599,11 +584,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
};
} else {
- centerAboutIcon();
+ calculatePivot();
AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
- int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
- int height = getFolderHeight();
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight();
float transX = - 0.075f * (width / 2 - getPivotX());
float transY = - 0.075f * (height / 2 - getPivotY());
@@ -1156,7 +1141,16 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// Do nothing
}
+ /**
+ * @return true if contents should persist their status to the database.
+ */
+ protected boolean shouldUpdateContentsInDatabase() {
+ return true;
+ }
+
private void updateItemLocationsInDatabaseBatch() {
+ if (!shouldUpdateContentsInDatabase()) return;
+
ArrayList<View> list = getItemsInReadingOrder();
ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
for (int i = 0; i < list.size(); i++) {
@@ -1170,6 +1164,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
public void addItemLocationsInDatabase() {
+ if (!shouldUpdateContentsInDatabase()) return;
+
ArrayList<View> list = getItemsInReadingOrder();
for (int i = 0; i < list.size(); i++) {
View v = list.get(i);
@@ -1193,12 +1189,22 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mContent.isFull();
}
- private void centerAboutIcon() {
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+ protected void calculatePivot() {
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight();
+
+ // If we haven't measured ourselves yet, force one now to determine our dimensions.
+ if (width <= 0 && height <= 0) {
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ width = getMeasuredWidth();
+ height = getMeasuredHeight();
+ }
+
+ calculatePivot(width, height);
+ }
+ private void calculatePivot(int width, int height) {
DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
- int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
- int height = getFolderHeight();
float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
@@ -1234,11 +1240,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
(1.0f * folderPivotX / width));
mFolderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() *
(1.0f * folderPivotY / height));
-
- lp.width = width;
- lp.height = height;
- lp.x = left;
- lp.y = top;
}
float getPivotXForIconAnimation() {
@@ -1248,53 +1249,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mFolderIconPivotY;
}
- private int getContentAreaHeight() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- Rect workspacePadding = grid.getWorkspacePadding(mContent.mIsRtl);
- int maxContentAreaHeight = grid.availableHeightPx -
- workspacePadding.top - workspacePadding.bottom -
- mFooterHeight;
- int height = Math.min(maxContentAreaHeight,
- mContent.getDesiredHeight());
- return Math.max(height, MIN_CONTENT_DIMEN);
+ protected int getContentAreaHeight() {
+ return Math.max(mContent.getDesiredHeight(), MIN_CONTENT_DIMEN);
}
- private int getContentAreaWidth() {
+ protected int getContentAreaWidth() {
return Math.max(mContent.getDesiredWidth(), MIN_CONTENT_DIMEN);
}
- private int getFolderHeight() {
- return getFolderHeight(getContentAreaHeight());
- }
-
- private int getFolderHeight(int contentAreaHeight) {
- return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mFooterHeight;
- }
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int contentWidth = getContentAreaWidth();
- int contentHeight = getContentAreaHeight();
-
- int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY);
- int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);
+ mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight());
- mContent.setFixedSize(contentWidth, contentHeight);
- mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec);
-
- if (mContent.getChildCount() > 0) {
- int cellIconGap = (mContent.getPageAt(0).getCellWidth()
- - mLauncher.getDeviceProfile().iconSizePx) / 2;
- mFooter.setPadding(mContent.getPaddingLeft() + cellIconGap,
- mFooter.getPaddingTop(),
- mContent.getPaddingRight() + cellIconGap,
- mFooter.getPaddingBottom());
- }
- mFooter.measure(contentAreaWidthSpec,
- MeasureSpec.makeMeasureSpec(mFooterHeight, MeasureSpec.EXACTLY));
-
- int folderWidth = getPaddingLeft() + getPaddingRight() + contentWidth;
- int folderHeight = getFolderHeight(contentHeight);
- setMeasuredDimension(folderWidth, folderHeight);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
@@ -1445,8 +1411,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
// Actually move the item in the database if it was an external drag. Call this
// before creating the view, so that ShortcutInfo is updated appropriately.
- LauncherModel.addOrMoveItemInDatabase(
- mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
+ if (shouldUpdateContentsInDatabase()) {
+ LauncherModel.addOrMoveItemInDatabase(
+ mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
+ }
// We only need to update the locations if it doesn't get handled in #onDropCompleted.
if (d.dragSource != this) {
@@ -1509,8 +1477,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (mSuppressOnAdd) return;
mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item));
mItemsInvalidated = true;
- LauncherModel.addOrMoveItemInDatabase(
- mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+
+ if (shouldUpdateContentsInDatabase()) {
+ LauncherModel.addOrMoveItemInDatabase(
+ mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+ }
}
public void onRemove(ShortcutInfo item) {
@@ -1518,8 +1489,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// If this item is being dragged from this open folder, we have already handled
// the work associated with removing the item, so we don't have to do anything here.
if (item == mCurrentDragInfo) return;
- View v = getViewForInfo(item);
- mContent.removeItem(v);
+ mContent.removeItem(getViewForInfo(item));
if (mState == STATE_ANIMATING) {
mRearrangeOnClose = true;
} else {
@@ -1530,14 +1500,64 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
}
- private View getViewForInfo(final ShortcutInfo item) {
- return mContent.iterateOverItems(new ItemOperator() {
+ @Override
+ public void onRemoveAll() {
+ // Clear the UX after folder contents are removed from the DB
+ removeViewsForItems(null);
+ mLauncher.closeFolder(this);
+ replaceFolderWithFinalItem();
+ }
- @Override
- public boolean evaluate(ItemInfo info, View view, View parent) {
- return info == item;
+ @Override
+ public void onRemoveAll(ArrayList<ShortcutInfo> items) {
+ removeViewsForItems(items);
+ if (mState == STATE_ANIMATING) {
+ mRearrangeOnClose = true;
+ } else {
+ rearrangeChildren();
+ }
+ if (mInfo.contents.isEmpty()) {
+ mLauncher.closeFolder(this);
+ }
+ replaceFolderWithFinalItem();
+ }
+
+ /**
+ * Remove all the supplied item views from this folder.
+ * @param items info of views to remove, or null if all views should be removed.
+ */
+ protected void removeViewsForItems(ArrayList<ShortcutInfo> items) {
+ mItemsInvalidated = true;
+ if (items == null) {
+ mContent.removeAllItems();
+ } else {
+ for (ShortcutInfo item : items) {
+ mContent.removeItem(getViewForInfo(item));
}
- });
+ }
+ }
+
+ /**
+ * Update the view tied to this shortcut.
+ * @param info updated info to be applied to view.
+ */
+ @SuppressWarnings("unused")
+ public void updateViewForInfo(final ShortcutInfo info) {
+ View v = getViewForInfo(info);
+ if (v != null & v instanceof BubbleTextView) {
+ ((BubbleTextView) v).reapplyItemInfo(info);
+
+ mItemsInvalidated = true;
+ }
+ }
+
+ public View getViewForInfo(ShortcutInfo item) {
+ View v = mContent.getChildAtRank(item.rank);
+ if (v != null && v.getTag() == item) {
+ return v;
+ }
+
+ return null;
}
public void onItemsChanged() {
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 9b6e3026b..356c2754b 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -174,7 +174,16 @@ public class FolderIcon extends FrameLayout implements FolderListener {
icon.mLauncher = launcher;
icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
folderInfo.title));
- Folder folder = Folder.fromXml(launcher);
+ Folder folder;
+ if (folderInfo.isRemote()) {
+ folder = launcher.getRemoteFolderManager().createRemoteFolder(icon, launcher.getDragLayer());
+ if (folder == null) {
+ LauncherModel.deleteItemFromDatabase(launcher, folderInfo);
+ return null;
+ }
+ } else {
+ folder = Folder.fromXml(launcher, launcher.getDragLayer());
+ }
folder.setDragController(launcher.getDragController());
folder.setFolderIcon(icon);
folder.bind(folderInfo);
@@ -236,6 +245,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
}
+ // Create an overlay badge if this FolderIcon is for a RemoteFolder
+ if (folderInfo.isRemote()) {
+ icon = RemoteFolderManager.addBadgeToFolderIcon(icon);
+ }
+
return icon;
}
@@ -377,10 +391,14 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
private boolean willAcceptItem(ItemInfo item) {
+ if (mInfo.isRemote()) return false;
+
final int itemType = item.itemType;
boolean hidden = false;
if (item instanceof FolderInfo){
+ if (((FolderInfo) item).isRemote()) return false;
+
hidden = ((FolderInfo) item).hidden;
}
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
@@ -686,7 +704,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
super.dispatchDraw(canvas);
if (mFolder == null) return;
- if (mFolder.getItemCount() == 0 && !mAnimating) return;
+ if (mFolder.getItemCount() == 0 && !mAnimating && !mInfo.isRemote()) return;
ArrayList<View> items = mFolder.getItemsInReadingOrder();
Drawable d;
@@ -695,13 +713,13 @@ public class FolderIcon extends FrameLayout implements FolderListener {
// Update our drawing parameters if necessary
if (mAnimating) {
computePreviewDrawingParams(mAnimParams.drawable);
- } else {
+ } else if (!items.isEmpty()) {
v = (TextView) items.get(0);
d = getTopDrawable(v);
- computePreviewDrawingParams(d);
+ if (d != null) computePreviewDrawingParams(d);
}
- int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
+ int ntemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
// Hidden folder - don't display Preview
View folderLock = findViewById(R.id.folder_lock_image);
@@ -720,17 +738,22 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
if (!mAnimating) {
- for (int i = NUM_ITEMS_IN_PREVIEW; i >= 0; i--) {
+ for (int i = 0; i < NUM_ITEMS_IN_PREVIEW; i++) {
d = null;
- if (i < items.size()) {
+ if (mInfo.isRemote()) {
+ d = mLauncher.getRemoteFolderManager().getFolderIconDrawable(items, i);
+ } else if (i < items.size()) {
v = (TextView) items.get(i);
if (!mHiddenItems.contains(v.getTag())) {
d = getTopDrawable(v);
- mParams = computePreviewItemDrawingParams(i, mParams);
- mParams.drawable = d;
}
}
+ if (d != null) {
+ mParams = computePreviewItemDrawingParams(i, mParams);
+ mParams.drawable = d;
+ }
+
ImageView appIcon = null;
switch(i) {
case 0:
@@ -829,6 +852,18 @@ public class FolderIcon extends FrameLayout implements FolderListener {
requestLayout();
}
+ @Override
+ public void onRemoveAll() {
+ invalidate();
+ requestLayout();
+ }
+
+ @Override
+ public void onRemoveAll(ArrayList<ShortcutInfo> items) {
+ invalidate();
+ requestLayout();
+ }
+
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
setContentDescription(String.format(getContext().getString(R.string.folder_name_format),
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index a1147408f..7969d627b 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -28,6 +28,7 @@ import java.util.Arrays;
* Represents a folder containing shortcuts or apps.
*/
public class FolderInfo extends ItemInfo {
+ public static final int REMOTE_SUBTYPE = 1;
public static final int NO_FLAGS = 0x00000000;
@@ -50,6 +51,7 @@ public class FolderInfo extends ItemInfo {
* Whether this folder has been opened
*/
boolean opened;
+ int subType;
public int options;
@@ -92,6 +94,54 @@ public class FolderInfo extends ItemInfo {
itemsChanged();
}
+ /**
+ * Remove all apps and shortcuts. Does not change the DB unless
+ * LauncherModel.deleteFolderContentsFromDatabase(Context, FolderInfo) is called first.
+ */
+ public void removeAll() {
+ if (contents.isEmpty()) return;
+
+ contents.clear();
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onRemoveAll();
+ }
+ itemsChanged();
+ }
+
+ /**
+ * Remove all supplied shortcuts. Does not change the DB unless
+ * LauncherModel.deleteFolderContentsFromDatabase(Context, FolderInfo) is called first.
+ * @param items the shortcuts to remove.
+ */
+ public void removeAll(ArrayList<ShortcutInfo> items) {
+ if (items.isEmpty()) return;
+
+ contents.removeAll(items);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onRemoveAll(items);
+ }
+ itemsChanged();
+ }
+
+ /**
+ * @return true if this info represents a remote folder, false otherwise
+ */
+ public boolean isRemote() {
+ return (subType & REMOTE_SUBTYPE) != 0;
+ }
+
+ /**
+ * Set flag indicating whether this folder is remote
+ * @param remote true if folder is remote, false otherwise
+ */
+ public void setRemote(final boolean remote) {
+ if (remote) {
+ subType |= REMOTE_SUBTYPE;
+ } else {
+ subType &= ~REMOTE_SUBTYPE;
+ }
+ }
+
public void setTitle(CharSequence title) {
this.title = title;
for (int i = 0; i < listeners.size(); i++) {
@@ -105,6 +155,7 @@ public class FolderInfo extends ItemInfo {
values.put(LauncherSettings.Favorites.TITLE, title.toString());
values.put(LauncherSettings.Favorites.OPTIONS, options);
values.put(LauncherSettings.Favorites.HIDDEN, hidden ? 1 : 0);
+ values.put(LauncherSettings.BaseLauncherColumns.SUBTYPE, subType);
}
void addListener(FolderListener listener) {
@@ -130,15 +181,17 @@ public class FolderInfo extends ItemInfo {
}
interface FolderListener {
- public void onAdd(ShortcutInfo item);
- public void onRemove(ShortcutInfo item);
- public void onTitleChanged(CharSequence title);
- public void onItemsChanged();
+ void onAdd(ShortcutInfo item);
+ void onRemove(ShortcutInfo item);
+ void onRemoveAll();
+ void onRemoveAll(ArrayList<ShortcutInfo> items);
+ void onTitleChanged(CharSequence title);
+ void onItemsChanged();
}
@Override
public String toString() {
- return "FolderInfo(id=" + this.id + " type=" + this.itemType
+ return "FolderInfo(id=" + this.id + " type=" + this.itemType + " subtype=" + this.subType
+ " container=" + this.container + " screen=" + screenId
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
+ " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index a7940d552..5d5ac3d5a 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -114,7 +114,7 @@ public class FolderPagedView extends PagedView {
* The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
* maintaining the restrictions of {@link #mMaxCountX} &amp; {@link #mMaxCountY}.
*/
- private void setupContentDimensions(int count) {
+ public void setupContentDimensions(int count) {
mAllocatedContentSize = count;
boolean done;
if (count >= mMaxItemsPerPage) {
@@ -274,6 +274,51 @@ public class FolderPagedView extends PagedView {
}
}
+ public void removeAllItems() {
+ for (int i = 0; i < getChildCount(); i++) {
+ getPageAt(i).removeAllViews();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (getChildCount() == 0) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ } else {
+ // We should only be as large as our pages, so measure all of them first.
+ View page = null;
+ for (int i = 0; i < getChildCount(); i++) {
+ page = getChildAt(i);
+ page.measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ // And then set ourselves to their size.
+ int width = getPaddingLeft() + page.getMeasuredWidth() + getPaddingRight();
+ int height = getPaddingTop() + page.getMeasuredHeight() + getPaddingBottom();
+ mViewport.set(0, 0, width, height);
+ setMeasuredDimension(width, height);
+ }
+ }
+
+ /**
+ * Find the child view for the given rank.
+ * @param rank sorted index of child.
+ * @return view of child at given rank.
+ */
+ public View getChildAtRank(int rank) {
+ int pagePos = rank % mMaxItemsPerPage;
+ int pageNo = rank / mMaxItemsPerPage;
+ int cellX = pagePos % mGridCountX;
+ int cellY = pagePos / mGridCountX;
+
+ CellLayout page = getPageAt(pageNo);
+ if (page != null) {
+ return page.getChildAt(cellX, cellY);
+ } else {
+ return null;
+ }
+ }
+
/**
* Updates position and rank of all the children in the view.
* It essentially removes all views from all the pages and then adds them again in appropriate
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 6400a0f89..770d1de1a 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -101,6 +101,7 @@ public class InsettableFrameLayout extends FrameLayout implements
@Override
public void onChildViewRemoved(View parent, View child) {
+ setFrameLayoutChildInsets(child, new Rect(), mInsets);
}
}
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index c7729ffe6..b30c954f5 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -20,6 +20,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
@@ -127,6 +128,12 @@ public class ItemInfo {
public UserHandleCompat user;
+ /**
+ * A custom drawable to use for the icon. Not persisted to the database because
+ * it is not guaranteed to be a bitmap (could be a vector).
+ */
+ Drawable customDrawable;
+
public ItemInfo() {
user = UserHandleCompat.myUserHandle();
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3454280b8..3b31b3ac5 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -112,7 +112,6 @@ import com.android.launcher3.list.SettingsPinnedHeaderAdapter;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.stats.LauncherStats;
-import com.android.launcher3.stats.internal.service.AggregationIntentService;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Thunk;
@@ -266,6 +265,9 @@ public class Launcher extends Activity
protected HiddenFolderFragment mHiddenFolderFragment;
private DynamicGridSizeFragment mDynamicGridSizeFragment;
+ private static RemoteFolderManager sRemoteFolderManager;
+ private boolean mRemoteDrawerEnabled;
+
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
@@ -376,7 +378,7 @@ public class Launcher extends Activity
// This is set to the view that launched the activity that navigated the user away from
// launcher. Since there is no callback for when the activity has finished launching, enable
// the press state and keep this reference to reset the press state when we return to launcher.
- private BubbleTextView mWaitingForResume;
+ BubbleTextView mWaitingForResume;
private long mDefaultScreenId;
@@ -519,6 +521,14 @@ public class Launcher extends Activity
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
+ if (sRemoteFolderManager == null) {
+ sRemoteFolderManager = new RemoteFolderManager(this);
+ } else {
+ sRemoteFolderManager.onRecreateLauncher(this);
+ }
+ mRemoteDrawerEnabled = SettingsProvider.getBoolean(this, null,
+ R.bool.preferences_interface_homescreen_remote_folder_default);
+
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
// this also ensures that any synchronous binding below doesn't re-trigger another
// LauncherModel load.
@@ -1825,7 +1835,8 @@ public class Launcher extends Activity
SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
R.bool.preferences_interface_homescreen_hide_icon_labels_default);
mDefaultScreenId = SettingsProvider.getLongCustomDefault(this,
- SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID, 1);
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID,
+ R.integer.preferences_interface_homescreen_id_default);
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
@@ -1884,6 +1895,7 @@ public class Launcher extends Activity
mAppsView.addApps(addedApps);
tryAndUpdatePredictedApps();
mAppsView.reset();
+ sRemoteFolderManager.onReloadAppDrawer();
}
public void reloadWidgetView() {
@@ -2148,6 +2160,10 @@ public class Launcher extends Activity
return mWorkspace;
}
+ public RemoteFolderManager getRemoteFolderManager() {
+ return sRemoteFolderManager;
+ }
+
public Hotseat getHotseat() {
return mHotseat;
}
@@ -2719,10 +2735,14 @@ public class Launcher extends Activity
}
FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
- int cellY) {
- final FolderInfo folderInfo = new FolderInfo();
+ int cellY) {
+ FolderInfo folderInfo = new FolderInfo();
folderInfo.title = getText(R.string.folder_name);
+ return addFolder(layout, container, screenId, cellX, cellY, folderInfo);
+ }
+ FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
+ int cellY, FolderInfo folderInfo) {
// Update the model
LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
cellX, cellY);
@@ -2869,10 +2889,11 @@ public class Launcher extends Activity
} else if (v == mAllAppsButton) {
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
+ AppInfo info = (AppInfo) tag;
startAppShortcutOrInfoActivity(v);
LauncherApplication.getLauncherStats().sendAppLaunchEvent(
- LauncherStats.ORIGIN_APPDRAWER, ((AppInfo)tag).componentName.getPackageName());
- String packageName = ((AppInfo)tag).getIntent().getComponent().getPackageName();
+ LauncherStats.ORIGIN_APPDRAWER, info.componentName.getPackageName());
+ String packageName = info.getIntent().getComponent().getPackageName();
if (LauncherStats.SETTINGS_PACKAGE_NAME.equals(packageName)) {
LauncherApplication.getLauncherStats()
.sendSettingsOpenedEvent(LauncherStats.ORIGIN_APPDRAWER);
@@ -3539,7 +3560,7 @@ public class Launcher extends Activity
folder.getParent() + ").");
return;
}
- folder.animateOpen(getWorkspace(), folderTouch);
+ folder.animateOpen();
/*growAndFadeOutFolderIcon(folderIcon);*/
// Notify the accessibility manager that this folder "window" has appeared and occluded
@@ -3783,6 +3804,8 @@ public class Launcher extends Activity
tryAndUpdatePredictedApps();
}
showAppsOrWidgets(State.APPS, animated, focusSearchBar);
+
+ sRemoteFolderManager.onAppDrawerOpened();
}
/**
@@ -3902,10 +3925,12 @@ public class Launcher extends Activity
* resumed.
*/
private void tryAndUpdatePredictedApps() {
- if (mLauncherCallbacks != null) {
- List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
- if (apps != null) {
- mAppsView.setPredictedApps(apps);
+ if (!mRemoteDrawerEnabled) {
+ if (mLauncherCallbacks != null) {
+ List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
+ if (apps != null) {
+ mAppsView.setPredictedAppComponents(apps);
+ }
}
}
}
@@ -4196,6 +4221,7 @@ public class Launcher extends Activity
if (addedApps != null && mAppsView != null) {
mAppsView.addApps(addedApps);
+ sRemoteFolderManager.onBindAddApps(addedApps);
}
}
@@ -4219,7 +4245,7 @@ public class Launcher extends Activity
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection<Animator> bounceAnims = new ArrayList<Animator>();
final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
- Workspace workspace = mWorkspace;
+ final Workspace workspace = mWorkspace;
long newShortcutsScreenId = -1;
for (int i = start; i < end; i++) {
final ItemInfo item = shortcuts.get(i);
@@ -4259,6 +4285,9 @@ public class Launcher extends Activity
view = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item, mIconCache);
+ if (view == null) {
+ continue;
+ }
((FolderIcon) view).setTextVisible(!mHideIconLabels);
break;
default:
@@ -4518,6 +4547,8 @@ public class Launcher extends Activity
}
mWorkspace.stripEmptyScreens();
+
+ sRemoteFolderManager.bindFinished();
}
private void sendLoadingCompleteBroadcastIfNecessary() {
@@ -4717,7 +4748,7 @@ public class Launcher extends Activity
for (AppInfo info : appInfos) {
removedComponents.add(info.componentName);
}
- if (!packageNames.isEmpty()) {
+ if (packageNames != null && !packageNames.isEmpty()) {
mWorkspace.removeItemsByPackageName(packageNames, user);
}
if (!removedComponents.isEmpty()) {
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
index 0bcc3bc6c..4bbcec073 100644
--- a/src/com/android/launcher3/LauncherApplication.java
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -37,7 +37,7 @@ public class LauncherApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
- sLauncherStats = LauncherStats.createInstance(this);
+ sLauncherStats = LauncherStats.getInstance(this);
AggregationIntentService.scheduleService(this);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index e140e2fab..2934b600d 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -441,6 +441,33 @@ public class LauncherModel extends BroadcastReceiver
ArrayList<Long> workspaceScreens,
ArrayList<Long> addedWorkspaceScreensFinal,
int spanX, int spanY) {
+
+ // Preferred screen is the next one after the default.
+ long preferredScreenId = SettingsProvider.getLongCustomDefault(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID,
+ R.integer.preferences_interface_homescreen_id_default);
+ int preferredScreenIndex = 0;
+ for (int i = 0; i < workspaceScreens.size(); i++) {
+ if (workspaceScreens.get(i) == preferredScreenId) {
+ preferredScreenIndex = i + 1;
+ break;
+ }
+ }
+
+ return findSpaceForItem(context, workspaceScreens, addedWorkspaceScreensFinal,
+ spanX, spanY, preferredScreenIndex);
+ }
+
+ /**
+ * Find a position on the screen for the given size or adds a new screen. Checks
+ * preferredScreen first, and if no space is found then starts searching from the left.
+ * @return screenId and the coordinates for the item.
+ */
+ @Thunk Pair<Long, int[]> findSpaceForItem(
+ Context context,
+ ArrayList<Long> workspaceScreens,
+ ArrayList<Long> addedWorkspaceScreensFinal,
+ int spanX, int spanY, int preferredScreenIndex) {
LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
// Use sBgItemsIdMap as all the items are already loaded.
@@ -458,6 +485,12 @@ public class LauncherModel extends BroadcastReceiver
}
}
+ // If we have a zero-id screen then we skip over it.
+ boolean hasZero = false;
+ if (!workspaceScreens.isEmpty() && workspaceScreens.get(0) == 0) {
+ hasZero = true;
+ }
+
// Find appropriate space for the item.
long screenId = 0;
int[] cordinates = new int[2];
@@ -465,7 +498,6 @@ public class LauncherModel extends BroadcastReceiver
int screenCount = workspaceScreens.size();
// First check the preferred screen.
- int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
if (preferredScreenIndex < screenCount) {
screenId = workspaceScreens.get(preferredScreenIndex);
found = findNextAvailableIconSpaceInScreen(
@@ -474,7 +506,8 @@ public class LauncherModel extends BroadcastReceiver
if (!found) {
// Search on any of the screens starting from the first screen.
- for (int screen = 1; screen < screenCount; screen++) {
+ int firstScreen = hasZero ? 1 : 0;
+ for (int screen = firstScreen; screen < screenCount; screen++) {
screenId = workspaceScreens.get(screen);
if (findNextAvailableIconSpaceInScreen(
screenItems.get(screenId), cordinates, spanX, spanY)) {
@@ -988,6 +1021,7 @@ public class LauncherModel extends BroadcastReceiver
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
final int hiddenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.HIDDEN);
+ final int subType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE);
FolderInfo folderInfo = null;
switch (c.getInt(itemTypeIndex)) {
@@ -1005,6 +1039,7 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.cellY = c.getInt(cellYIndex);
folderInfo.options = c.getInt(optionsIndex);
folderInfo.hidden = c.getInt(hiddenIndex) > 0;
+ folderInfo.subType = subType;
return folderInfo;
}
@@ -1937,6 +1972,7 @@ public class LauncherModel extends BroadcastReceiver
LauncherSettings.Favorites.OPTIONS);
final int hiddenIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.HIDDEN);
+ final int subTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE);
final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
@@ -2216,6 +2252,7 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.spanY = 1;
folderInfo.options = c.getInt(optionsIndex);
folderInfo.hidden = c.getInt(hiddenIndex) > 0;
+ folderInfo.subType = c.getInt(subTypeIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens,
@@ -2704,7 +2741,9 @@ public class LauncherModel extends BroadcastReceiver
}
workspaceItems.remove(i);
folders.remove(Long.valueOf(item.id));
- } else if (folder.contents.size() == 0) {
+
+ // Remote folders are always empty on bind.
+ } else if (folder.contents.size() == 0 && !folder.isRemote()) {
LauncherModel.deleteFolderContentsFromDatabase(mContext, folder);
workspaceItems.remove(i);
folders.remove(Long.valueOf(item.id));
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 4c401866e..022ccaccd 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -69,7 +69,7 @@ public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
private static final boolean LOGD = false;
- private static final int DATABASE_VERSION = 27;
+ private static final int DATABASE_VERSION = 28;
public static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -540,7 +540,8 @@ public class LauncherProvider extends ContentProvider {
"profileId INTEGER DEFAULT " + userSerialNumber + "," +
"hidden INTEGER DEFAULT 0" + "," +
"rank INTEGER NOT NULL DEFAULT 0," +
- "options INTEGER NOT NULL DEFAULT 0" +
+ "options INTEGER NOT NULL DEFAULT 0," +
+ "subType INTEGER DEFAULT 0" +
");");
addWorkspacesTable(db);
@@ -767,7 +768,6 @@ public class LauncherProvider extends ContentProvider {
break;
}
case 27: {
- // DB Upgraded successfully
migrateLauncherFavorite(db, "com.android.dialer", "com.cyngn.dialer",
"com.android.dialer.DialtactsActivity",
"com.android.dialer.DialtactsActivity");
@@ -780,7 +780,20 @@ public class LauncherProvider extends ContentProvider {
migrateLauncherFavorite(db, "org.cyanogenmod.snap", "com.android.camera2",
"com.android.camera.CameraLauncher",
"com.android.camera.CameraLauncher");
- return;
+ }
+ case 28: {
+ db.beginTransaction();
+ try {
+ db.execSQL("ALTER TABLE favorites " +
+ "ADD COLUMN subType INTEGER DEFAULT 0;");
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ return;
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(TAG, ex.getMessage(), ex);
+ db.endTransaction();
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index e9f7b0d09..20428670b 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -47,6 +47,10 @@ public class LauncherSettings {
public static final String HIDDEN = "hidden";
/**
+ * Folder subtype, used for Remote Folders
+ */
+ static final String SUBTYPE = "subType";
+ /**
* The Intent URL of the gesture, describing what it points to. This
* value is given to {@link android.content.Intent#parseUri(String, int)} to create
* an Intent that can be launched.
diff --git a/src/com/android/launcher3/OverviewSettingsPanel.java b/src/com/android/launcher3/OverviewSettingsPanel.java
index 229b01991..1b75602dc 100644
--- a/src/com/android/launcher3/OverviewSettingsPanel.java
+++ b/src/com/android/launcher3/OverviewSettingsPanel.java
@@ -6,6 +6,10 @@ import android.database.MatrixCursor;
import android.widget.ListView;
import com.android.launcher3.list.PinnedHeaderListView;
import com.android.launcher3.list.SettingsPinnedHeaderAdapter;
+import com.android.launcher3.settings.SettingsProvider;
+
+import java.util.ArrayList;
+import java.util.Arrays;
public class OverviewSettingsPanel {
public static final String ANDROID_SETTINGS = "com.android.settings";
@@ -35,12 +39,6 @@ public class OverviewSettingsPanel {
res.getString(R.string.drawer_settings),
res.getString(R.string.app_settings)};
- String[] values = new String[]{
- res.getString(R.string.home_screen_search_text),
- res.getString(R.string.icon_labels),
- res.getString(R.string.scrolling_wallpaper),
- res.getString(R.string.grid_size_text)};
-
String[] valuesDrawer = new String[] {
res.getString(R.string.icon_labels),
res.getString(R.string.app_drawer_style),
@@ -61,7 +59,8 @@ public class OverviewSettingsPanel {
mSettingsAdapter.addPartition(false, true);
mSettingsAdapter.mPinnedHeaderCount = headers.length;
- mSettingsAdapter.changeCursor(HOME_SETTINGS_POSITION, createCursor(headers[0], values));
+ mSettingsAdapter.changeCursor(HOME_SETTINGS_POSITION,
+ createCursor(headers[0], getValues()));
mSettingsAdapter.changeCursor(DRAWER_SETTINGS_POSITION, createCursor(headers[1],
valuesDrawer));
mSettingsAdapter.changeCursor(APP_SETTINGS_POSITION, createCursor(headers[2], valuesApp));
@@ -77,6 +76,29 @@ public class OverviewSettingsPanel {
return cursor;
}
+ private String[] getValues() {
+ Resources res = mLauncher.getResources();
+ ArrayList<String> values = new ArrayList<String>(Arrays.asList(new String[]{
+ res.getString(R.string.home_screen_search_text),
+ res.getString(R.string.icon_labels),
+ res.getString(R.string.scrolling_wallpaper),
+ res.getString(R.string.grid_size_text)}));
+
+ // Optionally add additional value based on setting
+ boolean remoteAppsEnabled = SettingsProvider.getBoolean(mLauncher, null,
+ R.bool.preferences_interface_homescreen_remote_folder_default);
+ if (remoteAppsEnabled) {
+ String remoteAppsName = RemoteFolderManager.getFeatureTitle(res);
+ if (remoteAppsName != null) {
+ values.add(remoteAppsName);
+ }
+ }
+
+ String[] valuesArr = new String[values.size()];
+ values.toArray(valuesArr);
+ return valuesArr;
+ }
+
public void notifyDataSetInvalidated() {
mSettingsAdapter.notifyDataSetInvalidated();
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 786822c49..fe940c182 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -169,7 +169,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Thunk PageIndicator mPageIndicator;
// The viewport whether the pages are to be contained (the actual view may be larger than the
// viewport)
- private Rect mViewport = new Rect();
+ protected Rect mViewport = new Rect();
// Reordering
// We use the min scale to determine how much to expand the actually PagedView measured
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 5766cf2f2..b1a787bfc 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -100,7 +100,7 @@ public class ShortcutInfo extends ItemInfo {
/**
* The application icon.
*/
- private Bitmap mIcon;
+ Bitmap mIcon;
/**
* Indicates that the icon is disabled due to safe mode restrictions.
@@ -265,6 +265,14 @@ public class ShortcutInfo extends ItemInfo {
return (status & flag) != 0;
}
+ /**
+ * Check if this shortcut has a specific flag.
+ * @param flag flag to check.
+ * @return true if the flag is present, false otherwise.
+ */
+ public boolean hasFlag(int flag) {
+ return (flags & flag) != 0;
+ }
public final boolean isPromise() {
return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b51ef168a..d2ce2eeac 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -43,6 +43,8 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Process;
@@ -726,4 +728,15 @@ public final class Utilities {
return false;
}
}
+
+ public static boolean isNetworkConnected(Context context) {
+ ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+ return activeNetwork != null && activeNetwork.isConnected();
+ }
+
+ public static boolean isConnectedToWiFi(Context context) {
+ ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 54d153299..c9fe24748 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -78,6 +78,8 @@ import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -359,6 +361,13 @@ public class Workspace extends PagedView
}
}
+ /**
+ * @return A {@link List} of {@link Long}s representing ids of the workspace screens
+ */
+ public List<Long> getWorkspaceScreenIds() {
+ return mScreenOrder;
+ }
+
// estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
// dimension if unsuccessful
public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded) {
@@ -3807,6 +3816,11 @@ public class Workspace extends PagedView
return;
}
+ // Drop is finished, so we should use our actual cell coordinates now.
+ if (mDragInfo != null) {
+ ((CellLayout.LayoutParams) mDragInfo.cell.getLayoutParams()).useTmpCoords = false;
+ }
+
boolean beingCalledAfterUninstall = mDeferredAction != null;
if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 0e3cba83e..f9690f509 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -57,75 +57,6 @@ import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.List;
-
-
-/**
- * A merge algorithm that merges every section indiscriminately.
- */
-final class FullMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
-
- @Override
- public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
- AlphabeticalAppsList.SectionInfo withSection,
- int sectionAppCount, int numAppsPerRow, int mergeCount) {
- // Don't merge the predicted apps
- if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
- return false;
- }
- // Otherwise, merge every other section
- return true;
- }
-}
-
-/**
- * The logic we use to merge multiple sections. We only merge sections when their final row
- * contains less than a certain number of icons, and stop at a specified max number of merges.
- * In addition, we will try and not merge sections that identify apps from different scripts.
- */
-final class SimpleSectionMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
-
- private int mMinAppsPerRow;
- private int mMinRowsInMergedSection;
- private int mMaxAllowableMerges;
- private CharsetEncoder mAsciiEncoder;
-
- public SimpleSectionMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection,
- int maxNumMerges) {
- mMinAppsPerRow = minAppsPerRow;
- mMinRowsInMergedSection = minRowsInMergedSection;
- mMaxAllowableMerges = maxNumMerges;
- mAsciiEncoder = Charset.forName("US-ASCII").newEncoder();
- }
-
- @Override
- public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
- AlphabeticalAppsList.SectionInfo withSection,
- int sectionAppCount, int numAppsPerRow, int mergeCount) {
- // Don't merge the predicted apps
- if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
- return false;
- }
-
- // Continue merging if the number of hanging apps on the final row is less than some
- // fixed number (ragged), the merged rows has yet to exceed some minimum row count,
- // and while the number of merged sections is less than some fixed number of merges
- int rows = sectionAppCount / numAppsPerRow;
- int cols = sectionAppCount % numAppsPerRow;
-
- // Ensure that we do not merge across scripts, currently we only allow for english and
- // native scripts so we can test if both can just be ascii encoded
- boolean isCrossScript = false;
- if (section.firstAppItem != null && withSection.firstAppItem != null) {
- isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) !=
- mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName);
- }
- return (0 < cols && cols < mMinAppsPerRow) &&
- rows < mMinRowsInMergedSection &&
- mergeCount < mMaxAllowableMerges &&
- !isCrossScript;
- }
-}
-
/**
* The all apps view container.
*/
@@ -139,9 +70,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
public static final int GRID_THEME_LIGHT = 1;
public static final int GRID_THEME_DARK = 2;
- private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
- private static final int MAX_NUM_MERGES_PHONE = 1;
-
@Thunk Launcher mLauncher;
@Thunk AlphabeticalAppsList mApps;
private AllAppsGridAdapter mAdapter;
@@ -204,15 +132,36 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
Selection.setSelection(mSearchQueryBuilder, 0);
}
+ public int getNumPredictedAppsPerRow() {
+ return mNumPredictedAppsPerRow;
+ }
+
+ /**
+ * Sets the current set of predicted apps by component.
+ * Only usable when custom predicted apps are disabled.
+ */
+ public void setPredictedAppComponents(List<ComponentKey> apps) {
+ mApps.setPredictedAppComponents(apps);
+ updateScrubber();
+ }
+
/**
- * Sets the current set of predicted apps.
+ * Sets the current set of predicted apps by info.
+ * Only usable when custom predicated apps are enabled.
*/
- public void setPredictedApps(List<ComponentKey> apps) {
+ public void setPredictedApps(List<AppInfo> apps) {
mApps.setPredictedApps(apps);
updateScrubber();
}
/**
+ * Set whether the predicted apps row will have a customized selection of apps.
+ */
+ public void setCustomPredictedAppsEnabled(boolean enabled) {
+ mApps.mCustomPredictedAppsEnabled = enabled;
+ }
+
+ /**
* Sets the current set of apps.
*/
public void setApps(List<AppInfo> apps) {
@@ -262,6 +211,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
return mApps.getApps();
}
+ public int getSectionStrategy() {
+ return mSectionStrategy;
+ }
+
private void updateSectionStrategy() {
Context context = getContext();
Resources res = context.getResources();
@@ -415,17 +368,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mNumAppsPerRow = grid.allAppsNumCols;
mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
- // If there is a start margin to draw section names, determine how we are going to merge
- // app sections
- boolean mergeSectionsFully = mSectionStrategy == SECTION_STRATEGY_GRID;
- AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ?
- new FullMergeAlgorithm() :
- new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
- MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
-
mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
- mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
+
+ boolean mergeSections = mSectionStrategy == SECTION_STRATEGY_GRID;
+ mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeSections);
+
+ mLauncher.getRemoteFolderManager().onMeasureDrawer(mNumPredictedAppsPerRow);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index a736f7770..3065fb428 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -37,9 +37,12 @@ import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar.FastScrollFocusApplicator;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar.FastScrollFocusable;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.RemoteFolderManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.util.Thunk;
@@ -68,6 +71,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
public static final int SEARCH_MARKET_DIVIDER_VIEW_TYPE = 4;
// The message to continue to a market search when there are no filtered results
public static final int SEARCH_MARKET_VIEW_TYPE = 5;
+ // Section header for customized predicated apps.
+ public static final int CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE = 6;
+ // Additional spacing between predicted apps and regular apps.
+ public static final int CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE = 7;
private boolean mIconsDimmed = false;
@@ -185,36 +192,34 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mPredictedAppsDividerPaint);
hasDrawnPredictedAppsDivider = true;
- } else if (showSectionNames && shouldDrawItemSection(holder, i, items)) {
- // At this point, we only draw sections for each section break;
+ // Only customized predicted apps will draw a section name.
+ if (!mApps.mCustomPredictedAppsEnabled) continue;
+ }
+
+ if (showSectionNames && shouldDrawItemSection(holder, items)) {
+ // Draw the section name for the first visible item
int viewTopOffset = (2 * child.getPaddingTop());
int pos = holder.getPosition();
AlphabeticalAppsList.AdapterItem item = items.get(pos);
AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo;
-
- // Draw all the sections for this index
String lastSectionName = item.sectionName;
- for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) {
- AlphabeticalAppsList.AdapterItem nextItem = items.get(pos);
- String sectionName = nextItem.sectionName;
- if (nextItem.sectionInfo != sectionInfo) {
- break;
- }
- if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) {
- continue;
- }
+ // Find the section name bounds
+ PointF sectionBounds = getAndCacheSectionBounds(lastSectionName);
- // Find the section name bounds
- PointF sectionBounds = getAndCacheSectionBounds(sectionName);
+ // Calculate where to draw the section
+ int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
+ int x = mIsRtl ?
+ parent.getWidth() - mBackgroundPadding.left - mSectionNamesMargin :
+ mBackgroundPadding.left;
+ x += (int) ((mSectionNamesMargin - sectionBounds.x) / 2f);
- // Calculate where to draw the section
- int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
- int x = mIsRtl ?
- parent.getWidth() - mBackgroundPadding.left - mSectionNamesMargin :
- mBackgroundPadding.left;
- x += (int) ((mSectionNamesMargin - sectionBounds.x) / 2f);
- int y = child.getTop() + sectionBaseline;
+ int y;
+ boolean fixedToRow = false;
+ if (item.viewType == PREDICTION_ICON_VIEW_TYPE) {
+ y = child.getTop() - (int) mSectionTextPaint.getTextSize() / 2;
+ } else {
+ y = child.getTop() + sectionBaseline;
// Determine whether this is the last row with apps in that section, if
// so, then fix the section to the row allowing it to scroll past the
@@ -223,7 +228,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
int nextRowPos = Math.min(items.size() - 1,
pos + mAppsPerRow - (appIndexInSection % mAppsPerRow));
AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos);
- boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName);
+ fixedToRow = !lastSectionName.equals(nextRowItem.sectionName);
if (!fixedToRow) {
y = Math.max(sectionBaseline, y);
}
@@ -233,22 +238,21 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) {
y += lastSectionTop - y + lastSectionHeight;
}
+ }
- // Draw the section header
- if (FADE_OUT_SECTIONS) {
- int alpha = 255;
- if (fixedToRow) {
- alpha = Math.min(255,
- (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
- }
- mSectionTextPaint.setAlpha(alpha);
+ // Draw the section header
+ if (FADE_OUT_SECTIONS) {
+ int alpha = 255;
+ if (fixedToRow) {
+ alpha = Math.min(255,
+ (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
}
- c.drawText(sectionName, x, y, mSectionTextPaint);
-
- lastSectionTop = y;
- lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset);
- lastSectionName = sectionName;
+ mSectionTextPaint.setAlpha(alpha);
}
+ c.drawText(lastSectionName, x, y, mSectionTextPaint);
+
+ lastSectionTop = y;
+ lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset);
i += (sectionInfo.numApps - item.sectionAppIndex);
}
}
@@ -308,21 +312,22 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
/**
* Returns whether to draw the section for the given child.
*/
- private boolean shouldDrawItemSection(ViewHolder holder, int childIndex,
+ private boolean shouldDrawItemSection(ViewHolder holder,
List<AlphabeticalAppsList.AdapterItem> items) {
int pos = holder.getPosition();
AlphabeticalAppsList.AdapterItem item = items.get(pos);
// Ensure it's an icon
- if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+ if (item.viewType != ICON_VIEW_TYPE && item.viewType != PREDICTION_ICON_VIEW_TYPE) {
return false;
}
- // Draw the section header for the first item in each section
- return (childIndex == 0) ||
- (items.get(pos - 1).viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE);
+
+ return true;
}
}
+ private final RemoteFolderManager mRemoteFolderManager;
+
private Launcher mLauncher;
private LayoutInflater mLayoutInflater;
@Thunk AlphabeticalAppsList mApps;
@@ -359,6 +364,9 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
private int mAllAppsTextColor;
+ private int mCustomPredictedAppsHeaderHeight;
+ private int mCustomPredictedAppsFooterHeight;
+
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps,
View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
View.OnLongClickListener iconLongClickListener) {
@@ -399,8 +407,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mPredictedAppsDividerPaint.setColor(0x1E000000);
mPredictedAppsDividerPaint.setAntiAlias(true);
mPredictionBarDividerOffset =
- (-res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_bottom_padding) +
- res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding)) / 2;
+ res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_divider_offset);
// Resolve the market app handling additional searches
PackageManager pm = launcher.getPackageManager();
@@ -409,6 +416,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
if (marketInfo != null) {
mMarketAppName = marketInfo.loadLabel(pm).toString();
}
+
+ mRemoteFolderManager = launcher.getRemoteFolderManager();
}
/**
@@ -494,6 +503,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
.getLongPressTimeout());
icon.setFocusable(true);
+ FastScrollFocusApplicator.createApplicator(icon,
+ FastScrollFocusable.FAST_SCROLL_FOCUS_DIMMABLE |
+ FastScrollFocusable.FAST_SCROLL_FOCUS_SCALABLE);
+
return new ViewHolder(icon);
}
case PREDICTION_ICON_VIEW_TYPE: {
@@ -508,7 +521,14 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
.getLongPressTimeout());
icon.setFocusable(true);
- return new ViewHolder(icon);
+ FastScrollFocusApplicator.createApplicator(icon,
+ FastScrollFocusable.FAST_SCROLL_FOCUS_DIMMABLE |
+ FastScrollFocusable.FAST_SCROLL_FOCUS_SCALABLE);
+
+ ViewHolder holder = new ViewHolder(icon);
+ mRemoteFolderManager.onCreateViewHolder(holder, viewType);
+
+ return holder;
}
case EMPTY_SEARCH_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
@@ -526,6 +546,22 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
}
});
return new ViewHolder(searchMarketView);
+ case CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE: {
+ View v = mLayoutInflater.inflate(
+ R.layout.custom_predicted_apps_header, parent, false);
+ FastScrollFocusApplicator.createApplicator(v,
+ FastScrollFocusable.FAST_SCROLL_FOCUS_DIMMABLE);
+ ViewHolder holder = new ViewHolder(v);
+ mRemoteFolderManager.onCreateViewHolder(holder, viewType);
+ return holder;
+ }
+ case CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE: {
+ View v = mLayoutInflater.inflate(R.layout.custom_predicted_apps_footer,
+ parent, false);
+ ViewHolder holder = new ViewHolder(v);
+ mRemoteFolderManager.onCreateViewHolder(holder, viewType);
+ return holder;
+ }
default:
throw new RuntimeException("Unexpected view type");
}
@@ -545,7 +581,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
icon.setTextVisibility(!hideIconLabels);
}
icon.applyFromApplicationInfo(info);
- icon.setFastScrollDimmed(mIconsDimmed, !mIconsDimmed);
+ FastScrollFocusApplicator.setFastScrollDimmed(icon, mIconsDimmed, !mIconsDimmed);
+ FastScrollFocusApplicator.setFastScrollFocused(icon, false, !mIconsDimmed);
break;
}
case PREDICTION_ICON_VIEW_TYPE: {
@@ -556,6 +593,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
icon.setTextVisibility(!hideIconLabels);
}
icon.applyFromApplicationInfo(info);
+ FastScrollFocusApplicator.setFastScrollDimmed(icon, mIconsDimmed, !mIconsDimmed);
+ FastScrollFocusApplicator.setFastScrollFocused(icon, false, !mIconsDimmed);
+
+ mRemoteFolderManager.onBindViewHolder(holder, info);
break;
}
case EMPTY_SEARCH_VIEW_TYPE:
@@ -576,9 +617,32 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
searchView.setVisibility(View.GONE);
}
break;
+ case CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE: {
+ TextView title = (TextView) holder.mContent.findViewById(R.id.title);
+ title.setTextColor(mAllAppsTextColor);
+ FastScrollFocusApplicator.setFastScrollDimmed(holder.mContent, mIconsDimmed, !mIconsDimmed);
+ FastScrollFocusApplicator.setFastScrollFocused(holder.mContent, false, !mIconsDimmed);
+
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) holder.mContent.getLayoutParams();
+ mCustomPredictedAppsHeaderHeight = holder.mContent.getHeight() +
+ lp.topMargin + lp.bottomMargin;
+ break;
+ }
+ case CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE:
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) holder.mContent.getLayoutParams();
+ mCustomPredictedAppsFooterHeight = holder.mContent.getHeight() +
+ lp.topMargin + lp.bottomMargin;
}
}
+ public int getCustomPredictedAppsOffset(int rowIndex) {
+ int offset = mCustomPredictedAppsHeaderHeight;
+ if (rowIndex > 0) offset += mCustomPredictedAppsFooterHeight;
+ return offset;
+ }
+
@Override
public int getItemCount() {
return mApps.getAdapterItems().size();
@@ -602,11 +666,15 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
int sectionTextColorId = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
R.color.all_apps_grid_section_text_color_dark :
R.color.all_apps_grid_section_text_color;
- mSectionTextPaint.setColor(mLauncher.getResources().getColor(sectionTextColorId));
- Resources res = mLauncher.getResources();
+ mSectionTextPaint.setColor(mLauncher.getColor(sectionTextColorId));
+
mAllAppsTextColor = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
- res.getColor(R.color.quantum_panel_text_color_dark) :
- res.getColor(R.color.quantum_panel_text_color);
+ mLauncher.getColor(R.color.quantum_panel_text_color_dark) :
+ mLauncher.getColor(R.color.quantum_panel_text_color);
+
+ int mPredictedAppsDividerColorId = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
+ R.color.drawer_divider_dark : R.color.drawer_divider_light;
+ mPredictedAppsDividerPaint.setColor(mLauncher.getColor(mPredictedAppsDividerColorId));
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 6c853a037..aaecf7a60 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -26,8 +26,8 @@ import android.util.AttributeSet;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.BaseRecyclerViewFastScrollBar;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar.FastScrollFocusApplicator;
import com.android.launcher3.R;
import com.android.launcher3.Stats;
import com.android.launcher3.Utilities;
@@ -52,11 +52,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private int mNumAppsPerRow;
private int mSectionStrategy = AllAppsContainerView.SECTION_STRATEGY_RAGGED;
- private boolean mFocusSection = false;
-
- @Thunk BaseRecyclerViewFastScrollBar.FastScrollFocusableView mLastFastScrollFocusedView;
- @Thunk ArrayList<BaseRecyclerViewFastScrollBar.FastScrollFocusableView>
- mLastFastScrollFocusedViews = new ArrayList();
+ @Thunk ArrayList<View> mLastFastScrollFocusedViews = new ArrayList();
@Thunk int mPrevFastScrollFocusedPosition;
@Thunk AlphabeticalAppsList.SectionInfo mPrevFastScrollFocusedSection;
@Thunk int mFastScrollFrameIndex;
@@ -66,6 +62,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private final int mScrollBarMode = FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW;
private ScrollPositionState mScrollPosState = new ScrollPositionState();
+ private boolean mFastScrollDragging;
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
@@ -92,6 +89,25 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
+
+ addOnScrollListener(new FocusScrollListener());
+ }
+
+ private class FocusScrollListener extends OnScrollListener {
+ public FocusScrollListener() { }
+
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ switch (newState) {
+ case SCROLL_STATE_IDLE:
+ // Don't change anything if we've stopped touching the scroll bar.
+ if (mFastScrollDragging) {
+ // Animation completed, set the fast scroll state on the target views
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, true);
+ }
+ }
+ }
}
/**
@@ -115,11 +131,12 @@ public class AllAppsRecyclerView extends BaseRecyclerView
pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE, 1);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE, 1);
}
public void setSectionStrategy(int sectionStrategy) {
mSectionStrategy = sectionStrategy;
- mFocusSection = mSectionStrategy == AllAppsContainerView.SECTION_STRATEGY_RAGGED;
}
/**
@@ -241,49 +258,30 @@ public class AllAppsRecyclerView extends BaseRecyclerView
throw new RuntimeException("Unexpected scroll bar mode");
}
- // Map the touch position back to the scroll of the recycler view
- getCurScrollState(mScrollPosState);
- int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight);
- LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
- if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
- layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
+ // Reset the last focused section
+ if (mPrevFastScrollFocusedSection != lastInfo.sectionInfo) {
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, true, true);
+ clearSectionFocusedItems();
}
- if (mPrevFastScrollFocusedPosition != lastInfo.fastScrollToItem.position) {
- if (mFocusSection) {
- setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, true, true);
- } else if (mLastFastScrollFocusedView != null){
- mLastFastScrollFocusedView.setFastScrollDimmed(true, true);
- }
- mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position;
- mPrevFastScrollFocusedSection =
- getSectionInfoForPosition(lastInfo.fastScrollToItem.position);
- // Reset the last focused section
- if (mFocusSection) {
- clearSectionFocusedItems();
- } else if (mLastFastScrollFocusedView != null) {
- mLastFastScrollFocusedView.setFastScrollFocused(false, true);
- mLastFastScrollFocusedView = null;
- }
+ mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position;
+ mPrevFastScrollFocusedSection = lastInfo.sectionInfo;
- if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) {
- smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState);
- } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
- if (mFocusSection) {
- setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
- } else {
- final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
- if (vh != null &&
- vh.itemView instanceof
- BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
- mLastFastScrollFocusedView =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- mLastFastScrollFocusedView.setFastScrollFocused(true, true);
- }
- }
- } else {
- throw new RuntimeException("Unexpected fast scroll mode");
- }
+ getCurScrollState(mScrollPosState);
+ if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) {
+ smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState);
+
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, true);
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
+ } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
+ // Map the touch position back to the scroll of the recycler view
+ int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight);
+ LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+ layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
+
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
+ } else {
+ throw new RuntimeException("Unexpected fast scroll mode");
}
return lastInfo.sectionName;
}
@@ -291,13 +289,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView
@Override
public void onFastScrollCompleted() {
super.onFastScrollCompleted();
- // Reset and clean up the last focused view
- if (mFocusSection) {
- clearSectionFocusedItems();
- } else if (mLastFastScrollFocusedView != null) {
- mLastFastScrollFocusedView.setFastScrollFocused(false, true);
- mLastFastScrollFocusedView = null;
- }
+
+ // Reset and clean up the last focused views
+ clearSectionFocusedItems();
mPrevFastScrollFocusedPosition = -1;
mPrevFastScrollFocusedSection = null;
}
@@ -338,8 +332,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
// Calculate the current scroll position, the scrollY of the recycler view accounts for the
// view padding, while the scrollBarY is drawn right up to the background padding (ignoring
// padding)
- int scrollY = getPaddingTop() +
- (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset;
+ int scrollY = getCurrentScroll(mScrollPosState);
int scrollBarY = mBackgroundPadding.top +
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
@@ -421,29 +414,16 @@ public class AllAppsRecyclerView extends BaseRecyclerView
return new String[0];
}
- private AlphabeticalAppsList.SectionInfo getSectionInfoForPosition(int position) {
- List<AlphabeticalAppsList.SectionInfo> sections =
- mApps.getSections();
- for (AlphabeticalAppsList.SectionInfo section : sections) {
- if (section.firstAppItem.position == position) {
- return section;
- }
- }
- return null;
- }
-
private void setSectionFastScrollFocused(int position) {
if (mPrevFastScrollFocusedSection != null) {
- for (int i = 0; i < mPrevFastScrollFocusedSection.numApps; i++) {
+ int size = mPrevFastScrollFocusedSection.numApps +
+ mPrevFastScrollFocusedSection.numOtherViews;
+ for (int i = 0; i < size; i++) {
int sectionPosition = position+i;
final ViewHolder vh = findViewHolderForAdapterPosition(sectionPosition);
- if (vh != null &&
- vh.itemView instanceof
- BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
- final BaseRecyclerViewFastScrollBar.FastScrollFocusableView view =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- view.setFastScrollFocused(true, true);
- mLastFastScrollFocusedViews.add(view);
+ if (vh != null) {
+ FastScrollFocusApplicator.setFastScrollFocused(vh.itemView, true, true);
+ mLastFastScrollFocusedViews.add(vh.itemView);
}
}
}
@@ -451,15 +431,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private void setSectionFastScrollDimmed(int position, boolean dimmed, boolean animate) {
if (mPrevFastScrollFocusedSection != null) {
- for (int i = 0; i < mPrevFastScrollFocusedSection.numApps; i++) {
+ int size = mPrevFastScrollFocusedSection.numApps +
+ mPrevFastScrollFocusedSection.numOtherViews;
+ for (int i = 0; i < size; i++) {
int sectionPosition = position+i;
final ViewHolder vh = findViewHolderForAdapterPosition(sectionPosition);
- if (vh != null &&
- vh.itemView instanceof
- BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
- final BaseRecyclerViewFastScrollBar.FastScrollFocusableView view =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- view.setFastScrollDimmed(dimmed, animate);
+ if (vh != null) {
+ FastScrollFocusApplicator.setFastScrollDimmed(vh.itemView, dimmed, animate);
}
}
}
@@ -468,9 +446,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private void clearSectionFocusedItems() {
final int N = mLastFastScrollFocusedViews.size();
for (int i = 0; i < N; i++) {
- BaseRecyclerViewFastScrollBar.FastScrollFocusableView view =
- mLastFastScrollFocusedViews.get(i);
- view.setFastScrollFocused(false, true);
+ View view = mLastFastScrollFocusedViews.get(i);
+ FastScrollFocusApplicator.setFastScrollFocused(view, false, true);
}
mLastFastScrollFocusedViews.clear();
}
@@ -478,6 +455,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
@Override
public void setFastScrollDragging(boolean dragging) {
((AllAppsGridAdapter) getAdapter()).setIconsDimmed(dragging);
+ mFastScrollDragging = dragging;
}
/**
@@ -488,28 +466,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView
@Override
public void run() {
if (mFastScrollFrameIndex < mFastScrollFrames.length) {
- if (mFocusSection) {
- setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, true);
- }
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, true);
scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]);
mFastScrollFrameIndex++;
postOnAnimation(mSmoothSnapNextFrameRunnable);
} else {
- if (mFocusSection) {
- setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
- } else {
- // Animation completed, set the fast scroll state on the target view
- final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
- if (vh != null &&
- vh.itemView instanceof
- BaseRecyclerViewFastScrollBar.FastScrollFocusableView &&
- mLastFastScrollFocusedView != vh.itemView) {
- mLastFastScrollFocusedView =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- mLastFastScrollFocusedView.setFastScrollFocused(true, true);
- mLastFastScrollFocusedView.setFastScrollDimmed(false, true);
- }
- }
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, false);
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
}
}
};
@@ -523,8 +486,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
// Calculate the full animation from the current scroll position to the final scroll
// position, and then run the animation for the duration.
- int curScrollY = getPaddingTop() +
- (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
+ int curScrollY = getCurrentScroll(scrollPosState);
int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight);
int numFrames = mFastScrollFrames.length;
for (int i = 0; i < numFrames; i++) {
@@ -558,7 +520,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
stateOut.rowIndex = item.rowIndex;
- stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
+ stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child) -
+ getAdditionalScrollHeight(stateOut.rowIndex);
stateOut.rowHeight = child.getHeight();
break;
}
@@ -566,6 +529,16 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
}
+ @Override
+ protected int getAvailableScrollHeight(int rowCount, int rowHeight) {
+ return super.getAvailableScrollHeight(rowCount, rowHeight) +
+ getAdditionalScrollHeight(mApps.getAdapterItems().size());
+ }
+
+ private int getAdditionalScrollHeight(int rowIndex) {
+ return ((AllAppsGridAdapter) getAdapter()).getCustomPredictedAppsOffset(rowIndex);
+ }
+
/**
* Returns the scrollY for the given position in the adapter.
*/
@@ -574,6 +547,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
int offset = item.rowIndex > 0 ? getPaddingTop() : 0;
+ offset += ((AllAppsGridAdapter) getAdapter()).
+ getCustomPredictedAppsOffset(item.rowIndex);
return offset + item.rowIndex * rowHeight;
} else {
return 0;
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 98d747936..9d30f81ee 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.allapps;
-import android.content.ComponentName;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@@ -32,6 +31,7 @@ import cyanogenmod.providers.CMSettings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -49,6 +49,9 @@ public class AlphabeticalAppsList {
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0;
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
+ private static final String CUSTOM_PREDICTIONS_SCRUBBER = "★";
+ private static final String CUSTOM_PREDICTIONS_HEADER = "☆";
+
private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
/**
@@ -57,6 +60,8 @@ public class AlphabeticalAppsList {
public static class SectionInfo {
// The number of applications in this section
public int numApps;
+ // The number of drawn (non-app) adapter items in this section.
+ public int numOtherViews;
// The section break AdapterItem for this section
public AdapterItem sectionBreakItem;
// The first app AdapterItem for this section
@@ -64,19 +69,21 @@ public class AlphabeticalAppsList {
}
/**
- * Info about a fast scroller section, depending if sections are merged, the fast scroller
- * sections will not be the same set as the section headers.
+ * Info about a fast scroller section.
*/
public static class FastScrollSectionInfo {
// The section name
public String sectionName;
+ // Info for this section
+ public SectionInfo sectionInfo;
// The AdapterItem to scroll to for this section
public AdapterItem fastScrollToItem;
// The touch fraction that should map to this fast scroll section info
public float touchFraction;
- public FastScrollSectionInfo(String sectionName) {
+ public FastScrollSectionInfo(String sectionName, SectionInfo sectionInfo) {
this.sectionName = sectionName;
+ this.sectionInfo = sectionInfo;
}
}
@@ -158,14 +165,20 @@ public class AlphabeticalAppsList {
item.position = pos;
return item;
}
- }
- /**
- * Common interface for different merging strategies.
- */
- public interface MergeAlgorithm {
- boolean continueMerging(SectionInfo section, SectionInfo withSection,
- int sectionAppCount, int numAppsPerRow, int mergeCount);
+ public static AdapterItem asCustomPredictedAppsHeader(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE;
+ item.position = pos;
+ return item;
+ }
+
+ public static AdapterItem asPredictedAppsSpacer(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE;
+ item.position = pos;
+ return item;
+ }
}
private Launcher mLauncher;
@@ -180,7 +193,7 @@ public class AlphabeticalAppsList {
private List<AdapterItem> mAdapterItems = new ArrayList<>();
// The set of sections for the apps with the current filter
private List<SectionInfo> mSections = new ArrayList<>();
- // The set of sections that we allow fast-scrolling to (includes non-merged sections)
+ // The set of sections that we allow fast-scrolling to
private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
// The set of predicted app component names
private List<ComponentKey> mPredictedAppComponents = new ArrayList<>();
@@ -192,11 +205,13 @@ public class AlphabeticalAppsList {
private RecyclerView.Adapter mAdapter;
private AlphabeticIndexCompat mIndexer;
private AppNameComparator mAppNameComparator;
- private MergeAlgorithm mMergeAlgorithm;
+ private boolean mMergeSections;
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
private int mNumAppRowsInAdapter;
+ boolean mCustomPredictedAppsEnabled;
+
public AlphabeticalAppsList(Context context) {
mLauncher = (Launcher) context;
mIndexer = new AlphabeticIndexCompat(context);
@@ -207,10 +222,10 @@ public class AlphabeticalAppsList {
* Sets the number of apps per row.
*/
public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow,
- MergeAlgorithm mergeAlgorithm) {
+ boolean mergeSections) {
mNumAppsPerRow = numAppsPerRow;
mNumPredictedAppsPerRow = numPredictedAppsPerRow;
- mMergeAlgorithm = mergeAlgorithm;
+ mMergeSections = mergeSections;
updateAdapterItems();
}
@@ -292,13 +307,33 @@ public class AlphabeticalAppsList {
* Sets the current set of predicted apps. Since this can be called before we get the full set
* of applications, we should merge the results only in onAppsUpdated() which is idempotent.
*/
- public void setPredictedApps(List<ComponentKey> apps) {
+ public void setPredictedAppComponents(List<ComponentKey> apps) {
+ if (!mCustomPredictedAppsEnabled) {
+ throw new IllegalStateException("Unable to set predicted app components when adapter " +
+ "is set to accept a custom predicted apps list.");
+ }
+
mPredictedAppComponents.clear();
mPredictedAppComponents.addAll(apps);
onAppsUpdated();
}
/**
+ * Sets the current set of predicted apps. This uses the info directly, so we do not
+ * merge data in {@link #onAppsUpdated()}, but go directly to {@link #updateAdapterItems()}.
+ */
+ public void setPredictedApps(List<AppInfo> apps) {
+ if (!mCustomPredictedAppsEnabled) {
+ throw new IllegalStateException("Unable to set predicted apps directly when adapter " +
+ "is not set to accept a custom predicted apps list.");
+ }
+
+ mPredictedApps.clear();
+ mPredictedApps.addAll(apps);
+ updateAdapterItems();
+ }
+
+ /**
* Sets the current set of apps.
*/
public void setApps(List<AppInfo> apps) {
@@ -415,27 +450,48 @@ public class AlphabeticalAppsList {
}
// Process the predicted app components
- mPredictedApps.clear();
- if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
- for (ComponentKey ck : mPredictedAppComponents) {
- AppInfo info = mComponentToAppMap.get(ck);
- if (info != null) {
- mPredictedApps.add(info);
- } else {
- if (LauncherAppState.isDogfoodBuild()) {
- Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
+ boolean hasPredictedApps;
+
+ // We haven't measured yet. Skip this for now. We will set properly after measure.
+ if (mNumPredictedAppsPerRow == 0) {
+ hasPredictedApps = false;
+ } else if (mCustomPredictedAppsEnabled) {
+ hasPredictedApps = !mPredictedApps.isEmpty();
+ } else {
+ mPredictedApps.clear();
+ hasPredictedApps = mPredictedAppComponents != null &&
+ !mPredictedAppComponents.isEmpty();
+ }
+
+ if (hasPredictedApps && !hasFilter()) {
+ if (!mCustomPredictedAppsEnabled) {
+ for (ComponentKey ck : mPredictedAppComponents) {
+ AppInfo info = mComponentToAppMap.get(ck);
+ if (info != null) {
+ mPredictedApps.add(info);
+ } else {
+ if (LauncherAppState.isDogfoodBuild()) {
+ Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
+ }
+ }
+ // Stop at the number of predicted apps
+ if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
+ break;
}
}
- // Stop at the number of predicted apps
- if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
- break;
+ } else {
+ // Shrink to column count.
+ if (mPredictedApps.size() > mNumPredictedAppsPerRow) {
+ mPredictedApps.subList(mNumAppsPerRow, mPredictedApps.size()).clear();
}
}
if (!mPredictedApps.isEmpty()) {
// Add a section for the predictions
lastSectionInfo = new SectionInfo();
- lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
+ String text = mCustomPredictedAppsEnabled ? CUSTOM_PREDICTIONS_SCRUBBER : " ";
+ lastFastScrollerSectionInfo =
+ new FastScrollSectionInfo(text, lastSectionInfo);
AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
mSections.add(lastSectionInfo);
mFastScrollerSections.add(lastFastScrollerSectionInfo);
@@ -443,8 +499,9 @@ public class AlphabeticalAppsList {
// Add the predicted app items
for (AppInfo info : mPredictedApps) {
+ text = mCustomPredictedAppsEnabled ? CUSTOM_PREDICTIONS_HEADER : " ";
AdapterItem appItem = AdapterItem.asPredictedApp(position++, lastSectionInfo,
- "", lastSectionInfo.numApps++, info, appIndex++);
+ text, lastSectionInfo.numApps++, info, appIndex++);
if (lastSectionInfo.firstAppItem == null) {
lastSectionInfo.firstAppItem = appItem;
lastFastScrollerSectionInfo.fastScrollToItem = appItem;
@@ -452,6 +509,11 @@ public class AlphabeticalAppsList {
mAdapterItems.add(appItem);
mFilteredApps.add(info);
}
+
+ if (mCustomPredictedAppsEnabled) {
+ position = mLauncher.getRemoteFolderManager().onUpdateAdapterItems(
+ mAdapterItems, lastFastScrollerSectionInfo, lastSectionInfo, position);
+ }
}
}
@@ -470,7 +532,7 @@ public class AlphabeticalAppsList {
if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
lastSectionName = sectionName;
lastSectionInfo = new SectionInfo();
- lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
+ lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, lastSectionInfo);
mSections.add(lastSectionInfo);
mFastScrollerSections.add(lastFastScrollerSectionInfo);
@@ -502,7 +564,7 @@ public class AlphabeticalAppsList {
mAdapterItems.add(AdapterItem.asMarketSearch(position++));
}
- // Merge multiple sections together as requested by the merge strategy for this device
+ // Merge multiple sections together as needed.
mergeSections();
if (mNumAppsPerRow != 0) {
@@ -536,7 +598,8 @@ public class AlphabeticalAppsList {
for (FastScrollSectionInfo info : mFastScrollerSections) {
AdapterItem item = info.fastScrollToItem;
if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
- item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE &&
+ item.viewType != AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE) {
info.touchFraction = 0f;
continue;
}
@@ -551,7 +614,8 @@ public class AlphabeticalAppsList {
for (FastScrollSectionInfo info : mFastScrollerSections) {
AdapterItem item = info.fastScrollToItem;
if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
- item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE &&
+ item.viewType != AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE) {
info.touchFraction = 0f;
continue;
}
@@ -588,52 +652,18 @@ public class AlphabeticalAppsList {
*/
private void mergeSections() {
// Ignore merging until we have an algorithm and a valid row size
- if (mMergeAlgorithm == null || mNumAppsPerRow == 0) {
+ if (!mMergeSections || mNumAppsPerRow == 0 || hasFilter()) {
return;
}
- // Go through each section and try and merge some of the sections
- if (!hasFilter()) {
- int sectionAppCount = 0;
- for (int i = 0; i < mSections.size() - 1; i++) {
- SectionInfo section = mSections.get(i);
- sectionAppCount = section.numApps;
- int mergeCount = 1;
-
- // Merge rows based on the current strategy
- while (i < (mSections.size() - 1) &&
- mMergeAlgorithm.continueMerging(section, mSections.get(i + 1),
- sectionAppCount, mNumAppsPerRow, mergeCount)) {
- SectionInfo nextSection = mSections.remove(i + 1);
-
- // Remove the next section break
- mAdapterItems.remove(nextSection.sectionBreakItem);
- int pos = mAdapterItems.indexOf(section.firstAppItem);
-
- // Point the section for these new apps to the merged section
- int nextPos = pos + section.numApps;
- for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) {
- AdapterItem item = mAdapterItems.get(j);
- item.sectionInfo = section;
- item.sectionAppIndex += section.numApps;
- }
-
- // Update the following adapter items of the removed section item
- pos = mAdapterItems.indexOf(nextSection.firstAppItem);
- for (int j = pos; j < mAdapterItems.size(); j++) {
- AdapterItem item = mAdapterItems.get(j);
- item.position--;
- }
- section.numApps += nextSection.numApps;
- sectionAppCount += nextSection.numApps;
-
- if (DEBUG) {
- Log.d(TAG, "Merging: " + nextSection.firstAppItem.sectionName +
- " to " + section.firstAppItem.sectionName +
- " mergedNumRows: " + (sectionAppCount / mNumAppsPerRow));
- }
- mergeCount++;
- }
+ Iterator<AdapterItem> iter = mAdapterItems.iterator();
+ int positionShift = 0;
+ while (iter.hasNext()) {
+ AdapterItem item = iter.next();
+ item.position -= positionShift;
+ if (item.viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE) {
+ iter.remove();
+ positionShift++;
}
}
}
diff --git a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java
index 31214e3d0..765243de1 100644
--- a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java
+++ b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java
@@ -123,6 +123,12 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter {
case 3:
updateDynamicGridSizeSettingsItem(v);
break;
+ case 4:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER,
+ R.bool.preferences_interface_homescreen_remote_folder_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
default:
hideStates(stateView, settingSwitch);
}
@@ -270,6 +276,12 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter {
case 3:
mLauncher.onClickDynamicGridSizeButton();
break;
+ case 4:
+ boolean newValue = onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER,
+ R.bool.preferences_interface_homescreen_remote_folder_default);
+ mLauncher.getRemoteFolderManager().onSettingChanged(newValue);
+ break;
}
break;
case OverviewSettingsPanel.DRAWER_SETTINGS_POSITION:
@@ -362,7 +374,7 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter {
mContext.sendBroadcast(intent);
}
- private void onSettingsBooleanChanged(View v, String key, int res) {
+ private boolean onSettingsBooleanChanged(View v, String key, int res) {
boolean current = SettingsProvider.getBoolean(
mContext, key, res);
@@ -379,6 +391,8 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter {
LauncherSettings.Settings.CONTENT_URI,
LauncherSettings.Settings.METHOD_SET_BOOLEAN,
key, extras);
+
+ return !current;
}
private void onIconLabelsBooleanChanged(View v, String key, int res) {
diff --git a/src/com/android/launcher3/settings/SettingsProvider.java b/src/com/android/launcher3/settings/SettingsProvider.java
index 4357a451c..c96b8f53a 100644
--- a/src/com/android/launcher3/settings/SettingsProvider.java
+++ b/src/com/android/launcher3/settings/SettingsProvider.java
@@ -27,6 +27,7 @@ public final class SettingsProvider {
public static final String SETTINGS_UI_HOMESCREEN_SEARCH = "ui_homescreen_search";
public static final String SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS = "ui_homescreen_general_hide_icon_labels";
public static final String SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL = "ui_homescreen_scrolling_wallpaper_scroll";
+ public static final String SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER = "ui_homescreen_remote_folder";
public static final String SETTINGS_UI_DYNAMIC_GRID_SIZE = "ui_dynamic_grid_size";
public static final String SETTINGS_UI_HOMESCREEN_ROWS = "ui_homescreen_rows";
public static final String SETTINGS_UI_HOMESCREEN_COLUMNS = "ui_homescreen_columns";
diff --git a/src/com/android/launcher3/stats/LauncherStats.java b/src/com/android/launcher3/stats/LauncherStats.java
index e1dd1bbc6..5e8cb83d5 100644
--- a/src/com/android/launcher3/stats/LauncherStats.java
+++ b/src/com/android/launcher3/stats/LauncherStats.java
@@ -16,12 +16,12 @@
package com.android.launcher3.stats;
+import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
-import com.android.launcher3.LauncherApplication;
import com.android.launcher3.stats.internal.db.DatabaseHelper;
import com.android.launcher3.stats.internal.model.TrackingEvent;
@@ -41,6 +41,9 @@ public class LauncherStats {
public static final String ORIGIN_APPDRAWER = "appdrawer";
public static final String ORIGIN_TREB_LONGPRESS = "trebuchet_longpress";
public static final String ORIGIN_CHOOSER = "theme_chooser";
+ public static final String ORIGIN_SETTINGS = "settings";
+ public static final String ORIGIN_DRAG_DROP = "drag_drop";
+ public static final String ORIGIN_FOLDER = "folder";
private static void log(String msg) throws IllegalArgumentException {
if (TextUtils.isEmpty(msg)) {
@@ -101,17 +104,16 @@ public class LauncherStats {
private static LauncherStats sInstance = null;
// Members
- private static WriteHandlerThread sHandlerThread = new WriteHandlerThread();
+ private static WriteHandlerThread sHandlerThread;
private static WriteHandler sWriteHandler;
private static DatabaseHelper sDatabaseHelper;
- private LauncherApplication mApplication;
/**
* Send a message to the handler to store event data
*
* @param trackingEvent {@link TrackingEvent}
*/
- private void sendStoreEventMessage(TrackingEvent trackingEvent) {
+ protected void sendStoreEventMessage(TrackingEvent trackingEvent) {
log("Sending tracking event to handler: " + trackingEvent);
Message msg = new Message();
msg.what = MSG_STORE_EVENT;
@@ -134,32 +136,37 @@ public class LauncherStats {
}
/**
+ * Used only for overlay extensions
+ */
+ protected LauncherStats() { }
+
+ /**
* Constructor
*
- * @param application {@link LauncherApplication} not null!
+ * @param context {@link Context} not null!
* @throws IllegalArgumentException {@link IllegalArgumentException}
*/
- private LauncherStats(LauncherApplication application) throws IllegalArgumentException {
- if (application == null) {
- throw new IllegalArgumentException("'application' cannot be null!");
+ private LauncherStats(Context context) throws IllegalArgumentException {
+ if (context == null) {
+ throw new IllegalArgumentException("'context' cannot be null!");
}
- mApplication = application;
- sDatabaseHelper = new DatabaseHelper(application);
+ sDatabaseHelper = new DatabaseHelper(context);
+ sHandlerThread = new WriteHandlerThread();
sHandlerThread.start();
sWriteHandler = new WriteHandler();
}
/**
- * Creates a singleton instance of the stats utility
+ * Gets a singleton instance of the stats utility
*
- * @param application {@link LauncherApplication} not null!
+ * @param context {@link Context} not null!
* @return {@link LauncherStats}
* @throws IllegalArgumentException {@link IllegalArgumentException}
*/
- public static LauncherStats createInstance(LauncherApplication application)
+ public static LauncherStats getInstance(Context context)
throws IllegalArgumentException {
if (sInstance == null) {
- sInstance = new LauncherStats(application);
+ sInstance = new LauncherStats(context);
}
return sInstance;
}
diff --git a/src/com/android/launcher3/stats/internal/model/TrackingEvent.java b/src/com/android/launcher3/stats/internal/model/TrackingEvent.java
index 91a9017be..a44d3f148 100644
--- a/src/com/android/launcher3/stats/internal/model/TrackingEvent.java
+++ b/src/com/android/launcher3/stats/internal/model/TrackingEvent.java
@@ -51,6 +51,14 @@ public class TrackingEvent {
WALLPAPER_CHANGE,
HOMESCREEN_PAGE,
WIDGET,
+
+ // Remote folder specific
+ REMOTE_FOLDER_DISABLED,
+ REMOTE_FOLDER_OPENED,
+ REMOTE_FOLDER_INFO_OPENED,
+ REMOTE_APP_OPENED,
+ REMOTE_APP_INSTALLED,
+ REMOTE_SYNC_TIME
}
public static final String KEY_ORIGIN = TrackingBundle.KEY_METADATA_ORIGIN;