From b745afbdd75157c73d581b345118cdaff99e912d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 2 Mar 2015 11:51:23 -0800 Subject: [PATCH] Initial changes to break out AllApps into its own view. - Moves launcher state-transition code into its own class - Moves all-apps related code into a separate view/set of classes - Implements a basic list view for all apps Change-Id: I68f174aa9e1bf82c4e46ce9549c78a8dc4623f46 --- res/drawable/apps_list_bg.xml | 21 + res/layout-land/launcher.xml | 6 + res/layout-port/launcher.xml | 6 + res/layout-sw600dp/apps_view.xml | 34 + res/layout-sw720dp/launcher.xml | 6 + res/layout/apps_grid_row_icon_view.xml | 22 + res/layout/apps_grid_row_view.xml | 38 + res/layout/apps_list_reveal_view.xml | 25 + res/layout/apps_list_row_icon_view.xml | 28 + res/layout/apps_list_row_view.xml | 34 + res/layout/apps_list_view.xml | 30 + res/layout/apps_view.xml | 28 + res/values-sw600dp/dimens.xml | 3 + res/values/attrs.xml | 8 + res/values/dimens.xml | 3 + src/com/android/launcher3/AppsContainerView.java | 609 +++++++++++++++ .../android/launcher3/AppsCustomizePagedView.java | 219 +----- .../android/launcher3/AppsCustomizeTabHost.java | 44 +- src/com/android/launcher3/BubbleTextView.java | 101 ++- src/com/android/launcher3/DeviceProfile.java | 2 + src/com/android/launcher3/Folder.java | 4 +- src/com/android/launcher3/Launcher.java | 781 ++++--------------- src/com/android/launcher3/LauncherModel.java | 2 +- .../LauncherStateTransitionAnimation.java | 832 +++++++++++++++++++++ .../launcher3/PagedViewWithDraggableItems.java | 2 +- src/com/android/launcher3/WidgetPreviewLoader.java | 4 +- .../android/launcher3/WidgetsContainerView.java | 88 +++ src/com/android/launcher3/Workspace.java | 67 +- 28 files changed, 2112 insertions(+), 935 deletions(-) create mode 100644 res/drawable/apps_list_bg.xml create mode 100644 res/layout-sw600dp/apps_view.xml create mode 100644 res/layout/apps_grid_row_icon_view.xml create mode 100644 res/layout/apps_grid_row_view.xml create mode 100644 res/layout/apps_list_reveal_view.xml create mode 100644 res/layout/apps_list_row_icon_view.xml create mode 100644 res/layout/apps_list_row_view.xml create mode 100644 res/layout/apps_list_view.xml create mode 100644 res/layout/apps_view.xml create mode 100644 src/com/android/launcher3/AppsContainerView.java create mode 100644 src/com/android/launcher3/LauncherStateTransitionAnimation.java create mode 100644 src/com/android/launcher3/WidgetsContainerView.java diff --git a/res/drawable/apps_list_bg.xml b/res/drawable/apps_list_bg.xml new file mode 100644 index 000000000..61f1c083a --- /dev/null +++ b/res/drawable/apps_list_bg.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index 6f95bd506..b13984a26 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -62,6 +62,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" /> + + + + + + + + + \ No newline at end of file diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml index 960ccf330..a3d502cf4 100644 --- a/res/layout-sw720dp/launcher.xml +++ b/res/layout-sw720dp/launcher.xml @@ -71,6 +71,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" /> + + + + + diff --git a/res/layout/apps_grid_row_view.xml b/res/layout/apps_grid_row_view.xml new file mode 100644 index 000000000..bce43bc1b --- /dev/null +++ b/res/layout/apps_grid_row_view.xml @@ -0,0 +1,38 @@ + + + + + \ No newline at end of file diff --git a/res/layout/apps_list_reveal_view.xml b/res/layout/apps_list_reveal_view.xml new file mode 100644 index 000000000..4a26787c8 --- /dev/null +++ b/res/layout/apps_list_reveal_view.xml @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/res/layout/apps_list_row_icon_view.xml b/res/layout/apps_list_row_icon_view.xml new file mode 100644 index 000000000..607af9b0b --- /dev/null +++ b/res/layout/apps_list_row_icon_view.xml @@ -0,0 +1,28 @@ + + + + diff --git a/res/layout/apps_list_row_view.xml b/res/layout/apps_list_row_view.xml new file mode 100644 index 000000000..c4dcd0018 --- /dev/null +++ b/res/layout/apps_list_row_view.xml @@ -0,0 +1,34 @@ + + + + + \ No newline at end of file diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml new file mode 100644 index 000000000..b1b0f310b --- /dev/null +++ b/res/layout/apps_list_view.xml @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml new file mode 100644 index 000000000..19ad3d2c9 --- /dev/null +++ b/res/layout/apps_view.xml @@ -0,0 +1,28 @@ + + + + + + \ No newline at end of file diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 28679be2e..f7ad0c4cd 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -17,6 +17,9 @@ 64dp + + 76dp + 60dp 8dp diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 3331cdec4..4e7c59280 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -18,6 +18,14 @@ + + + + + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index d6fc508d1..013bd925b 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -46,6 +46,9 @@ 4dip 12dip + + 64dp + diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java new file mode 100644 index 000000000..cabacec3c --- /dev/null +++ b/src/com/android/launcher3/AppsContainerView.java @@ -0,0 +1,609 @@ +package com.android.launcher3; + +import android.content.ComponentName; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.SectionIndexer; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +/** + * Represents a row in the apps list view. + */ +class AppsRow { + int sectionId; + String sectionDescription; + List apps; + + public AppsRow(int sId, String sc, List ai) { + sectionId = sId; + sectionDescription = sc; + apps = ai; + } + + public AppsRow(int sId, List ai) { + sectionId = sId; + apps = ai; + } +} + +/** + * An interface to an algorithm that generates app rows. + */ +interface AppRowAlgorithm { + public List computeAppRows(List sortedApps, int appsPerRow); + public int getIconViewLayoutId(); + public int getRowViewLayoutId(); + public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info); +} + +/** + * Computes the rows in the apps list view. + */ +class SectionedAppsAlgorithm implements AppRowAlgorithm { + + @Override + public List computeAppRows(List sortedApps, int appsPerRow) { + List rows = new ArrayList<>(); + LinkedHashMap> sections = computeSectionedApps(sortedApps); + int sectionId = 0; + for (Map.Entry> sectionEntry : sections.entrySet()) { + String section = sectionEntry.getKey(); + List apps = sectionEntry.getValue(); + int numRows = (int) Math.ceil((float) apps.size() / appsPerRow); + for (int i = 0; i < numRows; i++) { + List appsInRow = new ArrayList<>(); + int offset = i * appsPerRow; + for (int j = 0; j < appsPerRow; j++) { + if (offset + j < apps.size()) { + appsInRow.add(apps.get(offset + j)); + } + } + if (i == 0) { + rows.add(new AppsRow(sectionId, section, appsInRow)); + } else { + rows.add(new AppsRow(sectionId, appsInRow)); + } + } + sectionId++; + } + return rows; + } + + @Override + public int getIconViewLayoutId() { + return R.layout.apps_grid_row_icon_view; + } + + @Override + public int getRowViewLayoutId() { + return R.layout.apps_grid_row_view; + } + + private LinkedHashMap> computeSectionedApps(List sortedApps) { + LinkedHashMap> sections = new LinkedHashMap<>(); + for (AppInfo info : sortedApps) { + String section = getSection(info); + List sectionApps = sections.get(section); + if (sectionApps == null) { + sectionApps = new ArrayList<>(); + sections.put(section, sectionApps); + } + sectionApps.add(info); + } + return sections; + } + + @Override + public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) { + icon.applyFromApplicationInfo(info); + } + + private String getSection(AppInfo app) { + return app.title.toString().substring(0, 1).toLowerCase(); + } +} + +/** + * Computes the rows in the apps grid view. + */ +class ListedAppsAlgorithm implements AppRowAlgorithm { + + @Override + public List computeAppRows(List sortedApps, int appsPerRow) { + List rows = new ArrayList<>(); + int sectionId = -1; + String prevSection = ""; + for (AppInfo info : sortedApps) { + List appsInRow = new ArrayList<>(); + appsInRow.add(info); + String section = getSection(info); + if (!prevSection.equals(section)) { + prevSection = section; + sectionId++; + rows.add(new AppsRow(sectionId, section, appsInRow)); + } else { + rows.add(new AppsRow(sectionId, appsInRow)); + } + } + return rows; + } + + @Override + public int getIconViewLayoutId() { + return R.layout.apps_list_row_icon_view; + } + + @Override + public int getRowViewLayoutId() { + return R.layout.apps_list_row_view; + } + + @Override + public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) { + icon.applyFromApplicationInfo(info); + } + + private String getSection(AppInfo app) { + return app.title.toString().substring(0, 1).toLowerCase(); + } +} + +/** + * The adapter of all the apps + */ +class AppsListAdapter extends BaseAdapter implements SectionIndexer { + + private LayoutInflater mLayoutInflater; + private List mAppRows = new ArrayList<>(); + private View.OnTouchListener mTouchListener; + private View.OnClickListener mIconClickListener; + private View.OnLongClickListener mIconLongClickListener; + private AppRowAlgorithm mRowAlgorithm; + private int mAppsPerRow; + + public AppsListAdapter(Context context, View.OnTouchListener touchListener, + View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) { + mLayoutInflater = LayoutInflater.from(context); + mTouchListener = touchListener; + mIconClickListener = iconClickListener; + mIconLongClickListener = iconLongClickListener; + } + + void setApps(List apps, int appsPerRow, AppRowAlgorithm algo) { + mAppsPerRow = appsPerRow; + mRowAlgorithm = algo; + mAppRows.clear(); + mAppRows.addAll(apps); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mAppRows.size(); + } + + @Override + public Object getItem(int position) { + return mAppRows.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + AppsRow info = mAppRows.get(position); + ViewGroup row = (ViewGroup) convertView; + if (row == null) { + // Inflate the row and all the icon children necessary + row = (ViewGroup) mLayoutInflater.inflate(mRowAlgorithm.getRowViewLayoutId(), + parent, false); + for (int i = 0; i < mAppsPerRow; i++) { + BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( + mRowAlgorithm.getIconViewLayoutId(), row, false); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, 1); + lp.gravity = Gravity.CENTER_VERTICAL; + icon.setLayoutParams(lp); + icon.setOnTouchListener(mTouchListener); + icon.setOnClickListener(mIconClickListener); + icon.setOnLongClickListener(mIconLongClickListener); + icon.setFocusable(true); + row.addView(icon); + } + } + // Bind the section header + TextView tv = (TextView) row.findViewById(R.id.section); + if (info.sectionDescription != null) { + tv.setText(info.sectionDescription); + tv.setVisibility(View.VISIBLE); + } else { + tv.setVisibility(View.INVISIBLE); + } + // Bind the icons + for (int i = 0; i < mAppsPerRow; i++) { + BubbleTextView icon = (BubbleTextView) row.getChildAt(i + 1); + if (i < info.apps.size()) { + mRowAlgorithm.bindRowViewIconToInfo(icon, info.apps.get(i)); + icon.setVisibility(View.VISIBLE); + } else { + icon.setVisibility(View.INVISIBLE); + } + } + return row; + } + + @Override + public Object[] getSections() { + ArrayList sections = new ArrayList<>(); + int prevSectionId = -1; + for (AppsRow row : mAppRows) { + if (row.sectionId != prevSectionId) { + sections.add(row.sectionDescription.toUpperCase()); + prevSectionId = row.sectionId; + } + } + return sections.toArray(); + } + + @Override + public int getPositionForSection(int sectionIndex) { + for (int i = 0; i < mAppRows.size(); i++) { + AppsRow row = mAppRows.get(i); + if (row.sectionId == sectionIndex) { + return i; + } + } + return 0; + } + + @Override + public int getSectionForPosition(int position) { + return mAppRows.get(position).sectionId; + } +} + +/** + * The alphabetically sorted list of applications. + */ +class AlphabeticalAppList { + + /** + * Callbacks for when this list is modified. + */ + public interface Callbacks { + public void onAppsUpdated(); + } + + private List mApps; + private Callbacks mCb; + + public AlphabeticalAppList(Callbacks cb) { + mCb = cb; + } + + /** + * Returns the list of applications. + */ + public List getApps() { + return mApps; + } + + /** + * Sets the current set of apps. + */ + public void setApps(List apps) { + Collections.sort(apps, LauncherModel.getAppNameComparator()); + mApps = apps; + mCb.onAppsUpdated(); + } + + /** + * Adds new apps to the list. + */ + public void addApps(List apps) { + // We add it in place, in alphabetical order + Comparator appNameComparator = LauncherModel.getAppNameComparator(); + for (AppInfo info : apps) { + // This call will return the exact index of where the item is if >= 0, or the index + // where it should be inserted if < 0. + int index = Collections.binarySearch(mApps, info, appNameComparator); + if (index < 0) { + mApps.add(-(index + 1), info); + } + } + mCb.onAppsUpdated(); + } + + /** + * Updates existing apps in the list + */ + public void updateApps(List apps) { + Comparator appNameComparator = LauncherModel.getAppNameComparator(); + for (AppInfo info : apps) { + int index = mApps.indexOf(info); + if (index != -1) { + mApps.set(index, info); + } else { + index = Collections.binarySearch(mApps, info, appNameComparator); + if (index < 0) { + mApps.add(-(index + 1), info); + } + } + } + mCb.onAppsUpdated(); + } + + /** + * Removes some apps from the list. + */ + public void removeApps(List apps) { + for (AppInfo info : apps) { + int removeIndex = findAppByComponent(mApps, info); + if (removeIndex != -1) { + mApps.remove(removeIndex); + } + } + mCb.onAppsUpdated(); + } + + /** + * Finds the index of an app given a target AppInfo. + */ + private int findAppByComponent(List apps, AppInfo targetInfo) { + ComponentName targetComponent = targetInfo.intent.getComponent(); + int length = apps.size(); + for (int i = 0; i < length; ++i) { + AppInfo info = apps.get(i); + if (info.user.equals(info.user) + && info.intent.getComponent().equals(targetComponent)) { + return i; + } + } + return -1; + } + +} + +/** + * The all apps list view container. + */ +public class AppsContainerView extends FrameLayout implements DragSource, View.OnTouchListener, + View.OnLongClickListener, Insettable, AlphabeticalAppList.Callbacks { + + static final int GRID_LAYOUT = 0; + static final int LIST_LAYOUT = 1; + static final int USE_LAYOUT = LIST_LAYOUT; + + private Launcher mLauncher; + private AppRowAlgorithm mAppRowsAlgorithm; + private AppsListAdapter mAdapter; + private AlphabeticalAppList mApps; + private ListView mList; + private int mAppsRowSize; + private Point mLastTouchDownPos = new Point(); + private Rect mPadding = new Rect(); + + public AppsContainerView(Context context) { + this(context, null); + } + + public AppsContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + + mLauncher = (Launcher) context; + if (USE_LAYOUT == GRID_LAYOUT) { + mAppRowsAlgorithm = new SectionedAppsAlgorithm(); + mAppsRowSize = grid.allAppsRowsSize; + } else if (USE_LAYOUT == LIST_LAYOUT) { + mAppRowsAlgorithm = new ListedAppsAlgorithm(); + mAppsRowSize = 1; + } + mAdapter = new AppsListAdapter(context, this, mLauncher, this); + mApps = new AlphabeticalAppList(this); + } + + /** + * Sets the current set of apps. + */ + public void setApps(List apps) { + mApps.setApps(apps); + } + + /** + * Adds new apps to the list. + */ + public void addApps(List apps) { + mApps.addApps(apps); + } + + /** + * Updates existing apps in the list + */ + public void updateApps(List apps) { + mApps.updateApps(apps); + } + + /** + * Removes some apps from the list. + */ + public void removeApps(List apps) { + mApps.removeApps(apps); + } + + /** + * Scrolls this list view to the top. + */ + public void scrollToTop() { + mList.scrollTo(0, 0); + } + + /** + * Returns the content view used for the launcher transitions. + */ + public View getContentView() { + return findViewById(R.id.apps_list); + } + + /** + * Returns the reveal view used for the launcher transitions. + */ + public View getRevealView() { + return findViewById(R.id.all_apps_transition_overlay); + } + + @Override + public void onAppsUpdated() { + List rows = mAppRowsAlgorithm.computeAppRows(mApps.getApps(), mAppsRowSize); + mAdapter.setApps(rows, mAppsRowSize, mAppRowsAlgorithm); + } + + @Override + protected void onFinishInflate() { + mList = (ListView) findViewById(R.id.apps_list); + mList.setFastScrollEnabled(true); + mList.setFastScrollAlwaysVisible(true); + mList.setItemsCanFocus(true); + mList.setAdapter(mAdapter); + mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); + } + + @Override + public void setInsets(Rect insets) { + setPadding(mPadding.left + insets.left, mPadding.top + insets.top, + mPadding.right + insets.right, mPadding.bottom + insets.bottom); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN || + event.getAction() == MotionEvent.ACTION_MOVE) { + mLastTouchDownPos.set((int) event.getX(), (int) event.getY()); + } + return false; + } + + @Override + public boolean onLongClick(View v) { + // Return early if this is not initiated from a touch + if (!v.isInTouchMode()) return false; + // When we have exited all apps or are in transition, disregard long clicks + if (!mLauncher.isAppsViewVisible() || + mLauncher.getWorkspace().isSwitchingState()) return false; + // Return if global dragging is not enabled + if (!mLauncher.isDraggingEnabled()) return false; + + // Start the drag + mLauncher.getWorkspace().beginDragShared(v, mLastTouchDownPos, this, false); + + // We delay entering spring-loaded mode slightly to make sure the UI + // thready is free of any work. + postDelayed(new Runnable() { + @Override + public void run() { + // We don't enter spring-loaded mode if the drag has been cancelled + if (mLauncher.getDragController().isDragging()) { + // Go into spring loaded mode (must happen before we startDrag()) + mLauncher.enterSpringLoadedDragMode(); + } + } + }, 150); + + return false; + } + + @Override + public boolean supportsFlingToDelete() { + return true; + } + + @Override + public boolean supportsAppInfoDropTarget() { + return true; + } + + @Override + public boolean supportsDeleteDropTarget() { + return true; + } + + @Override + public float getIntrinsicIconScaleFactor() { + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + return (float) grid.allAppsIconSizePx / grid.iconSizePx; + } + + @Override + public void onFlingToDeleteCompleted() { + // We just dismiss the drag when we fling, so cleanup here + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + mLauncher.unlockScreenOrientation(false); + } + + @Override + public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) { + if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && + !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { + // Exit spring loaded mode if we have not successfully dropped or have not handled the + // drop in Workspace + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + mLauncher.unlockScreenOrientation(false); + + // Display an error message if the drag failed due to there not being enough space on the + // target layout we were dropping on. + if (!success) { + boolean showOutOfSpaceMessage = false; + if (target instanceof Workspace) { + int currentScreen = mLauncher.getCurrentWorkspaceScreen(); + Workspace workspace = (Workspace) target; + CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); + ItemInfo itemInfo = (ItemInfo) d.dragInfo; + if (layout != null) { + layout.calculateSpans(itemInfo); + showOutOfSpaceMessage = + !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); + } + } + if (showOutOfSpaceMessage) { + mLauncher.showOutOfSpaceMessage(false); + } + + d.deferDragViewCleanupPostAnimation = false; + } + } +} diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 9f8d499eb..bf368125f 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -149,10 +149,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen * The different content types that this paged view can show. */ public enum ContentType { - Applications, Widgets } - private ContentType mContentType = ContentType.Applications; + private ContentType mContentType = ContentType.Widgets; // Refs private Launcher mLauncher; @@ -164,7 +163,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private int mSaveInstanceStateItemIndex = -1; // Content - private ArrayList mApps; private ArrayList mWidgets; // Caching @@ -174,9 +172,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private int mContentWidth, mContentHeight; private int mWidgetCountX, mWidgetCountY; private PagedViewCellLayout mWidgetSpacingLayout; - private int mNumAppsPages; private int mNumWidgetPages; - private Rect mAllAppsPadding = new Rect(); // Previews & outlines ArrayList mRunningTasks; @@ -214,10 +210,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen super(context, attrs); mLayoutInflater = LayoutInflater.from(context); mPackageManager = context.getPackageManager(); - mApps = new ArrayList(); - mWidgets = new ArrayList(); + mWidgets = new ArrayList<>(); mIconCache = (LauncherAppState.getInstance()).getIconCache(); - mRunningTasks = new ArrayList(); + mRunningTasks = new ArrayList<>(); // Save the default widget preview background TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); @@ -256,10 +251,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen grid.edgeMarginPx, 2 * grid.edgeMarginPx); } - void setAllAppsPadding(Rect r) { - mAllAppsPadding.set(r); - } - void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) { setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight); } @@ -277,22 +268,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int i = -1; if (getPageCount() > 0) { int currentPage = getCurrentPage(); - if (mContentType == ContentType.Applications) { - AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(currentPage); - ShortcutAndWidgetContainer childrenLayout = layout.getShortcutsAndWidgets(); - int numItemsPerPage = mCellCountX * mCellCountY; - int childCount = childrenLayout.getChildCount(); - if (childCount > 0) { - i = (currentPage * numItemsPerPage) + (childCount / 2); - } - } else if (mContentType == ContentType.Widgets) { - int numApps = mApps.size(); + if (mContentType == ContentType.Widgets) { PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage); int numItemsPerPage = mWidgetCountX * mWidgetCountY; int childCount = layout.getChildCount(); if (childCount > 0) { - i = numApps + - (currentPage * numItemsPerPage) + (childCount / 2); + i = (currentPage * numItemsPerPage) + (childCount / 2); } } else { throw new RuntimeException("Invalid ContentType"); @@ -314,13 +295,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int getPageForComponent(int index) { if (index < 0) return 0; - if (index < mApps.size()) { - int numItemsPerPage = mCellCountX * mCellCountY; - return (index / numItemsPerPage); - } else { - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - return (index - mApps.size()) / numItemsPerPage; - } + int numItemsPerPage = mWidgetCountX * mWidgetCountY; + return index / numItemsPerPage; } /** Restores the page for an item at the specified index */ @@ -332,16 +308,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private void updatePageCounts() { mNumWidgetPages = (int) Math.ceil(mWidgets.size() / (float) (mWidgetCountX * mWidgetCountY)); - mNumAppsPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY)); } protected void onDataReady(int width, int height) { - // Now that the data is ready, we can calculate the content width, the number of cells to - // use for each page - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - mCellCountX = (int) grid.allAppsNumCols; - mCellCountY = (int) grid.allAppsNumRows; updatePageCounts(); // Force a measure to update recalculate the gaps @@ -360,7 +329,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen super.onLayout(changed, l, t, r, b); if (!isDataReady()) { - if ((!mApps.isEmpty()) && !mWidgets.isEmpty()) { + if (!mWidgets.isEmpty()) { post(new Runnable() { // This code triggers requestLayout so must be posted outside of the // layout pass. @@ -438,7 +407,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen @Override public void onClick(View v) { // When we have exited all apps or are in transition, disregard clicks - if (!mLauncher.isAllAppsVisible() + if (!mLauncher.isWidgetsViewVisible() || mLauncher.getWorkspace().isSwitchingState() || !(v instanceof PagedViewWidget)) return; @@ -456,11 +425,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen */ @Override protected void determineDraggingStart(android.view.MotionEvent ev) { - // Disable dragging by pulling an app down for now. - } - - private void beginDraggingApplication(View v) { - mLauncher.getWorkspace().beginDragShared(v, this); } static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) { @@ -681,12 +645,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen protected boolean beginDragging(final View v) { if (!super.beginDragging(v)) return false; - if (v instanceof BubbleTextView) { - beginDraggingApplication(v); - } else if (v instanceof PagedViewWidget) { + if (v instanceof PagedViewWidget) { if (!beginDraggingWidget(v)) { return false; } + } else { + Log.e(TAG, "Unexpected dragging view: " + v); } // We delay entering spring-loaded mode slightly to make sure the UI @@ -852,7 +816,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Clean up all the async tasks Iterator iter = mRunningTasks.iterator(); while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); task.cancel(false); iter.remove(); mDirtyPageContent.set(task.page, true); @@ -886,7 +850,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Update the thread priorities given the direction lookahead Iterator iter = mRunningTasks.iterator(); while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); int pageIndex = task.page; if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) || (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) { @@ -897,36 +861,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - /* - * Apps PagedView implementation - */ - private void setVisibilityOnChildren(ViewGroup layout, int visibility) { - int childCount = layout.getChildCount(); - for (int i = 0; i < childCount; ++i) { - layout.getChildAt(i).setVisibility(visibility); - } - } - private void setupPage(AppsCustomizeCellLayout layout) { - layout.setGridSize(mCellCountX, mCellCountY); - - // Note: We force a measure here to get around the fact that when we do layout calculations - // immediately after syncing, we don't have a proper width. That said, we already know the - // expected page width, so we can actually optimize by hiding all the TextView-based - // children that are expensive to measure, and let that happen naturally later. - setVisibilityOnChildren(layout, View.GONE); - int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST); - int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); - layout.measure(widthSpec, heightSpec); - - Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel); - if (bg != null) { - bg.setAlpha(mPageBackgroundsVisible ? 255: 0); - layout.setBackground(bg); - } - - setVisibilityOnChildren(layout, View.VISIBLE); - } - public void setPageBackgroundsVisible(boolean visible) { mPageBackgroundsVisible = visible; int childCount = getChildCount(); @@ -938,43 +872,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - public void syncAppsPageItems(int page, boolean immediate) { - // ensure that we have the right number of items on the pages - final boolean isRtl = isLayoutRtl(); - int numCells = mCellCountX * mCellCountY; - int startIndex = page * numCells; - int endIndex = Math.min(startIndex + numCells, mApps.size()); - AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(page); - - layout.removeAllViewsOnPage(); - ArrayList items = new ArrayList(); - ArrayList images = new ArrayList(); - for (int i = startIndex; i < endIndex; ++i) { - AppInfo info = mApps.get(i); - BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - R.layout.apps_customize_application, layout, false); - icon.applyFromApplicationInfo(info); - icon.setOnClickListener(mLauncher); - icon.setOnLongClickListener(this); - icon.setOnTouchListener(this); - icon.setOnKeyListener(mKeyListener); - icon.setOnFocusChangeListener(layout.mFocusHandlerView); - - int index = i - startIndex; - int x = index % mCellCountX; - int y = index / mCellCountX; - if (isRtl) { - x = mCellCountX - x - 1; - } - layout.addViewToCellLayout(icon, -1, i, new CellLayout.LayoutParams(x,y, 1,1), false); - - items.add(info); - images.add(info.iconBitmap); - } - - enableHwLayersOnVisiblePages(); - } - /** * A helper to return the priority for loading of the specified widget page. */ @@ -991,7 +888,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen Iterator iter = mRunningTasks.iterator(); int minPageDiff = Integer.MAX_VALUE; while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); minPageDiff = Math.abs(task.page - toPage); } @@ -1026,7 +923,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // Prune all tasks that are no longer needed Iterator iter = mRunningTasks.iterator(); while (iter.hasNext()) { - AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); + AppsCustomizeAsyncTask task = iter.next(); int taskPage = task.page; if (taskPage < getAssociatedLowerPageBound(mCurrentPage) || taskPage > getAssociatedUpperPageBound(mCurrentPage)) { @@ -1264,14 +1161,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen cancelAllTasks(); Context context = getContext(); - if (mContentType == ContentType.Applications) { - for (int i = 0; i < mNumAppsPages; ++i) { - AppsCustomizeCellLayout layout = new AppsCustomizeCellLayout(context); - setupPage(layout); - addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT)); - } - } else if (mContentType == ContentType.Widgets) { + if (mContentType == ContentType.Widgets) { for (int j = 0; j < mNumWidgetPages; ++j) { PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX, mWidgetCountY); @@ -1291,7 +1181,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen if (mContentType == ContentType.Widgets) { syncWidgetPageItems(page, immediate); } else { - syncAppsPageItems(page, immediate); + Log.e(TAG, "Unexpected ContentType"); } } @@ -1383,7 +1273,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } /** - * We should call thise method whenever the core data changes (mApps, mWidgets) so that we can + * We should call thise method whenever the core data changes (mWidgets) so that we can * appropriately determine when to invalidate the PagedView page data. In cases where the data * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the * next onMeasure() pass, which will trigger an invalidatePageData() itself. @@ -1399,73 +1289,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - public void setApps(ArrayList list) { - mApps = list; - Collections.sort(mApps, LauncherModel.getAppNameComparator()); - updatePageCountsAndInvalidateData(); - } - - public ArrayList getApps() { - return mApps; - } - - private void addAppsWithoutInvalidate(ArrayList list) { - // We add it in place, in alphabetical order - int count = list.size(); - for (int i = 0; i < count; ++i) { - AppInfo info = list.get(i); - int index = Collections.binarySearch(mApps, info, LauncherModel.getAppNameComparator()); - if (index < 0) { - mApps.add(-(index + 1), info); - } - } - } - public void addApps(ArrayList list) { - addAppsWithoutInvalidate(list); - updatePageCountsAndInvalidateData(); - } - private int findAppByComponent(List list, AppInfo item) { - ComponentName removeComponent = item.intent.getComponent(); - int length = list.size(); - for (int i = 0; i < length; ++i) { - AppInfo info = list.get(i); - if (info.user.equals(item.user) - && info.intent.getComponent().equals(removeComponent)) { - return i; - } - } - return -1; - } - private void removeAppsWithoutInvalidate(ArrayList list) { - // loop through all the apps and remove apps that have the same component - int length = list.size(); - for (int i = 0; i < length; ++i) { - AppInfo info = list.get(i); - int removeIndex = findAppByComponent(mApps, info); - if (removeIndex > -1) { - mApps.remove(removeIndex); - } - } - } - public void removeApps(ArrayList appInfos) { - removeAppsWithoutInvalidate(appInfos); - updatePageCountsAndInvalidateData(); - } - public void updateApps(ArrayList list) { - // We remove and re-add the updated applications list because it's properties may have - // changed (ie. the title), and this will ensure that the items will be in their proper - // place in the list. - removeAppsWithoutInvalidate(list); - addAppsWithoutInvalidate(list); - updatePageCountsAndInvalidateData(); - } - public void reset() { // If we have reset, then we should not continue to restore the previous state mSaveInstanceStateItemIndex = -1; - if (mContentType != ContentType.Applications) { - setContentType(ContentType.Applications); + if (mContentType != ContentType.Widgets) { + setContentType(ContentType.Widgets); } if (mCurrentPage != 0) { @@ -1479,7 +1308,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen public void dumpState() { // TODO: Dump information related to current list of Applications, Widgets, etc. - AppInfo.dumpApplicationInfoList(TAG, "mApps", mApps); dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets); } @@ -1534,10 +1362,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int stringId = R.string.default_scroll_format; int count = 0; - if (mContentType == ContentType.Applications) { - stringId = R.string.apps_customize_apps_scroll_format; - count = mNumAppsPages; - } else if (mContentType == ContentType.Widgets) { + if (mContentType == ContentType.Widgets) { stringId = R.string.apps_customize_widgets_scroll_format; count = mNumWidgetPages; } else { diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java index a2717126d..5e2f05c61 100644 --- a/src/com/android/launcher3/AppsCustomizeTabHost.java +++ b/src/com/android/launcher3/AppsCustomizeTabHost.java @@ -27,7 +27,6 @@ import android.widget.FrameLayout; public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable { static final String LOG_TAG = "AppsCustomizeTabHost"; - private static final String APPS_TAB_TAG = "APPS"; private static final String WIDGETS_TAB_TAG = "WIDGETS"; private AppsCustomizePagedView mPagedView; @@ -50,10 +49,6 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit mPagedView.setContentType(type); } - public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) { - setContentTypeImmediate(type); - } - @Override public void setInsets(Rect insets) { mInsets.set(insets); @@ -79,27 +74,38 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit } /** + * Returns the content view used for the launcher transitions. + */ + public View getContentView() { + return findViewById(R.id.apps_customize_pane_content); + } + + /** + * Returns the reveal view used for the launcher transitions. + */ + public View getRevealView() { + return findViewById(R.id.fake_page); + } + + /** + * Returns the page indicators view. + */ + public View getPageIndicators() { + return findViewById(R.id.apps_customize_page_indicator); + } + + /** * Returns the content type for the specified tab tag. */ public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) { - if (tag.equals(APPS_TAB_TAG)) { - return AppsCustomizePagedView.ContentType.Applications; - } else if (tag.equals(WIDGETS_TAB_TAG)) { - return AppsCustomizePagedView.ContentType.Widgets; - } - return AppsCustomizePagedView.ContentType.Applications; + return AppsCustomizePagedView.ContentType.Widgets; } /** * Returns the tab tag for a given content type. */ public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) { - if (type == AppsCustomizePagedView.ContentType.Applications) { - return APPS_TAB_TAG; - } else if (type == AppsCustomizePagedView.ContentType.Widgets) { - return WIDGETS_TAB_TAG; - } - return APPS_TAB_TAG; + return WIDGETS_TAB_TAG; } /** @@ -199,6 +205,7 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit ViewGroup parent = (ViewGroup) getParent(); if (parent == null) return; + View appsView = ((Launcher) getContext()).getAppsView(); View overviewPanel = ((Launcher) getContext()).getOverviewPanel(); final int count = parent.getChildCount(); if (!isChildrenDrawingOrderEnabled()) { @@ -207,7 +214,8 @@ public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransit if (child == this) { break; } else { - if (child.getVisibility() == GONE || child == overviewPanel) { + if (child.getVisibility() == GONE || child == overviewPanel || + child == appsView) { continue; } child.setVisibility(visibility); diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index f9255e6bd..5ea84aeb2 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.SparseArray; import android.util.TypedValue; +import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -49,7 +50,7 @@ public class BubbleTextView extends TextView { private static final int SHADOW_SMALL_COLOUR = 0xCC000000; static final float PADDING_V = 3.0f; - + private Drawable mIcon; private final Drawable mBackground; private final CheckLongPressHelper mLongPressHelper; private final HolographicOutlineHelper mOutlineHelper; @@ -62,9 +63,12 @@ public class BubbleTextView extends TextView { private float mSlop; - private int mTextColor; private final boolean mCustomShadowsEnabled; - private boolean mIsTextVisible; + private final boolean mLayoutHorizontal; + private final int mIconSize; + private final int mIconPaddingSize; + private final int mTextSize; + private int mTextColor; private boolean mStayPressed; private boolean mIgnorePressedStateChange; @@ -79,10 +83,19 @@ public class BubbleTextView extends TextView { public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, true); + mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false); + mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride, + grid.allAppsIconSizePx); + mIconPaddingSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconPaddingOverride, + grid.iconDrawablePaddingPx); + mTextSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_textSizeOverride, + grid.allAppsIconTextSizePx); a.recycle(); if (mCustomShadowsEnabled) { @@ -92,6 +105,12 @@ public class BubbleTextView extends TextView { } else { mBackground = null; } + + // If we are laying out horizontal, then center the text vertically + if (mLayoutHorizontal) { + setGravity(Gravity.CENTER_VERTICAL); + } + mLongPressHelper = new CheckLongPressHelper(this); mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); @@ -106,9 +125,7 @@ public class BubbleTextView extends TextView { super.onFinishInflate(); // Ensure we are using the right text size - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); + setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize); } public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, @@ -119,16 +136,11 @@ public class BubbleTextView extends TextView { public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, boolean setDefaultPadding, boolean promiseStateChanged) { Bitmap b = info.getIcon(iconCache); - LauncherAppState app = LauncherAppState.getInstance(); FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); iconDrawable.setGhostModeEnabled(info.isDisabled != 0); - setCompoundDrawables(null, iconDrawable, null, null); - if (setDefaultPadding) { - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setCompoundDrawablePadding(grid.iconDrawablePaddingPx); - } + setIcon(iconDrawable, mIconSize, setDefaultPadding ? mIconPaddingSize : -1); if (info.contentDescription != null) { setContentDescription(info.contentDescription); } @@ -141,13 +153,7 @@ public class BubbleTextView extends TextView { } public void applyFromApplicationInfo(AppInfo info) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - - Drawable topDrawable = Utilities.createIconDrawable(info.iconBitmap); - topDrawable.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx); - setCompoundDrawables(null, topDrawable, null, null); - setCompoundDrawablePadding(grid.iconDrawablePaddingPx); + setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize, mIconPaddingSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); @@ -155,7 +161,6 @@ public class BubbleTextView extends TextView { setTag(info); } - @Override protected boolean setFrame(int left, int top, int right, int bottom) { if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) { @@ -186,10 +191,19 @@ public class BubbleTextView extends TextView { } } + /** Returns the icon for this view. */ + public Drawable getIcon() { + return mIcon; + } + + /** Returns whether the layout is horizontal. */ + public boolean isLayoutHorizontal() { + return mLayoutHorizontal; + } + private void updateIconState() { - Drawable top = getCompoundDrawables()[1]; - if (top instanceof FastBitmapDrawable) { - ((FastBitmapDrawable) top).setPressed(isPressed() || mStayPressed); + if (mIcon instanceof FastBitmapDrawable) { + ((FastBitmapDrawable) mIcon).setPressed(isPressed() || mStayPressed); } } @@ -325,10 +339,9 @@ public class BubbleTextView extends TextView { super.onAttachedToWindow(); if (mBackground != null) mBackground.setCallback(this); - Drawable top = getCompoundDrawables()[1]; - if (top instanceof PreloadIconDrawable) { - ((PreloadIconDrawable) top).applyPreloaderTheme(getPreloaderTheme()); + if (mIcon instanceof PreloadIconDrawable) { + ((PreloadIconDrawable) mIcon).applyPreloaderTheme(getPreloaderTheme()); } mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -358,11 +371,6 @@ public class BubbleTextView extends TextView { } else { super.setTextColor(res.getColor(android.R.color.transparent)); } - mIsTextVisible = visible; - } - - public boolean isTextVisible() { - return mIsTextVisible; } @Override @@ -385,15 +393,13 @@ public class BubbleTextView extends TextView { ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ? info.getInstallProgress() : 0)) : 100; - Drawable[] drawables = getCompoundDrawables(); - Drawable top = drawables[1]; - if (top != null) { + if (mIcon != null) { final PreloadIconDrawable preloadDrawable; - if (top instanceof PreloadIconDrawable) { - preloadDrawable = (PreloadIconDrawable) top; + if (mIcon instanceof PreloadIconDrawable) { + preloadDrawable = (PreloadIconDrawable) mIcon; } else { - preloadDrawable = new PreloadIconDrawable(top, getPreloaderTheme()); - setCompoundDrawables(drawables[0], preloadDrawable, drawables[2], drawables[3]); + preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme()); + setIcon(preloadDrawable, mIconSize, -1); } preloadDrawable.setLevel(progressLevel); @@ -417,4 +423,23 @@ public class BubbleTextView extends TextView { } return theme; } + + /** + * Sets the icon for this view based on the layout direction. + */ + private Drawable setIcon(Drawable icon, int iconSize, int drawablePadding) { + mIcon = icon; + if (iconSize != -1) { + mIcon.setBounds(0, 0, iconSize, iconSize); + } + if (mLayoutHorizontal) { + setCompoundDrawablesRelative(mIcon, null, null, null); + } else { + setCompoundDrawablesRelative(null, mIcon, null, null); + } + if (drawablePadding != -1) { + setCompoundDrawablePadding(drawablePadding); + } + return icon; + } } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 7d02e10b5..b5bb55ca7 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -122,6 +122,8 @@ public class DeviceProfile { int hotseatAllAppsRank; int allAppsNumRows; int allAppsNumCols; + // TODO(winsonc): to be used with the grid layout + int allAppsRowsSize; int searchBarSpaceWidthPx; int searchBarSpaceHeightPx; int pageIndicatorHeightPx; diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 0a6557907..f6b05208b 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -24,6 +24,7 @@ import android.animation.PropertyValuesHolder; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; @@ -46,7 +47,6 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; import android.widget.TextView; - import com.android.launcher3.FolderInfo.FolderListener; import com.android.launcher3.Workspace.ItemOperator; @@ -219,7 +219,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return false; } - mLauncher.getWorkspace().beginDragShared(v, this); + mLauncher.getWorkspace().beginDragShared(v, new Point(), this, false); mCurrentDragInfo = item; mEmptyCellRank = item.rank; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 984d536a5..9c4632cc8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1,4 +1,3 @@ - /* * Copyright (C) 2008 The Android Open Source Project * @@ -22,7 +21,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; -import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.Activity; @@ -48,7 +46,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; -import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; @@ -82,15 +79,12 @@ import android.view.Surface; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; import android.widget.FrameLayout; @@ -132,7 +126,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, - View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener { + View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener, + LauncherStateTransitionAnimation.Callbacks { static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -213,9 +208,10 @@ public class Launcher extends Activity public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data"; /** The different states that Launcher can be in. */ - private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED }; + enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }; private State mState = State.WORKSPACE; private AnimatorSet mStateAnimation; + private LauncherStateTransitionAnimation mStateTransitionAnimation; private boolean mIsSafeModeEnabled; @@ -235,7 +231,6 @@ public class Launcher extends Activity private static int NEW_APPS_PAGE_MOVE_DELAY = 500; private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; private static int NEW_APPS_ANIMATION_DELAY = 500; - private static final int SINGLE_FRAME_DELAY = 16; private final BroadcastReceiver mCloseSystemDialogsReceiver = new CloseSystemDialogsIntentReceiver(); @@ -267,6 +262,7 @@ public class Launcher extends Activity private View mAllAppsButton; private SearchDropTargetBar mSearchDropTargetBar; + private AppsContainerView mAppsView; private AppsCustomizeTabHost mAppsCustomizeTabHost; private AppsCustomizePagedView mAppsCustomizeContent; private boolean mAutoAdvanceRunning = false; @@ -305,9 +301,6 @@ public class Launcher extends Activity private View.OnTouchListener mHapticFeedbackTouchListener; - public static final int BUILD_LAYER = 0; - public static final int BUILD_AND_SET_LAYER = 1; - // Related to the auto-advancing of widgets private final int ADVANCE_MSG = 1; private final int mAdvanceInterval = 20000; @@ -431,6 +424,7 @@ public class Launcher extends Activity mIconCache.flushInvalidIcons(grid); mDragController = new DragController(this); mInflater = getLayoutInflater(); + mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this); mStats = new Stats(this); @@ -990,10 +984,12 @@ public class Launcher extends Activity super.onResume(); // Restore the previous launcher state - if (mOnResumeState == State.WORKSPACE) { + if (mOnResumeState == State.WORKSPACE || mOnResumeState == State.NONE) { showWorkspace(false); - } else if (mOnResumeState == State.APPS_CUSTOMIZE) { - showAllApps(false, mAppsCustomizeContent.getContentType(), false); + } else if (mOnResumeState == State.APPS) { + showAppsView(false /* animated */, false /* resetListToTop */); + } else if (mOnResumeState == State.WIDGETS) { + showWidgetsView(false, false); } mOnResumeState = State.NONE; @@ -1302,8 +1298,8 @@ public class Launcher extends Activity } State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal())); - if (state == State.APPS_CUSTOMIZE) { - mOnResumeState = State.APPS_CUSTOMIZE; + if (state == State.APPS || state == State.WIDGETS) { + mOnResumeState = state; } int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, @@ -1429,6 +1425,9 @@ public class Launcher extends Activity mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.search_drop_target_bar); + // Setup Apps + mAppsView = (AppsContainerView) findViewById(R.id.apps_view); + // Setup AppsCustomize mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane); mAppsCustomizeContent = (AppsCustomizePagedView) @@ -1646,16 +1645,17 @@ public class Launcher extends Activity if (Intent.ACTION_SCREEN_OFF.equals(action)) { mUserPresent = false; mDragLayer.clearAllResizeFrames(); - updateRunning(); + updateAutoAdvanceState(); // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop - if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) { + if (mAppsView != null && mAppsCustomizeTabHost != null && + mPendingAddInfo.container == ItemInfo.NO_ID) { showWorkspace(false); } } else if (Intent.ACTION_USER_PRESENT.equals(action)) { mUserPresent = true; - updateRunning(); + updateAutoAdvanceState(); } else if (ENABLE_DEBUG_INTENTS && DebugIntents.DELETE_DATABASE.equals(action)) { mModel.resetLoadedState(false, true); mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE, @@ -1723,12 +1723,12 @@ public class Launcher extends Activity unregisterReceiver(mReceiver); mAttached = false; } - updateRunning(); + updateAutoAdvanceState(); } public void onWindowVisibilityChanged(int visibility) { mVisible = visibility == View.VISIBLE; - updateRunning(); + updateAutoAdvanceState(); // The following code used to be in onResume, but it turns out onResume is called when // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged // is a more appropriate event to handle @@ -1775,7 +1775,7 @@ public class Launcher extends Activity mAutoAdvanceSentTime = System.currentTimeMillis(); } - private void updateRunning() { + private void updateAutoAdvanceState() { boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty(); if (autoAdvanceRunning != mAutoAdvanceRunning) { mAutoAdvanceRunning = autoAdvanceRunning; @@ -1821,14 +1821,14 @@ public class Launcher extends Activity if (v instanceof Advanceable) { mWidgetsToAdvance.put(hostView, appWidgetInfo); ((Advanceable) v).fyiWillBeAdvancedByHostKThx(); - updateRunning(); + updateAutoAdvanceState(); } } void removeWidgetToAutoAdvance(View hostView) { if (mWidgetsToAdvance.containsKey(hostView)) { mWidgetsToAdvance.remove(hostView); - updateRunning(); + updateAutoAdvanceState(); } } @@ -1842,14 +1842,18 @@ public class Launcher extends Activity Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); } - public ArrayList getAllAppsList() { - return mAppsCustomizeContent.getApps(); - } - public DragLayer getDragLayer() { return mDragLayer; } + public AppsContainerView getAppsView() { + return mAppsView; + } + + public AppsCustomizeTabHost getWidgetsView() { + return mAppsCustomizeTabHost; + } + public Workspace getWorkspace() { return mWorkspace; } @@ -1935,6 +1939,11 @@ public class Launcher extends Activity imm.hideSoftInputFromWindow(v.getWindowToken(), 0); } + // Reset the apps view + if (!alreadyOnHome && mAppsView != null) { + mAppsView.scrollToTop(); + } + // Reset the apps customize page if (!alreadyOnHome && mAppsCustomizeTabHost != null) { mAppsCustomizeTabHost.reset(); @@ -2452,13 +2461,10 @@ public class Launcher extends Activity return; } - if (isAllAppsVisible()) { - if (mAppsCustomizeContent.getContentType() == - AppsCustomizePagedView.ContentType.Applications) { - showWorkspace(true); - } else { - showOverviewMode(true); - } + if (isAppsViewVisible()) { + showWorkspace(true); + } else if (isWidgetsViewVisible()) { + showOverviewMode(true); } else if (mWorkspace.isInOverviewMode()) { mWorkspace.exitOverviewMode(true); } else if (mWorkspace.getOpenFolder() != null) { @@ -2589,10 +2595,10 @@ public class Launcher extends Activity */ protected void onClickAllAppsButton(View v) { if (LOGD) Log.d(TAG, "onClickAllAppsButton"); - if (isAllAppsVisible()) { + if (isAppsViewVisible()) { showWorkspace(true); } else { - showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false); + showAppsView(true /* animated */, false /* resetListToTop */); } if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickAllAppsButton(v); @@ -2763,7 +2769,7 @@ public class Launcher extends Activity if (mIsSafeModeEnabled) { Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); } else { - showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true); + showWidgetsView(true /* animated */, true /* resetPageToZero */); if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickAddWidgetButton(view); } @@ -2920,12 +2926,11 @@ public class Launcher extends Activity int width = v.getMeasuredWidth(), height = v.getMeasuredHeight(); if (v instanceof TextView) { // Launch from center of icon, not entire view - TextView tv = (TextView) v; - Drawable[] drawables = tv.getCompoundDrawables(); - if (drawables != null && drawables[1] != null) { - Rect bounds = drawables[1].getBounds(); + Drawable icon = Workspace.getTextViewIcon((TextView) v); + if (icon != null) { + Rect bounds = icon.getBounds(); left = (width - bounds.width()) / 2; - top = tv.getPaddingTop(); + top = v.getPaddingTop(); width = bounds.width(); height = bounds.height(); } @@ -3219,12 +3224,23 @@ public class Launcher extends Activity return null; } } else { - return (CellLayout) mWorkspace.getScreenWithId(screenId); + return mWorkspace.getScreenWithId(screenId); } } + /** + * For overridden classes. + */ public boolean isAllAppsVisible() { - return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE); + return isAppsViewVisible(); + } + + public boolean isAppsViewVisible() { + return (mState == State.APPS) || (mOnResumeState == State.APPS); + } + + public boolean isWidgetsViewVisible() { + return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS); } private void setWorkspaceBackground(boolean workspace) { @@ -3242,578 +3258,6 @@ public class Launcher extends Activity setWorkspaceBackground(visible); } - private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace); - } - } - - private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace); - } - - // Update the workspace transition step as well - dispatchOnLauncherTransitionStep(v, 0f); - } - - private void dispatchOnLauncherTransitionStep(View v, float t) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionStep(this, t); - } - } - - private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) { - if (v instanceof LauncherTransitionable) { - ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace); - } - - // Update the workspace transition step as well - dispatchOnLauncherTransitionStep(v, 1f); - } - - /** - * Things to test when changing the following seven functions. - * - Home from workspace - * - from center screen - * - from other screens - * - Home from all apps - * - from center screen - * - from other screens - * - Back from all apps - * - from center screen - * - from other screens - * - Launch app from workspace and quit - * - with back - * - with home - * - Launch app from all apps and quit - * - with back - * - with home - * - Go to a screen that's not the default, then all - * apps, and launch and app, and go back - * - with back - * -with home - * - On workspace, long press power and go back - * - with back - * - with home - * - On all apps, long press power and go back - * - with back - * - with home - * - On workspace, power off - * - On all apps, power off - * - Launch an app and turn off the screen while in that app - * - Go back with home key - * - Go back with back key TODO: make this not go to workspace - * - From all apps - * - From workspace - * - Enter and exit car mode (becuase it causes an extra configuration changed) - * - From all apps - * - From the center workspace - * - From another workspace - */ - - /** - * Zoom the camera out from the workspace to reveal 'toView'. - * Assumes that the view to show is anchored at either the very top or very bottom - * of the screen. - */ - private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) { - AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType(); - showAppsCustomizeHelper(animated, springLoaded, contentType); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded, - final AppsCustomizePagedView.ContentType contentType) { - if (mStateAnimation != null) { - mStateAnimation.setDuration(0); - mStateAnimation.cancel(); - mStateAnimation = null; - } - - boolean material = Utilities.isLmpOrAbove(); - - final Resources res = getResources(); - - final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); - final int itemsAlphaStagger = - res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); - - final View fromView = mWorkspace; - final AppsCustomizeTabHost toView = mAppsCustomizeTabHost; - - final HashMap layerViews = new HashMap(); - - Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ? - Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN; - Animator workspaceAnim = - mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews); - if (contentType == AppsCustomizePagedView.ContentType.Widgets) { - // Set the content type for the all apps/widgets space - mAppsCustomizeTabHost.setContentTypeImmediate(contentType); - } - - // If for some reason our views aren't initialized, don't animate - boolean initialized = getAllAppsButton() != null; - - if (animated && initialized) { - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - final AppsCustomizePagedView content = (AppsCustomizePagedView) - toView.findViewById(R.id.apps_customize_pane_content); - - final View page = content.getPageAt(content.getCurrentPage()); - final View revealView = toView.findViewById(R.id.fake_page); - - final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets; - if (isWidgetTray) { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); - } else { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel)); - } - - // Hide the real page background, and swap in the fake one - content.setPageBackgroundsVisible(false); - revealView.setVisibility(View.VISIBLE); - // We need to hide this view as the animation start will be posted. - revealView.setAlpha(0); - - int width = revealView.getMeasuredWidth(); - int height = revealView.getMeasuredHeight(); - float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); - - revealView.setTranslationY(0); - revealView.setTranslationX(0); - - // Get the y delta between the center of the page and the center of the all apps button - int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, - getAllAppsButton(), null); - - float alpha = 0; - float xDrift = 0; - float yDrift = 0; - if (material) { - alpha = isWidgetTray ? 0.3f : 1f; - yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1]; - xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0]; - } else { - yDrift = 2 * height / 3; - xDrift = 0; - } - final float initAlpha = alpha; - - layerViews.put(revealView, BUILD_AND_SET_LAYER); - PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f); - PropertyValuesHolder panelDriftY = - PropertyValuesHolder.ofFloat("translationY", yDrift, 0); - PropertyValuesHolder panelDriftX = - PropertyValuesHolder.ofFloat("translationX", xDrift, 0); - - ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView, - panelAlpha, panelDriftY, panelDriftX); - - panelAlphaAndDrift.setDuration(revealDuration); - panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); - - mStateAnimation.play(panelAlphaAndDrift); - - if (page != null) { - page.setVisibility(View.VISIBLE); - layerViews.put(page, BUILD_AND_SET_LAYER); - - ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0); - page.setTranslationY(yDrift); - pageDrift.setDuration(revealDuration); - pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); - pageDrift.setStartDelay(itemsAlphaStagger); - mStateAnimation.play(pageDrift); - - page.setAlpha(0f); - ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f); - itemsAlpha.setDuration(revealDuration); - itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); - itemsAlpha.setStartDelay(itemsAlphaStagger); - mStateAnimation.play(itemsAlpha); - } - - View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator); - pageIndicators.setAlpha(0.01f); - ObjectAnimator indicatorsAlpha = - ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f); - indicatorsAlpha.setDuration(revealDuration); - mStateAnimation.play(indicatorsAlpha); - - if (material) { - final View allApps = getAllAppsButton(); - int allAppsButtonSize = LauncherAppState.getInstance(). - getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; - float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2; - Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2, - height / 2, startRadius, revealRadius); - reveal.setDuration(revealDuration); - reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - - reveal.addListener(new AnimatorListenerAdapter() { - public void onAnimationStart(Animator animation) { - if (!isWidgetTray) { - allApps.setVisibility(View.INVISIBLE); - } - } - public void onAnimationEnd(Animator animation) { - if (!isWidgetTray) { - allApps.setVisibility(View.VISIBLE); - } - } - }); - mStateAnimation.play(reveal); - } - - mStateAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - dispatchOnLauncherTransitionEnd(fromView, animated, false); - dispatchOnLauncherTransitionEnd(toView, animated, false); - - revealView.setVisibility(View.INVISIBLE); - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_NONE, null); - } - } - content.setPageBackgroundsVisible(true); - - // Hide the search bar - if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.hideSearchBar(false); - } - - // This can hold unnecessary references to views. - mStateAnimation = null; - } - - }); - - if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); - } - - dispatchOnLauncherTransitionPrepare(fromView, animated, false); - dispatchOnLauncherTransitionPrepare(toView, animated, false); - final AnimatorSet stateAnimation = mStateAnimation; - final Runnable startAnimRunnable = new Runnable() { - public void run() { - // Check that mStateAnimation hasn't changed while - // we waited for a layout/draw pass - if (mStateAnimation != stateAnimation) - return; - dispatchOnLauncherTransitionStart(fromView, animated, false); - dispatchOnLauncherTransitionStart(toView, animated, false); - - revealView.setAlpha(initAlpha); - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - } - - if (Utilities.isLmpOrAbove()) { - for (View v : layerViews.keySet()) { - if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); - } - } - mStateAnimation.start(); - } - }; - toView.bringToFront(); - toView.setVisibility(View.VISIBLE); - toView.post(startAnimRunnable); - } else { - toView.setTranslationX(0.0f); - toView.setTranslationY(0.0f); - toView.setScaleX(1.0f); - toView.setScaleY(1.0f); - toView.setVisibility(View.VISIBLE); - toView.bringToFront(); - - if (!springLoaded && !LauncherAppState.getInstance().isScreenLarge()) { - // Hide the search bar - if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.hideSearchBar(false); - } - } - dispatchOnLauncherTransitionPrepare(fromView, animated, false); - dispatchOnLauncherTransitionStart(fromView, animated, false); - dispatchOnLauncherTransitionEnd(fromView, animated, false); - dispatchOnLauncherTransitionPrepare(toView, animated, false); - dispatchOnLauncherTransitionStart(toView, animated, false); - dispatchOnLauncherTransitionEnd(toView, animated, false); - } - } - - /** - * Zoom the camera back into the workspace, hiding 'fromView'. - * This is the opposite of showAppsCustomizeHelper. - * @param animated If true, the transition will be animated. - */ - private void hideAppsCustomizeHelper(Workspace.State toState, final boolean animated, - final boolean springLoaded, final Runnable onCompleteRunnable) { - - if (mStateAnimation != null) { - mStateAnimation.setDuration(0); - mStateAnimation.cancel(); - mStateAnimation = null; - } - - boolean material = Utilities.isLmpOrAbove(); - Resources res = getResources(); - - final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime); - final int itemsAlphaStagger = - res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); - - final View fromView = mAppsCustomizeTabHost; - final View toView = mWorkspace; - Animator workspaceAnim = null; - final HashMap layerViews = new HashMap(); - - if (toState == Workspace.State.NORMAL) { - workspaceAnim = mWorkspace.getChangeStateAnimation( - toState, animated, layerViews); - } else if (toState == Workspace.State.SPRING_LOADED || - toState == Workspace.State.OVERVIEW) { - workspaceAnim = mWorkspace.getChangeStateAnimation( - toState, animated, layerViews); - } - - // If for some reason our views aren't initialized, don't animate - boolean initialized = getAllAppsButton() != null; - - if (animated && initialized) { - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); - } - - final AppsCustomizePagedView content = (AppsCustomizePagedView) - fromView.findViewById(R.id.apps_customize_pane_content); - - final View page = content.getPageAt(content.getNextPage()); - - // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases - int count = content.getChildCount(); - for (int i = 0; i < count; i++) { - View child = content.getChildAt(i); - if (child != page) { - child.setVisibility(View.INVISIBLE); - } - } - final View revealView = fromView.findViewById(R.id.fake_page); - - // hideAppsCustomizeHelper is called in some cases when it is already hidden - // don't perform all these no-op animations. In particularly, this was causing - // the all-apps button to pop in and out. - if (fromView.getVisibility() == View.VISIBLE) { - AppsCustomizePagedView.ContentType contentType = content.getContentType(); - final boolean isWidgetTray = - contentType == AppsCustomizePagedView.ContentType.Widgets; - - if (isWidgetTray) { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark)); - } else { - revealView.setBackground(res.getDrawable(R.drawable.quantum_panel)); - } - - int width = revealView.getMeasuredWidth(); - int height = revealView.getMeasuredHeight(); - float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); - - // Hide the real page background, and swap in the fake one - revealView.setVisibility(View.VISIBLE); - content.setPageBackgroundsVisible(false); - - final View allAppsButton = getAllAppsButton(); - revealView.setTranslationY(0); - int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, - allAppsButton, null); - - float xDrift = 0; - float yDrift = 0; - if (material) { - yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1]; - xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0]; - } else { - yDrift = 2 * height / 3; - xDrift = 0; - } - - layerViews.put(revealView, BUILD_AND_SET_LAYER); - TimeInterpolator decelerateInterpolator = material ? - new LogDecelerateInterpolator(100, 0) : - new DecelerateInterpolator(1f); - - // The vertical motion of the apps panel should be delayed by one frame - // from the conceal animation in order to give the right feel. We correpsondingly - // shorten the duration so that the slide and conceal end at the same time. - ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY", - 0, yDrift); - panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); - panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - panelDriftY.setInterpolator(decelerateInterpolator); - mStateAnimation.play(panelDriftY); - - ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX", - 0, xDrift); - panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); - panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - panelDriftX.setInterpolator(decelerateInterpolator); - mStateAnimation.play(panelDriftX); - - if (isWidgetTray || !material) { - float finalAlpha = material ? 0.4f : 0f; - revealView.setAlpha(1f); - ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha", - 1f, finalAlpha); - panelAlpha.setDuration(material ? revealDuration : 150); - panelAlpha.setInterpolator(decelerateInterpolator); - panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); - mStateAnimation.play(panelAlpha); - } - - if (page != null) { - layerViews.put(page, BUILD_AND_SET_LAYER); - - ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY", - 0, yDrift); - page.setTranslationY(0); - pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); - pageDrift.setInterpolator(decelerateInterpolator); - pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - mStateAnimation.play(pageDrift); - - page.setAlpha(1f); - ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f); - itemsAlpha.setDuration(100); - itemsAlpha.setInterpolator(decelerateInterpolator); - mStateAnimation.play(itemsAlpha); - } - - View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator); - pageIndicators.setAlpha(1f); - ObjectAnimator indicatorsAlpha = - LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f); - indicatorsAlpha.setDuration(revealDuration); - indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f)); - mStateAnimation.play(indicatorsAlpha); - - width = revealView.getMeasuredWidth(); - - if (material) { - if (!isWidgetTray) { - allAppsButton.setVisibility(View.INVISIBLE); - } - int allAppsButtonSize = LauncherAppState.getInstance(). - getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; - float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2; - Animator reveal = - LauncherAnimUtils.createCircularReveal(revealView, width / 2, - height / 2, revealRadius, finalRadius); - reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); - reveal.setDuration(revealDuration); - reveal.setStartDelay(itemsAlphaStagger); - - reveal.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - revealView.setVisibility(View.INVISIBLE); - if (!isWidgetTray) { - allAppsButton.setVisibility(View.VISIBLE); - } - } - }); - - mStateAnimation.play(reveal); - } - - dispatchOnLauncherTransitionPrepare(fromView, animated, true); - dispatchOnLauncherTransitionPrepare(toView, animated, true); - mAppsCustomizeContent.stopScrolling(); - } - - mStateAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - fromView.setVisibility(View.GONE); - dispatchOnLauncherTransitionEnd(fromView, animated, true); - dispatchOnLauncherTransitionEnd(toView, animated, true); - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_NONE, null); - } - } - - content.setPageBackgroundsVisible(true); - // Unhide side pages - int count = content.getChildCount(); - for (int i = 0; i < count; i++) { - View child = content.getChildAt(i); - child.setVisibility(View.VISIBLE); - } - - // Reset page transforms - if (page != null) { - page.setTranslationX(0); - page.setTranslationY(0); - page.setAlpha(1); - } - content.setCurrentPage(content.getNextPage()); - - mAppsCustomizeContent.updateCurrentPageScroll(); - - // This can hold unnecessary references to views. - mStateAnimation = null; - } - }); - - final AnimatorSet stateAnimation = mStateAnimation; - final Runnable startAnimRunnable = new Runnable() { - public void run() { - // Check that mStateAnimation hasn't changed while - // we waited for a layout/draw pass - if (mStateAnimation != stateAnimation) - return; - dispatchOnLauncherTransitionStart(fromView, animated, false); - dispatchOnLauncherTransitionStart(toView, animated, false); - - for (View v : layerViews.keySet()) { - if (layerViews.get(v) == BUILD_AND_SET_LAYER) { - v.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - } - - if (Utilities.isLmpOrAbove()) { - for (View v : layerViews.keySet()) { - if (Utilities.isViewAttachedToWindow(v)) v.buildLayer(); - } - } - mStateAnimation.start(); - } - }; - fromView.post(startAnimRunnable); - } else { - fromView.setVisibility(View.GONE); - dispatchOnLauncherTransitionPrepare(fromView, animated, true); - dispatchOnLauncherTransitionStart(fromView, animated, true); - dispatchOnLauncherTransitionEnd(fromView, animated, true); - dispatchOnLauncherTransitionPrepare(toView, animated, true); - dispatchOnLauncherTransitionStart(toView, animated, true); - dispatchOnLauncherTransitionEnd(toView, animated, true); - } - } - @Override public void onTrimMemory(int level) { super.onTrimMemory(level); @@ -3829,19 +3273,24 @@ public class Launcher extends Activity } } - protected void showWorkspace(boolean animated) { - showWorkspace(animated, null); + @Override + public void onStateTransitionHideSearchBar() { + // Hide the search bar + if (mSearchDropTargetBar != null) { + mSearchDropTargetBar.hideSearchBar(false /* animated */); + } } - protected void showWorkspace() { - showWorkspace(true); + protected void showWorkspace(boolean animated) { + showWorkspace(animated, null); } void showWorkspace(boolean animated, Runnable onCompleteRunnable) { if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) { boolean wasInSpringLoadedMode = (mState != State.WORKSPACE); mWorkspace.setVisibility(View.VISIBLE); - hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable); + mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL, + animated, onCompleteRunnable); // Show the search bar (only animate if we were showing the drop target bar in spring // loaded mode) @@ -3860,7 +3309,7 @@ public class Launcher extends Activity // Resume the auto-advance of widgets mUserPresent = true; - updateRunning(); + updateAutoAdvanceState(); // Send an accessibility event to announce the context change getWindow().getDecorView() @@ -3871,7 +3320,8 @@ public class Launcher extends Activity void showOverviewMode(boolean animated) { mWorkspace.setVisibility(View.VISIBLE); - hideAppsCustomizeHelper(Workspace.State.OVERVIEW, animated, false, null); + mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW, + animated, null /* onCompleteRunnable */); mState = State.WORKSPACE; onWorkspaceShown(animated); } @@ -3879,14 +3329,24 @@ public class Launcher extends Activity public void onWorkspaceShown(boolean animated) { } - void showAllApps(boolean animated, AppsCustomizePagedView.ContentType contentType, - boolean resetPageToZero) { - if (mState != State.WORKSPACE) return; + /** + * Shows the apps view. + */ + void showAppsView(boolean animated, boolean resetListToTop) { + if (resetListToTop) { + mAppsView.scrollToTop(); + } + showAppsOrWidgets(animated, State.APPS); + } + /** + * Shows the widgets view. + */ + void showWidgetsView(boolean animated, boolean resetPageToZero) { if (resetPageToZero) { mAppsCustomizeTabHost.reset(); } - showAppsCustomizeHelper(animated, false, contentType); + showAppsOrWidgets(animated, State.WIDGETS); mAppsCustomizeTabHost.post(new Runnable() { @Override public void run() { @@ -3894,13 +3354,27 @@ public class Launcher extends Activity mAppsCustomizeTabHost.requestFocus(); } }); + } + + /** + * Sets up the transition to show the apps/widgets view. + */ + private void showAppsOrWidgets(boolean animated, State toState) { + if (mState != State.WORKSPACE) return; + if (toState != State.APPS && toState != State.WIDGETS) return; + + if (toState == State.APPS) { + mStateTransitionAnimation.startAnimationToAllApps(animated); + } else { + mStateTransitionAnimation.startAnimationToWidgets(animated); + } // Change the state *after* we've called all the transition code - mState = State.APPS_CUSTOMIZE; + mState = toState; // Pause the auto-advance of widgets until we are out of AllApps mUserPresent = false; - updateRunning(); + updateAutoAdvanceState(); closeFolder(); // Send an accessibility event to announce the context change @@ -3909,15 +3383,19 @@ public class Launcher extends Activity } void enterSpringLoadedDragMode() { - if (isAllAppsVisible()) { - hideAppsCustomizeHelper(Workspace.State.SPRING_LOADED, true, true, null); - mState = State.APPS_CUSTOMIZE_SPRING_LOADED; + if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED || + mState == State.WIDGETS_SPRING_LOADED) { + return; } + + mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED, + true /* animated */, null /* onCompleteRunnable */); + mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; } void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { - if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return; + if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return; mHandler.postDelayed(new Runnable() { @Override @@ -3936,11 +3414,12 @@ public class Launcher extends Activity } void exitSpringLoadedDragMode() { - if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) { - final boolean animated = true; - final boolean springLoaded = true; - showAppsCustomizeHelper(animated, springLoaded); - mState = State.APPS_CUSTOMIZE; + if (mState == State.APPS_SPRING_LOADED) { + mStateTransitionAnimation.startAnimationToAllApps(true /* animated */); + mState = State.APPS; + } else if (mState == State.WIDGETS_SPRING_LOADED) { + mStateTransitionAnimation.startAnimationToWidgets(true /* animated */); + mState = State.WIDGETS; } // Otherwise, we are not in spring loaded mode, so don't do anything. } @@ -4018,8 +3497,10 @@ public class Launcher extends Activity final List text = event.getText(); text.clear(); // Populate event with a fake title based on the current state. - if (mState == State.APPS_CUSTOMIZE) { - text.add(mAppsCustomizeTabHost.getContentTag()); + if (mState == State.APPS) { + text.add("Apps"); + } else if (mState == State.WIDGETS) { + text.add("Widgets"); } else { text.add(getString(R.string.all_apps_home_button_label)); } @@ -4242,8 +3723,8 @@ public class Launcher extends Activity // Remove the extra empty screen mWorkspace.removeExtraEmptyScreen(false, false); - if (addedApps != null && mAppsCustomizeContent != null) { - mAppsCustomizeContent.addApps(addedApps); + if (addedApps != null && mAppsView != null) { + mAppsView.addApps(addedApps); } } @@ -4617,8 +4098,10 @@ public class Launcher extends Activity * Implementation of the method from LauncherModel.Callbacks. */ public void bindAllApplications(final ArrayList apps) { + if (mAppsView != null) { + mAppsView.setApps(apps); + } if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.setApps(apps); mAppsCustomizeContent.onPackagesUpdated( LauncherModel.getSortedWidgetsAndShortcuts(this)); } @@ -4642,8 +4125,8 @@ public class Launcher extends Activity return; } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.updateApps(apps); + if (mAppsView != null) { + mAppsView.updateApps(apps); } } @@ -4757,8 +4240,8 @@ public class Launcher extends Activity } // Update AllApps - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.removeApps(appInfos); + if (mAppsView != null) { + mAppsView.removeApps(appInfos); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index cb9d12e01..3cada6fb8 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3308,7 +3308,7 @@ public class LauncherModel extends BroadcastReceiver } } - // Returns a list of ResolveInfos/AppWindowInfos in sorted order + // Returns a list of ResolveInfos/AppWidgetInfos in sorted order public static ArrayList getSortedWidgetsAndShortcuts(Context context) { PackageManager packageManager = context.getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java new file mode 100644 index 000000000..484ed5c30 --- /dev/null +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -0,0 +1,832 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.TimeInterpolator; +import android.content.res.Resources; +import android.util.Log; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; + +import java.util.HashMap; + +/** + * TODO: figure out what kind of tests we can write for this + * + * Things to test when changing the following class. + * - Home from workspace + * - from center screen + * - from other screens + * - Home from all apps + * - from center screen + * - from other screens + * - Back from all apps + * - from center screen + * - from other screens + * - Launch app from workspace and quit + * - with back + * - with home + * - Launch app from all apps and quit + * - with back + * - with home + * - Go to a screen that's not the default, then all + * apps, and launch and app, and go back + * - with back + * -with home + * - On workspace, long press power and go back + * - with back + * - with home + * - On all apps, long press power and go back + * - with back + * - with home + * - On workspace, power off + * - On all apps, power off + * - Launch an app and turn off the screen while in that app + * - Go back with home key + * - Go back with back key TODO: make this not go to workspace + * - From all apps + * - From workspace + * - Enter and exit car mode (becuase it causes an extra configuration changed) + * - From all apps + * - From the center workspace + * - From another workspace + */ +public class LauncherStateTransitionAnimation { + + /** + * Callbacks made during the state transition + */ + interface Callbacks { + public void onStateTransitionHideSearchBar(); + } + + /** + * Private callbacks made during transition setup. + */ + static abstract class PrivateTransitionCallbacks { + void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {} + void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {} + float getMaterialRevealViewFinalAlpha(View revealView) { + return 0; + } + float getMaterialRevealViewFinalXDrift(View revealView) { + return 0; + } + float getMaterialRevealViewFinalYDrift(View revealView) { + return 0; + } + float getMaterialRevealViewStartFinalRadius() { + return 0; + } + AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView, + View allAppsButtonView) { + return null; + } + } + + public static final String TAG = "LauncherStateTransitionAnimation"; + + // Flags to determine how to set the layers on views before the transition animation + public static final int BUILD_LAYER = 0; + public static final int BUILD_AND_SET_LAYER = 1; + public static final int SINGLE_FRAME_DELAY = 16; + + private Launcher mLauncher; + private Callbacks mCb; + private AnimatorSet mStateAnimation; + + public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) { + mLauncher = l; + mCb = cb; + } + + /** + * Starts an animation to the apps view. + */ + public void startAnimationToAllApps(final boolean animated) { + final AppsContainerView toView = mLauncher.getAppsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + private int[] mAllAppsToPanelDelta; + + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + toView.setBackground(null); + // Get the y delta between the center of the page and the center of the all apps + // button + mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, + allAppsButtonView, null); + } + @Override + public float getMaterialRevealViewFinalAlpha(View revealView) { + return 1f; + } + @Override + public float getMaterialRevealViewFinalXDrift(View revealView) { + return mAllAppsToPanelDelta[0]; + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return mAllAppsToPanelDelta[1]; + } + @Override + public float getMaterialRevealViewStartFinalRadius() { + int allAppsButtonSize = LauncherAppState.getInstance(). + getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; + return allAppsButtonSize / 2; + } + @Override + public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( + final View revealView, final View allAppsButtonView) { + return new AnimatorListenerAdapter() { + public void onAnimationStart(Animator animation) { + allAppsButtonView.setVisibility(View.INVISIBLE); + } + public void onAnimationEnd(Animator animation) { + allAppsButtonView.setVisibility(View.VISIBLE); + } + }; + } + }; + startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(), + toView.getRevealView(), null, animated, cb); + } + + /** + * Starts an animation to the widgets view. + */ + public void startAnimationToWidgets(final boolean animated) { + final AppsCustomizeTabHost toView = mLauncher.getWidgetsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + // Hide the real page background, and swap in the fake one + ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false); + revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); + } + @Override + public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { + // Show the real page background + ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true); + } + @Override + public float getMaterialRevealViewFinalAlpha(View revealView) { + return 0.3f; + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return revealView.getMeasuredHeight() / 2; + } + }; + startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(), + toView.getRevealView(), toView.getPageIndicators(), animated, cb); + } + + /** + * Starts and animation to the workspace from the current overlay view. + */ + public void startAnimationToWorkspace(final Launcher.State fromState, + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { + if (toWorkspaceState != Workspace.State.NORMAL && + toWorkspaceState != Workspace.State.SPRING_LOADED && + toWorkspaceState != Workspace.State.OVERVIEW) { + Log.e(TAG, "Unexpected call to startAnimationToWorkspace"); + } + + if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { + startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, animated, + onCompleteRunnable); + } else { + startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, animated, + onCompleteRunnable); + } + } + + /** + * Creates and starts a new animation to a particular overlay view. + */ + private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView, + final View contentView, final View revealView, final View pageIndicatorsView, + final boolean animated, final PrivateTransitionCallbacks pCb) { + final Resources res = mLauncher.getResources(); + final boolean material = Utilities.isLmpOrAbove(); + final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final int itemsAlphaStagger = + res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); + + final View allAppsButtonView = mLauncher.getAllAppsButton(); + final View fromView = mLauncher.getWorkspace(); + + final HashMap layerViews = new HashMap<>(); + + // If for some reason our views aren't initialized, don't animate + boolean initialized = allAppsButtonView != null; + + // Cancel the current animation + cancelAnimation(); + + // Create the workspace animation. + // NOTE: this call apparently also sets the state for the workspace if !animated + Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( + toWorkspaceState, animated, layerViews); + + if (animated && initialized) { + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + + // Setup the reveal view animation + int width = revealView.getMeasuredWidth(); + int height = revealView.getMeasuredHeight(); + float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); + revealView.setVisibility(View.VISIBLE); + revealView.setAlpha(0f); + revealView.setTranslationY(0f); + revealView.setTranslationX(0f); + pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView); + + // Calculate the final animation values + final float revealViewToAlpha; + final float revealViewToXDrift; + final float revealViewToYDrift; + if (material) { + revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView); + revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView); + revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView); + } else { + revealViewToAlpha = 0f; + revealViewToYDrift = 2 * height / 3; + revealViewToXDrift = 0; + } + + // Create the animators + PropertyValuesHolder panelAlpha = + PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f); + PropertyValuesHolder panelDriftY = + PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0); + PropertyValuesHolder panelDriftX = + PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0); + ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView, + panelAlpha, panelDriftY, panelDriftX); + panelAlphaAndDrift.setDuration(revealDuration); + panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); + + // Play the animation + layerViews.put(revealView, BUILD_AND_SET_LAYER); + mStateAnimation.play(panelAlphaAndDrift); + + // Setup the animation for the page indicators + if (pageIndicatorsView != null) { + pageIndicatorsView.setAlpha(0.01f); + ObjectAnimator indicatorsAlpha = + ObjectAnimator.ofFloat(pageIndicatorsView, "alpha", 1f); + indicatorsAlpha.setDuration(revealDuration); + mStateAnimation.play(indicatorsAlpha); + } + + // Setup the animation for the content view + contentView.setVisibility(View.VISIBLE); + contentView.setAlpha(0f); + contentView.setTranslationY(revealViewToYDrift); + layerViews.put(contentView, BUILD_AND_SET_LAYER); + + // Create the individual animators + ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY", + revealViewToYDrift, 0); + pageDrift.setDuration(revealDuration); + pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); + pageDrift.setStartDelay(itemsAlphaStagger); + mStateAnimation.play(pageDrift); + + ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f); + itemsAlpha.setDuration(revealDuration); + itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); + itemsAlpha.setStartDelay(itemsAlphaStagger); + mStateAnimation.play(itemsAlpha); + + if (material) { + // Animate the all apps button + float startRadius = pCb.getMaterialRevealViewStartFinalRadius(); + AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener( + revealView, allAppsButtonView); + Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2, + height / 2, startRadius, revealRadius); + reveal.setDuration(revealDuration); + reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); + if (listener != null) { + reveal.addListener(listener); + } + mStateAnimation.play(reveal); + } + + mStateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + + // Hide the reveal view + revealView.setVisibility(View.INVISIBLE); + pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); + + // Disable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + + // Hide the search bar + mCb.onStateTransitionHideSearchBar(); + + // This can hold unnecessary references to views. + mStateAnimation = null; + } + + }); + + // Play the workspace animation + if (workspaceAnim != null) { + mStateAnimation.play(workspaceAnim); + } + + // Dispatch the prepare transition signal + dispatchOnLauncherTransitionPrepare(fromView, animated, false); + dispatchOnLauncherTransitionPrepare(toView, animated, false); + + + final AnimatorSet stateAnimation = mStateAnimation; + final Runnable startAnimRunnable = new Runnable() { + public void run() { + // Check that mStateAnimation hasn't changed while + // we waited for a layout/draw pass + if (mStateAnimation != stateAnimation) + return; + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + + // Enable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + if (Utilities.isViewAttachedToWindow(v)) { + v.buildLayer(); + } + } + + // Focus the new view + toView.requestFocus(); + + mStateAnimation.start(); + } + }; + + toView.bringToFront(); + toView.setVisibility(View.VISIBLE); + toView.post(startAnimRunnable); + } else { + toView.setTranslationX(0.0f); + toView.setTranslationY(0.0f); + toView.setScaleX(1.0f); + toView.setScaleY(1.0f); + toView.setVisibility(View.VISIBLE); + toView.bringToFront(); + + // Show the content view + contentView.setVisibility(View.VISIBLE); + + // Hide the search bar + mCb.onStateTransitionHideSearchBar(); + + dispatchOnLauncherTransitionPrepare(fromView, animated, false); + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionPrepare(toView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + } + } + + /** + * Starts and animation to the workspace from the apps view. + */ + private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState, + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { + AppsContainerView appsView = mLauncher.getAppsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + int[] mAllAppsToPanelDelta; + + @Override + public void onRevealViewVisible(View revealView, View contentView, + View allAppsButtonView) { + // Get the y delta between the center of the page and the center of the all apps + // button + mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, + allAppsButtonView, null); + } + @Override + public float getMaterialRevealViewFinalXDrift(View revealView) { + return mAllAppsToPanelDelta[0]; + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return mAllAppsToPanelDelta[1]; + } + @Override + float getMaterialRevealViewFinalAlpha(View revealView) { + // No alpha anim from all apps + return 1f; + } + @Override + float getMaterialRevealViewStartFinalRadius() { + int allAppsButtonSize = LauncherAppState.getInstance(). + getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize; + return allAppsButtonSize / 2; + } + @Override + public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( + final View revealView, final View allAppsButtonView) { + return new AnimatorListenerAdapter() { + public void onAnimationStart(Animator animation) { + // We set the alpha instead of visibility to ensure that the focus does not + // get taken from the all apps view + allAppsButtonView.setVisibility(View.VISIBLE); + allAppsButtonView.setAlpha(0f); + } + public void onAnimationEnd(Animator animation) { + // Hide the reveal view + revealView.setVisibility(View.INVISIBLE); + + // Show the all apps button, and focus it + allAppsButtonView.setAlpha(1f); + } + }; + } + }; + startAnimationToWorkspaceFromOverlay(toWorkspaceState, appsView, appsView.getContentView(), + appsView.getRevealView(), null /* pageIndicatorsView */, animated, + onCompleteRunnable, cb); + } + + /** + * Starts and animation to the workspace from the widgets view. + */ + private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, + final Workspace.State toWorkspaceState, final boolean animated, + final Runnable onCompleteRunnable) { + AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView(); + PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { + @Override + public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { + AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); + + // Hide the real page background, and swap in the fake one + pagedView.stopScrolling(); + pagedView.setPageBackgroundsVisible(false); + revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); + + // Hide the side pages of the Widget tray to avoid some ugly edge cases + final View currentPage = pagedView.getPageAt(pagedView.getNextPage()); + int count = pagedView.getChildCount(); + for (int i = 0; i < count; i++) { + View child = pagedView.getChildAt(i); + if (child != currentPage) { + child.setVisibility(View.INVISIBLE); + } + } + } + @Override + public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { + AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); + + // Show the real page background and force-update the page + pagedView.setPageBackgroundsVisible(true); + pagedView.setCurrentPage(pagedView.getNextPage()); + pagedView.updateCurrentPageScroll(); + + // Unhide the side pages + int count = pagedView.getChildCount(); + for (int i = 0; i < count; i++) { + View child = pagedView.getChildAt(i); + child.setVisibility(View.VISIBLE); + } + } + @Override + public float getMaterialRevealViewFinalYDrift(View revealView) { + return revealView.getMeasuredHeight() / 2; + } + @Override + float getMaterialRevealViewFinalAlpha(View revealView) { + return 0.4f; + } + @Override + public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener( + final View revealView, final View allAppsButtonView) { + return new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + // Hide the reveal view + revealView.setVisibility(View.INVISIBLE); + } + }; + } + }; + startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView, + widgetsView.getContentView(), widgetsView.getRevealView(), + widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb); + } + + /** + * Creates and starts a new animation to the workspace. + */ + private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, + final View fromView, final View contentView, final View revealView, + final View pageIndicatorsView, final boolean animated, + final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { + final Resources res = mLauncher.getResources(); + final boolean material = Utilities.isLmpOrAbove(); + final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final int itemsAlphaStagger = + res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger); + + final View allAppsButtonView = mLauncher.getAllAppsButton(); + final View toView = mLauncher.getWorkspace(); + + final HashMap layerViews = new HashMap<>(); + + // If for some reason our views aren't initialized, don't animate + boolean initialized = allAppsButtonView != null; + + // Cancel the current animation + cancelAnimation(); + + // Create the workspace animation. + // NOTE: this call apparently also sets the state for the workspace if !animated + Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation( + toWorkspaceState, animated, layerViews); + + if (animated && initialized) { + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + + // Play the workspace animation + if (workspaceAnim != null) { + mStateAnimation.play(workspaceAnim); + } + + // hideAppsCustomizeHelper is called in some cases when it is already hidden + // don't perform all these no-op animations. In particularly, this was causing + // the all-apps button to pop in and out. + if (fromView.getVisibility() == View.VISIBLE) { + int width = revealView.getMeasuredWidth(); + int height = revealView.getMeasuredHeight(); + float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4); + revealView.setVisibility(View.VISIBLE); + revealView.setAlpha(1f); + revealView.setTranslationY(0); + layerViews.put(revealView, BUILD_AND_SET_LAYER); + pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView); + + // Calculate the final animation values + final float revealViewToXDrift; + final float revealViewToYDrift; + if (material) { + revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView); + revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView); + } else { + revealViewToYDrift = 2 * height / 3; + revealViewToXDrift = 0; + } + + // The vertical motion of the apps panel should be delayed by one frame + // from the conceal animation in order to give the right feel. We correspondingly + // shorten the duration so that the slide and conceal end at the same time. + TimeInterpolator decelerateInterpolator = material ? + new LogDecelerateInterpolator(100, 0) : + new DecelerateInterpolator(1f); + ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY", + 0, revealViewToYDrift); + panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); + panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); + panelDriftY.setInterpolator(decelerateInterpolator); + mStateAnimation.play(panelDriftY); + + ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX", + 0, revealViewToXDrift); + panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); + panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); + panelDriftX.setInterpolator(decelerateInterpolator); + mStateAnimation.play(panelDriftX); + + // Setup animation for the reveal panel alpha + final float revealViewToAlpha = !material ? 0f : + pCb.getMaterialRevealViewFinalAlpha(revealView); + if (revealViewToAlpha != 1f) { + ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha", + 1f, revealViewToAlpha); + panelAlpha.setDuration(material ? revealDuration : 150); + panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); + panelAlpha.setInterpolator(decelerateInterpolator); + mStateAnimation.play(panelAlpha); + } + + // Setup the animation for the content view + layerViews.put(contentView, BUILD_AND_SET_LAYER); + + // Create the individual animators + ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY", + 0, revealViewToYDrift); + contentView.setTranslationY(0); + pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); + pageDrift.setInterpolator(decelerateInterpolator); + pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); + mStateAnimation.play(pageDrift); + + contentView.setAlpha(1f); + ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f); + itemsAlpha.setDuration(100); + itemsAlpha.setInterpolator(decelerateInterpolator); + mStateAnimation.play(itemsAlpha); + + // Setup the page indicators animation + if (pageIndicatorsView != null) { + pageIndicatorsView.setAlpha(1f); + ObjectAnimator indicatorsAlpha = + LauncherAnimUtils.ofFloat(pageIndicatorsView, "alpha", 0f); + indicatorsAlpha.setDuration(revealDuration); + indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f)); + mStateAnimation.play(indicatorsAlpha); + } + + if (material) { + // Animate the all apps button + float finalRadius = pCb.getMaterialRevealViewStartFinalRadius(); + AnimatorListenerAdapter listener = + pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView); + Animator reveal = + LauncherAnimUtils.createCircularReveal(revealView, width / 2, + height / 2, revealRadius, finalRadius); + reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); + reveal.setDuration(revealDuration); + reveal.setStartDelay(itemsAlphaStagger); + if (listener != null) { + reveal.addListener(listener); + } + mStateAnimation.play(reveal); + } + + dispatchOnLauncherTransitionPrepare(fromView, animated, true); + dispatchOnLauncherTransitionPrepare(toView, animated, true); + } + + mStateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + fromView.setVisibility(View.GONE); + dispatchOnLauncherTransitionEnd(fromView, animated, true); + dispatchOnLauncherTransitionEnd(toView, animated, true); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + // Animation complete callback + pCb.onAnimationComplete(revealView, contentView, allAppsButtonView); + + // Disable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + + // Reset page transforms + if (contentView != null) { + contentView.setTranslationX(0); + contentView.setTranslationY(0); + contentView.setAlpha(1); + } + + // This can hold unnecessary references to views. + mStateAnimation = null; + } + }); + + final AnimatorSet stateAnimation = mStateAnimation; + final Runnable startAnimRunnable = new Runnable() { + public void run() { + // Check that mStateAnimation hasn't changed while + // we waited for a layout/draw pass + if (mStateAnimation != stateAnimation) + return; + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + + // Enable all necessary layers + for (View v : layerViews.keySet()) { + if (layerViews.get(v) == BUILD_AND_SET_LAYER) { + v.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + if (Utilities.isLmpOrAbove()) { + v.buildLayer(); + } + } + mStateAnimation.start(); + } + }; + fromView.post(startAnimRunnable); + } else { + fromView.setVisibility(View.GONE); + dispatchOnLauncherTransitionPrepare(fromView, animated, true); + dispatchOnLauncherTransitionStart(fromView, animated, true); + dispatchOnLauncherTransitionEnd(fromView, animated, true); + dispatchOnLauncherTransitionPrepare(toView, animated, true); + dispatchOnLauncherTransitionStart(toView, animated, true); + dispatchOnLauncherTransitionEnd(toView, animated, true); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + } + } + + + /** + * Dispatches the prepare-transition event to suitable views. + */ + void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated, + toWorkspace); + } + } + + /** + * Dispatches the start-transition event to suitable views. + */ + void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated, + toWorkspace); + } + + // Update the workspace transition step as well + dispatchOnLauncherTransitionStep(v, 0f); + } + + /** + * Dispatches the step-transition event to suitable views. + */ + void dispatchOnLauncherTransitionStep(View v, float t) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t); + } + } + + /** + * Dispatches the end-transition event to suitable views. + */ + void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) { + if (v instanceof LauncherTransitionable) { + ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated, + toWorkspace); + } + + // Update the workspace transition step as well + dispatchOnLauncherTransitionStep(v, 1f); + } + + /** + * Cancels the current animation. + */ + private void cancelAnimation() { + if (mStateAnimation != null) { + mStateAnimation.setDuration(0); + mStateAnimation.cancel(); + mStateAnimation = null; + } + } +} \ No newline at end of file diff --git a/src/com/android/launcher3/PagedViewWithDraggableItems.java b/src/com/android/launcher3/PagedViewWithDraggableItems.java index 0e593698d..f0743cf1c 100644 --- a/src/com/android/launcher3/PagedViewWithDraggableItems.java +++ b/src/com/android/launcher3/PagedViewWithDraggableItems.java @@ -109,7 +109,7 @@ public abstract class PagedViewWithDraggableItems extends PagedView // Return early if we are still animating the pages if (mNextPage != INVALID_PAGE) return false; // When we have exited all apps or are in transition, disregard long clicks - if (!mLauncher.isAllAppsVisible() || + if (!mLauncher.isWidgetsViewVisible() || mLauncher.getWorkspace().isSwitchingState()) return false; // Return if global dragging is not enabled if (!mLauncher.isDraggingEnabled()) return false; diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index d963f2db9..312814039 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -131,8 +131,8 @@ public class WidgetPreviewLoader { private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache(); private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache(); - private final HashMap> mLoadedPreviews = new HashMap>(); - private final ArrayList> mUnusedBitmaps = new ArrayList>(); + private final HashMap> mLoadedPreviews = new HashMap<>(); + private final ArrayList> mUnusedBitmaps = new ArrayList<>(); private final Context mContext; private final int mAppIconSize; diff --git a/src/com/android/launcher3/WidgetsContainerView.java b/src/com/android/launcher3/WidgetsContainerView.java new file mode 100644 index 000000000..d0dd733a6 --- /dev/null +++ b/src/com/android/launcher3/WidgetsContainerView.java @@ -0,0 +1,88 @@ +package com.android.launcher3; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +class SectionedWidgetsRow { + String section; + List> widgets; + + public SectionedWidgetsRow(String sc) { + section = sc; + } +} + +class SectionedWidgetsAlgorithm { + public List computeSectionedWidgetRows(List sortedWidgets, + int widgetsPerRow) { + List rows = new ArrayList<>(); + LinkedHashMap> sections = computeSectionedApps(sortedWidgets); + for (Map.Entry> sectionEntry : sections.entrySet()) { + String section = sectionEntry.getKey(); + SectionedWidgetsRow row = new SectionedWidgetsRow(section); + List widgets = sectionEntry.getValue(); + int numRows = (int) Math.ceil((float) widgets.size() / widgetsPerRow); + for (int i = 0; i < numRows; i++) { + List widgetsInRow = new ArrayList<>(); + int offset = i * widgetsPerRow; + for (int j = 0; j < widgetsPerRow; j++) { + widgetsInRow.add(widgets.get(offset + j)); + } + row.widgets.add(widgetsInRow); + } + } + return rows; + } + + private LinkedHashMap> computeSectionedApps(List sortedWidgets) { + LinkedHashMap> sections = new LinkedHashMap<>(); + for (Object info : sortedWidgets) { + String section = getSection(info); + List sectionedWidgets = sections.get(section); + if (sectionedWidgets == null) { + sectionedWidgets = new ArrayList<>(); + sections.put(section, sectionedWidgets); + } + sectionedWidgets.add(info); + } + return sections; + } + + private String getSection(Object widgetOrShortcut) { + return "UNKNOWN"; + } +} + +/** + * The widgets list view container. + */ +public class WidgetsContainerView extends FrameLayout { + + + public WidgetsContainerView(Context context) { + this(context, null); + } + + public WidgetsContainerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + } +} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 3b293f95b..a59e25e08 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1528,7 +1528,7 @@ public class Workspace extends SmoothPagedView @Override public void announceForAccessibility(CharSequence text) { // Don't announce if apps is on top of us. - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isAppsViewVisible()) { super.announceForAccessibility(text); } } @@ -1816,7 +1816,7 @@ public class Workspace extends SmoothPagedView @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isAppsViewVisible()) { final Folder openFolder = getOpenFolder(); if (openFolder != null) { return openFolder.requestFocus(direction, previouslyFocusedRect); @@ -1837,7 +1837,7 @@ public class Workspace extends SmoothPagedView @Override public void addFocusables(ArrayList views, int direction, int focusableMode) { - if (!mLauncher.isAllAppsVisible()) { + if (!mLauncher.isAppsViewVisible()) { final Folder openFolder = getOpenFolder(); if (openFolder != null) { openFolder.addFocusables(views, direction); @@ -2046,8 +2046,7 @@ public class Workspace extends SmoothPagedView // If this is a text view, use its drawable instead if (v instanceof TextView) { - TextView tv = (TextView) v; - Drawable d = tv.getCompoundDrawables()[1]; + Drawable d = getTextViewIcon((TextView) v); Rect bounds = getDrawableBounds(d); bmpWidth = bounds.width(); bmpHeight = bounds.height(); @@ -2348,7 +2347,7 @@ public class Workspace extends SmoothPagedView cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); } else { if (layerViews != null) { - layerViews.put(cl, Launcher.BUILD_LAYER); + layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER); } if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { LauncherViewPropertyAnimator alphaAnim = @@ -2400,8 +2399,8 @@ public class Workspace extends SmoothPagedView if (layerViews != null) { // If layerViews is not null, we add these views, and indicate that // the caller can manage layer state. - layerViews.put(hotseat, Launcher.BUILD_AND_SET_LAYER); - layerViews.put(overviewPanel, Launcher.BUILD_AND_SET_LAYER); + layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); + layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); } else { // Otherwise let the animator handle layer management. hotseatAlpha.withLayer(); @@ -2430,7 +2429,7 @@ public class Workspace extends SmoothPagedView if (layerViews != null) { // If layerViews is not null, we add these views, and indicate that // the caller can manage layer state. - layerViews.put(searchBar, Launcher.BUILD_AND_SET_LAYER); + layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); } else { // Otherwise let the animator handle layer management. searchBarAlpha.withLayer(); @@ -2584,6 +2583,19 @@ public class Workspace extends SmoothPagedView } /** + * Returns the drawable for the given text view. + */ + public static Drawable getTextViewIcon(TextView tv) { + final Drawable[] drawables = tv.getCompoundDrawables(); + for (int i = 0; i < drawables.length; i++) { + if (drawables[i] != null) { + return drawables[i]; + } + } + return null; + } + + /** * Draw the View v into the given Canvas. * * @param v the view to draw @@ -2598,7 +2610,7 @@ public class Workspace extends SmoothPagedView destCanvas.save(); if (v instanceof TextView) { - Drawable d = ((TextView) v).getCompoundDrawables()[1]; + Drawable d = getTextViewIcon((TextView) v); Rect bounds = getDrawableBounds(d); clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding); destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top); @@ -2635,7 +2647,7 @@ public class Workspace extends SmoothPagedView int padding = expectedPadding.get(); if (v instanceof TextView) { - Drawable d = ((TextView) v).getCompoundDrawables()[1]; + Drawable d = getTextViewIcon((TextView) v); Rect bounds = getDrawableBounds(d); b = Bitmap.createBitmap(bounds.width() + padding, bounds.height() + padding, Bitmap.Config.ARGB_8888); @@ -2716,11 +2728,12 @@ public class Workspace extends SmoothPagedView beginDragShared(child, this, accessible); } - public void beginDragShared(View child, DragSource source) { - beginDragShared(child, source, false); + public void beginDragShared(View child, DragSource source, boolean accessible) { + beginDragShared(child, new Point(), source, accessible); } - public void beginDragShared(View child, DragSource source, boolean accessible) { + public void beginDragShared(View child, Point relativeTouchPos, DragSource source, + boolean accessible) { child.clearFocus(); child.setPressed(false); @@ -2745,11 +2758,23 @@ public class Workspace extends SmoothPagedView Point dragVisualizeOffset = null; Rect dragRect = null; if (child instanceof BubbleTextView) { + BubbleTextView icon = (BubbleTextView) child; int iconSize = grid.iconSizePx; int top = child.getPaddingTop(); int left = (bmpWidth - iconSize) / 2; int right = left + iconSize; int bottom = top + iconSize; + if (icon.isLayoutHorizontal()) { + // If the layout is horizontal, then if we are just picking up the icon, then just + // use the child position since the icon is top-left aligned. Otherwise, offset + // the drag layer position horizontally so that the icon is under the current + // touch position. + if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) { + dragLayerX = Math.round(mTempXY[0]); + } else { + dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2)); + } + } dragLayerY += top; // Note: The drag region is used to calculate drag layer offsets, but the // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. @@ -2831,18 +2856,6 @@ public class Workspace extends SmoothPagedView tmpB.recycle(); } - void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId, - int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) { - View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info); - - final int[] cellXY = new int[2]; - target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); - addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst); - - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0], - cellXY[1]); - } - public boolean transitionStateShouldAllowDrop() { return ((!isSwitchingState() || mTransitionProgress > 0.5f) && (mState == State.NORMAL || mState == State.SPRING_LOADED)); @@ -4769,7 +4782,7 @@ public class Workspace extends SmoothPagedView updates.contains(info)) { ShortcutInfo si = (ShortcutInfo) info; BubbleTextView shortcut = (BubbleTextView) v; - boolean oldPromiseState = shortcut.getCompoundDrawables()[1] + boolean oldPromiseState = getTextViewIcon(shortcut) instanceof PreloadIconDrawable; shortcut.applyFromShortcutInfo(si, mIconCache, true, si.isPromise() != oldPromiseState); -- 2.11.0