Merge "Using xml-drawable and elevation as folder background" into ub-launcher3-burnaby
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index e29cac5..a726cd8 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -43,7 +43,7 @@
android:paddingBottom="12dp"
android:contentDescription="@string/all_apps_button_label"
android:src="@drawable/ic_arrow_back_grey" />
- <EditText
+ <com.android.launcher3.AppsContainerSearchEditTextView
android:id="@+id/app_search_box"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -59,6 +59,7 @@
android:textColor="#4c4c4c"
android:textColorHint="#9c9c9c"
android:imeOptions="actionDone|flagNoExtractUi"
+ android:focusableInTouchMode="true"
android:background="@android:color/transparent" />
</LinearLayout>
<ImageView
diff --git a/res/values/config.xml b/res/values/config.xml
index 6ef8635..84ccef1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -102,4 +102,5 @@
<item type="id" name="action_move_to_workspace" />
<item type="id" name="action_move_screen_backwards" />
<item type="id" name="action_move_screen_forwards" />
+ <item type="id" name="action_resize" />
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5b5b5fc..4d33b88 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -50,8 +50,6 @@
<dimen name="apps_container_width">0dp</dimen>
<dimen name="apps_container_height">0dp</dimen>
<dimen name="apps_container_inset">8dp</dimen>
- <!-- Note: This needs to match the fixed insets for the search box -->
- <dimen name="apps_container_fixed_bounds_inset">8dp</dimen>
<dimen name="apps_grid_view_start_margin">52dp</dimen>
<dimen name="apps_grid_section_y_offset">8dp</dimen>
<dimen name="apps_view_row_height">64dp</dimen>
@@ -62,6 +60,9 @@
<dimen name="apps_view_fast_scroll_popup_size">64dp</dimen>
<dimen name="apps_view_fast_scroll_text_size">40dp</dimen>
+ <!-- Note: This needs to match the fixed insets for the search box. -->
+ <dimen name="container_fixed_bounds_inset">8dp</dimen>
+
<!-- AllApps/Customize/AppsCustomize -->
<dimen name="app_icon_size">48dp</dimen>
<dimen name="apps_customize_horizontal_padding">0dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1681fc6..5962584 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -252,4 +252,23 @@
<!-- Accessibility confirmation when a screen was moved [DO NOT TRANSLATE] -->
<string name="screen_moved">Screen moved</string>
+
+ <!-- Accessibility action to resize a widget [DO NOT TRANSLATE] -->
+ <string name="action_resize">Resize</string>
+
+ <!-- Accessibility action to increase width of a widget [DO NOT TRANSLATE] -->
+ <string name="action_increase_width">Increase width</string>
+
+ <!-- Accessibility action to increase height of a widget [DO NOT TRANSLATE] -->
+ <string name="action_increase_height">Increase height</string>
+
+ <!-- Accessibility action to decrease width of a widget [DO NOT TRANSLATE] -->
+ <string name="action_decrease_width">Decrease width</string>
+
+ <!-- Accessibility action to decrease height of a widget [DO NOT TRANSLATE] -->
+ <string name="action_decrease_height">Decrease height</string>
+
+ <!-- Accessibility confirmation for widget resize [DO NOT TRANSLATE]-->
+ <string name="widget_resized">Widget resized to width <xliff:g id="number" example="2">%1$s</xliff:g> height <xliff:g id="number" example="1">%2$s</xliff:g></string>
+
</resources>
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index 477c00f..70e36a7 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -82,17 +82,30 @@
* Info about a section in the alphabetic list
*/
public static class SectionInfo {
- // The name of this section
- public String sectionName;
// The number of applications in this section
- public int numAppsInSection;
- // The section AdapterItem for this section
- public AdapterItem sectionItem;
+ public int numApps;
+ // The section break AdapterItem for this section
+ public AdapterItem sectionBreakItem;
// The first app AdapterItem for this section
public AdapterItem firstAppItem;
+ }
- public SectionInfo(String name) {
- sectionName = name;
+ /**
+ * Info about a fast scroller section, depending if sections are merged, the fast scroller
+ * sections will not be the same set as the section headers.
+ */
+ public static class FastScrollSectionInfo {
+ // The section name
+ public String sectionName;
+ // To map the touch (from 0..1) to the index in the app list to jump to in the fast
+ // scroller, we use the fraction in range (0..1) of the app index / total app count.
+ public float appRangeFraction;
+ // The AdapterItem to scroll to for this section
+ public AdapterItem appItem;
+
+ public FastScrollSectionInfo(String sectionName, float appRangeFraction) {
+ this.sectionName = sectionName;
+ this.appRangeFraction = appRangeFraction;
}
}
@@ -100,32 +113,41 @@
* Info about a particular adapter item (can be either section or app)
*/
public static class AdapterItem {
+ /** Section & App properties */
// The index of this adapter item in the list
public int position;
// Whether or not the item at this adapter position is a section or not
public boolean isSectionHeader;
- // The name of this section, or the section that this app is contained in
- public String sectionName;
- // The associated AppInfo, or null if this adapter item is a section
- public AppInfo appInfo;
- // The index of this app (not including sections), or -1 if this adapter item is a section
- public int appIndex;
+ // The section for this item
+ public SectionInfo sectionInfo;
- public static AdapterItem asSection(int pos, String name) {
+ /** App-only properties */
+ // The section name of this app. Note that there can be multiple items with different
+ // sectionNames in the same section
+ public String sectionName = null;
+ // The index of this app in the section
+ public int sectionAppIndex = -1;
+ // The associated AppInfo for the app
+ public AppInfo appInfo = null;
+ // The index of this app not including sections
+ public int appIndex = -1;
+
+ public static AdapterItem asSectionBreak(int pos, SectionInfo section) {
AdapterItem item = new AdapterItem();
item.position = pos;
item.isSectionHeader = true;
- item.sectionName = name;
- item.appInfo = null;
- item.appIndex = -1;
+ item.sectionInfo = section;
return item;
}
- public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, int appIndex) {
+ public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
+ int sectionAppIndex, AppInfo appInfo, int appIndex) {
AdapterItem item = new AdapterItem();
item.position = pos;
item.isSectionHeader = false;
+ item.sectionInfo = section;
item.sectionName = sectionName;
+ item.sectionAppIndex = sectionAppIndex;
item.appInfo = appInfo;
item.appIndex = appIndex;
return item;
@@ -140,12 +162,13 @@
}
// The maximum number of rows allowed in a merged section before we stop merging
- private static final int MAX_ROWS_IN_MERGED_SECTION = Integer.MAX_VALUE;
+ private static final int MAX_ROWS_IN_MERGED_SECTION = 3;
private List<AppInfo> mApps = new ArrayList<>();
private List<AppInfo> mFilteredApps = new ArrayList<>();
private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
private List<SectionInfo> mSections = new ArrayList<>();
+ private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
private RecyclerView.Adapter mAdapter;
private Filter mFilter;
private AlphabeticIndexCompat mIndexer;
@@ -184,6 +207,13 @@
}
/**
+ * Returns fast scroller sections of all the current filtered applications.
+ */
+ public List<FastScrollSectionInfo> getFastScrollerSections() {
+ return mFastScrollerSections;
+ }
+
+ /**
* Returns the current filtered list of applications broken down into their sections.
*/
public List<AdapterItem> getAdapterItems() {
@@ -226,7 +256,6 @@
* Sets the current set of apps.
*/
public void setApps(List<AppInfo> apps) {
- Collections.sort(apps, mAppNameComparator.getComparator());
mApps.clear();
mApps.addAll(apps);
onAppsUpdated();
@@ -241,6 +270,8 @@
for (AppInfo info : apps) {
addApp(info);
}
+ onAppsUpdated();
+ mAdapter.notifyDataSetChanged();
}
/**
@@ -251,12 +282,12 @@
int index = mApps.indexOf(info);
if (index != -1) {
mApps.set(index, info);
- onAppsUpdated();
- mAdapter.notifyItemChanged(index);
} else {
addApp(info);
}
}
+ onAppsUpdated();
+ mAdapter.notifyDataSetChanged();
}
/**
@@ -267,10 +298,10 @@
int removeIndex = findAppByComponent(mApps, info);
if (removeIndex != -1) {
mApps.remove(removeIndex);
- onAppsUpdated();
- mAdapter.notifyDataSetChanged();
}
}
+ onAppsUpdated();
+ mAdapter.notifyDataSetChanged();
}
/**
@@ -290,14 +321,12 @@
}
/**
- * Implementation to actually add an app to the alphabetic list
+ * Implementation to actually add an app to the alphabetic list, but does not notify.
*/
private void addApp(AppInfo info) {
int index = Collections.binarySearch(mApps, info, mAppNameComparator.getComparator());
if (index < 0) {
mApps.add(-(index + 1), info);
- onAppsUpdated();
- mAdapter.notifyDataSetChanged();
}
}
@@ -305,13 +334,20 @@
* Updates internals when the set of apps are updated.
*/
private void onAppsUpdated() {
+ // Sort the list of apps
+ Collections.sort(mApps, mAppNameComparator.getComparator());
+
// Recreate the filtered and sectioned apps (for convenience for the grid layout)
mFilteredApps.clear();
mSections.clear();
mSectionedFilteredApps.clear();
+ mFastScrollerSections.clear();
SectionInfo lastSectionInfo = null;
+ String lastSectionName = null;
+ FastScrollSectionInfo lastFastScrollerSectionInfo = null;
int position = 0;
int appIndex = 0;
+ int numApps = mApps.size();
for (AppInfo info : mApps) {
String sectionName = mIndexer.computeSectionName(info.title.toString().trim());
@@ -321,24 +357,29 @@
}
// Create a new section if necessary
- if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) {
- lastSectionInfo = new SectionInfo(sectionName);
+ if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
+ lastSectionName = sectionName;
+ lastSectionInfo = new SectionInfo();
mSections.add(lastSectionInfo);
+ lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName,
+ (float) appIndex / numApps);
+ mFastScrollerSections.add(lastFastScrollerSectionInfo);
// Create a new section item, this item is used to break the flow of items in the
// list
- AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName);
+ AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
- lastSectionInfo.sectionItem = sectionItem;
+ lastSectionInfo.sectionBreakItem = sectionItem;
mSectionedFilteredApps.add(sectionItem);
}
}
// Create an app item
- AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
- lastSectionInfo.numAppsInSection++;
+ AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName,
+ lastSectionInfo.numApps++, info, appIndex++);
if (lastSectionInfo.firstAppItem == null) {
lastSectionInfo.firstAppItem = appItem;
+ lastFastScrollerSectionInfo.appItem = appItem;
}
mSectionedFilteredApps.add(appItem);
mFilteredApps.add(info);
@@ -350,40 +391,38 @@
int sectionAppCount = 0;
for (int i = 0; i < mSections.size(); i++) {
SectionInfo section = mSections.get(i);
- String mergedSectionName = section.sectionName;
- sectionAppCount = section.numAppsInSection;
+ sectionAppCount = section.numApps;
int mergeCount = 1;
+
// Merge rows if the last app in this section is in a column that is greater than
// 0, but less than the min number of apps per row. In addition, apply the
// constraint to stop merging if the number of rows in the section is greater than
// some limit, and also if there are no lessons to merge.
while (0 < (sectionAppCount % mNumAppsPerRow) &&
(sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow &&
- (int) Math.ceil(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
+ (sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
(i + 1) < mSections.size()) {
SectionInfo nextSection = mSections.remove(i + 1);
- // Merge the section names
- if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
- mergedSectionName += nextSection.sectionName;
- }
+
// Remove the next section break
- mSectionedFilteredApps.remove(nextSection.sectionItem);
- if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
- // Update the section names for the two sections
- int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
- for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
- AdapterItem item = mSectionedFilteredApps.get(j);
- item.sectionName = mergedSectionName;
- }
+ mSectionedFilteredApps.remove(nextSection.sectionBreakItem);
+ int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
+ // Point the section for these new apps to the merged section
+ int nextPos = pos + section.numApps;
+ for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) {
+ AdapterItem item = mSectionedFilteredApps.get(j);
+ item.sectionInfo = section;
+ item.sectionAppIndex += section.numApps;
}
- // Update the following adapter items of the removed section
- int pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
+
+ // Update the following adapter items of the removed section item
+ pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
for (int j = pos; j < mSectionedFilteredApps.size(); j++) {
AdapterItem item = mSectionedFilteredApps.get(j);
item.position--;
}
- section.numAppsInSection += nextSection.numAppsInSection;
- sectionAppCount += nextSection.numAppsInSection;
+ section.numApps += nextSection.numApps;
+ sectionAppCount += nextSection.numApps;
mergeCount++;
if (mergeCount >= mMaxAllowableMerges) {
break;
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index 7f64be2..6556cf9 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -30,23 +30,15 @@
import android.view.View;
import android.view.ViewConfiguration;
-import com.android.launcher3.util.Thunk;
-
import java.util.List;
/**
* A RecyclerView with custom fastscroll support. This is the main container for the all apps
* icons.
*/
-public class AppsContainerRecyclerView extends RecyclerView
- implements RecyclerView.OnItemTouchListener {
+public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f;
- private static final int SCROLL_DELTA_THRESHOLD = 4;
-
- /** Keeps the last known scrolling delta/velocity along y-axis. */
- @Thunk int mDy = 0;
- private float mDeltaThreshold;
private AlphabeticalAppsList mApps;
private int mNumAppsPerRow;
@@ -66,7 +58,6 @@
private int mScrollbarWidth;
private int mScrollbarMinHeight;
private int mScrollbarInset;
- private RecyclerView.OnScrollListener mScrollListenerProxy;
public AppsContainerRecyclerView(Context context) {
this(context, null);
@@ -100,21 +91,6 @@
mScrollbarInset =
res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset);
setFastScrollerAlpha(getFastScrollerAlpha());
- mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD;
-
- ScrollListener listener = new ScrollListener();
- setOnScrollListener(listener);
- }
-
- private class ScrollListener extends RecyclerView.OnScrollListener {
- public ScrollListener() {
- }
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- mDy = dy;
- mScrollListenerProxy.onScrolled(recyclerView, dx, dy);
- }
}
/**
@@ -132,13 +108,6 @@
}
/**
- * Sets an additional scroll listener, not necessary in master support lib.
- */
- public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) {
- mScrollListenerProxy = listener;
- }
-
- /**
* Sets the fast scroller alpha.
*/
public void setFastScrollerAlpha(float alpha) {
@@ -187,10 +156,6 @@
handleTouchEvent(ev);
}
- public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
- }
-
/**
* Handles the touch event and determines whether to show the fast scroller (or updates it if
* it is already showing).
@@ -206,8 +171,7 @@
// Keep track of the down positions
mDownX = mLastX = x;
mDownY = mLastY = y;
- if ((Math.abs(mDy) < mDeltaThreshold &&
- getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
+ if (shouldStopScroll(ev)) {
stopScroll();
}
break;
@@ -290,8 +254,9 @@
mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255));
mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0,
mFastScrollSectionName.length(), mFastScrollTextBounds);
+ float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName);
canvas.drawText(mFastScrollSectionName,
- (bgBounds.width() - mFastScrollTextBounds.width()) / 2,
+ (bgBounds.width() - textWidth) / 2,
bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2,
mFastScrollTextPaint);
canvas.restoreToCount(restoreCount);
@@ -321,38 +286,33 @@
}
/**
- * Maps the progress (from 0..1) to the position that should be visible
+ * Maps the touch (from 0..1) to the adapter position that should be visible.
*/
- private String scrollToPositionAtProgress(float progress) {
- List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
- if (sections.isEmpty()) {
+ private String scrollToPositionAtProgress(float touchFraction) {
+ // Ensure that we have any sections
+ List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
+ mApps.getFastScrollerSections();
+ if (fastScrollSections.isEmpty()) {
return "";
}
- // Find the position of the first application in the section that contains the row at the
- // current progress
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- int rowAtProgress = (int) (progress * getNumRows());
- int rowCount = 0;
- AlphabeticalAppsList.SectionInfo lastSectionInfo = null;
- for (AlphabeticalAppsList.SectionInfo section : sections) {
- int numRowsInSection = (int) Math.ceil((float) section.numAppsInSection / mNumAppsPerRow);
- if (rowCount + numRowsInSection >= rowAtProgress) {
- lastSectionInfo = section;
+ AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0);
+ for (int i = 1; i < fastScrollSections.size(); i++) {
+ AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i);
+ if (lastScrollSection.appRangeFraction <= touchFraction &&
+ touchFraction < scrollSection.appRangeFraction) {
break;
}
- rowCount += numRowsInSection;
+ lastScrollSection = scrollSection;
}
- int position = items.indexOf(lastSectionInfo.firstAppItem);
// Scroll the position into view, anchored at the top of the screen if possible. We call the
// scroll method on the LayoutManager directly since it is not exposed by RecyclerView.
LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
stopScroll();
- layoutManager.scrollToPositionWithOffset(position, 0);
+ layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0);
- // Return the section name of the row
- return lastSectionInfo.sectionName;
+ return lastScrollSection.sectionName;
}
/**
@@ -428,11 +388,11 @@
int appIndex = 0;
int rowCount = 0;
for (AlphabeticalAppsList.SectionInfo info : sections) {
- int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow);
- if (appIndex + info.numAppsInSection > position) {
+ int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
+ if (appIndex + info.numApps > position) {
return rowCount + ((position - appIndex) / mNumAppsPerRow);
}
- appIndex += info.numAppsInSection;
+ appIndex += info.numApps;
rowCount += numRowsInSection;
}
return appIndex;
@@ -445,7 +405,7 @@
List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
int rowCount = 0;
for (AlphabeticalAppsList.SectionInfo info : sections) {
- int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow);
+ int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
rowCount += numRowsInSection;
}
return rowCount;
diff --git a/src/com/android/launcher3/AppsContainerSearchEditTextView.java b/src/com/android/launcher3/AppsContainerSearchEditTextView.java
new file mode 100644
index 0000000..c688237
--- /dev/null
+++ b/src/com/android/launcher3/AppsContainerSearchEditTextView.java
@@ -0,0 +1,65 @@
+/*
+ * 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.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+
+/**
+ * The edit text for the search container
+ */
+public class AppsContainerSearchEditTextView extends EditText {
+
+ /**
+ * Implemented by listeners of the back key.
+ */
+ public interface OnBackKeyListener {
+ public void onBackKey();
+ }
+
+ private OnBackKeyListener mBackKeyListener;
+
+ public AppsContainerSearchEditTextView(Context context) {
+ this(context, null);
+ }
+
+ public AppsContainerSearchEditTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppsContainerSearchEditTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void setOnBackKeyListener(OnBackKeyListener listener) {
+ mBackKeyListener = listener;
+ }
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ // If this is a back key, propagate the key back to the listener
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+ if (mBackKeyListener != null) {
+ mBackKeyListener.onBackKey();
+ }
+ return false;
+ }
+ return super.onKeyPreIme(keyCode, event);
+ }
+}
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index 9122427..993f9c8 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -31,7 +31,6 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
-import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.util.Thunk;
@@ -40,21 +39,22 @@
/**
- * The all apps list view container.
+ * The all apps view container.
*/
-public class AppsContainerView extends FrameLayout implements DragSource, Insettable, TextWatcher,
- TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener,
+public class AppsContainerView extends BaseContainerView implements DragSource, Insettable,
+ TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener,
View.OnClickListener, View.OnLongClickListener {
public static final boolean GRID_MERGE_SECTIONS = true;
- public static final boolean GRID_MERGE_SECTION_HEADERS = false;
public static final boolean GRID_HIDE_SECTION_HEADERS = false;
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
private static final boolean DYNAMIC_HEADER_ELEVATION = false;
+ private static final boolean DISMISS_SEARCH_ON_BACK = true;
private static final float HEADER_ELEVATION_DP = 4;
private static final int FADE_IN_DURATION = 175;
- private static final int FADE_OUT_DURATION = 125;
+ private static final int FADE_OUT_DURATION = 100;
+ private static final int SEARCH_TRANSLATION_X_DP = 18;
@Thunk Launcher mLauncher;
@Thunk AlphabeticalAppsList mApps;
@@ -68,18 +68,14 @@
private View mSearchBarContainerView;
private View mSearchButtonView;
private View mDismissSearchButtonView;
- private EditText mSearchBarEditView;
+ private AppsContainerSearchEditTextView mSearchBarEditView;
private int mNumAppsPerRow;
private Point mLastTouchDownPos = new Point(-1, -1);
private Point mLastTouchPos = new Point();
- private Rect mInsets = new Rect();
- private Rect mFixedBounds = new Rect();
private int mContentMarginStart;
// Normal container insets
private int mContainerInset;
- // Fixed bounds container insets
- private int mFixedBoundsContainerInset;
// RecyclerView scroll position
@Thunk int mRecyclerViewScrollY;
@@ -99,8 +95,6 @@
mContainerInset = context.getResources().getDimensionPixelSize(
R.dimen.apps_container_inset);
- mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize(
- R.dimen.apps_container_fixed_bounds_inset);
mLauncher = (Launcher) context;
mNumAppsPerRow = grid.appsViewNumCols;
mApps = new AlphabeticalAppsList(context, mNumAppsPerRow);
@@ -146,8 +140,8 @@
*/
public void hideHeaderBar() {
mHeaderView.setVisibility(View.GONE);
- updateBackgrounds();
- updatePaddings();
+ onUpdateBackgrounds();
+ onUpdatePaddings();
}
/**
@@ -199,10 +193,19 @@
mSearchBarContainerView = findViewById(R.id.app_search_container);
mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
mDismissSearchButtonView.setOnClickListener(this);
- mSearchBarEditView = (EditText) findViewById(R.id.app_search_box);
+ mSearchBarEditView = (AppsContainerSearchEditTextView) findViewById(R.id.app_search_box);
if (mSearchBarEditView != null) {
mSearchBarEditView.addTextChangedListener(this);
mSearchBarEditView.setOnEditorActionListener(this);
+ if (DISMISS_SEARCH_ON_BACK) {
+ mSearchBarEditView.setOnBackKeyListener(
+ new AppsContainerSearchEditTextView.OnBackKeyListener() {
+ @Override
+ public void onBackKey() {
+ hideSearchField(true, true);
+ }
+ });
+ }
}
mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
mAppsRecyclerView.setApps(mApps);
@@ -225,46 +228,81 @@
if (mItemDecoration != null) {
mAppsRecyclerView.addItemDecoration(mItemDecoration);
}
- updateBackgrounds();
- updatePaddings();
+ onUpdateBackgrounds();
+ onUpdatePaddings();
}
@Override
- public void setInsets(Rect insets) {
- mInsets.set(insets);
- updatePaddings();
+ protected void onFixedBoundsUpdated() {
+ // Update the number of items in the grid
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) {
+ mNumAppsPerRow = grid.appsViewNumCols;
+ mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
+ mAdapter.setNumAppsPerRow(mNumAppsPerRow);
+ mApps.setNumAppsPerRow(mNumAppsPerRow);
+ }
}
/**
- * Sets the fixed bounds for this Apps view.
+ * Update the padding of the Apps view and children. To ensure that the RecyclerView has the
+ * full width to handle touches right to the edge of the screen, we only apply the top and
+ * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView
+ * itself. In particular, the left/right padding is applied to the background of the view,
+ * and then additionally inset by the start margin.
*/
- public void setFixedBounds(Context context, Rect fixedBounds) {
- if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) {
- // Update the number of items in the grid
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) {
- mNumAppsPerRow = grid.appsViewNumCols;
- mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
- mAdapter.setNumAppsPerRow(mNumAppsPerRow);
- mApps.setNumAppsPerRow(mNumAppsPerRow);
- }
+ @Override
+ protected void onUpdatePaddings() {
+ boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
+ LAYOUT_DIRECTION_RTL);
+ boolean hasSearchBar = (mSearchBarEditView != null) &&
+ (mSearchBarEditView.getVisibility() == View.VISIBLE);
- mFixedBounds.set(fixedBounds);
- if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
- mFixedBounds.top = mInsets.top;
- mFixedBounds.bottom = getMeasuredHeight();
- }
+ if (mFixedBounds.isEmpty()) {
+ // If there are no fixed bounds, then use the default padding and insets
+ setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right,
+ mContainerInset + mInsets.bottom);
+ } else {
+ // If there are fixed bounds, then we update the padding to reflect the fixed bounds.
+ setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
+ mInsets.bottom);
}
- // Post the updates since they can trigger a relayout, and this call can be triggered from
- // a layout pass itself.
- post(new Runnable() {
- @Override
- public void run() {
- updateBackgrounds();
- updatePaddings();
- }
- });
+
+ // Update the apps recycler view, inset it by the container inset as well
+ int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
+ if (isRtl) {
+ mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset);
+ } else {
+ mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
+ }
+
+ // Update the header bar
+ if (hasSearchBar) {
+ LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) mHeaderView.getLayoutParams();
+ lp.leftMargin = lp.rightMargin = inset;
+ }
+ }
+
+ /**
+ * Update the background of the Apps view and children.
+ */
+ @Override
+ protected void onUpdateBackgrounds() {
+ int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
+ boolean hasSearchBar = (mSearchBarEditView != null) &&
+ (mSearchBarEditView.getVisibility() == View.VISIBLE);
+
+ // Update the background of the reveal view and list to be inset with the fixed bound
+ // insets instead of the default insets
+ mAppsRecyclerView.setBackground(new InsetDrawable(
+ getContext().getResources().getDrawable(
+ hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg),
+ inset, 0, inset, 0));
+ getRevealView().setBackground(new InsetDrawable(
+ getContext().getResources().getDrawable(R.drawable.apps_reveal_bg),
+ inset, 0, inset, 0));
}
@Override
@@ -531,72 +569,20 @@
}
/**
- * Update the padding of the Apps view and children. To ensure that the RecyclerView has the
- * full width to handle touches right to the edge of the screen, we only apply the top and
- * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView
- * itself. In particular, the left/right padding is applied to the background of the view,
- * and then additionally inset by the start margin.
- */
- private void updatePaddings() {
- boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
- LAYOUT_DIRECTION_RTL);
- boolean hasSearchBar = (mSearchBarEditView != null) &&
- (mSearchBarEditView.getVisibility() == View.VISIBLE);
-
- if (mFixedBounds.isEmpty()) {
- // If there are no fixed bounds, then use the default padding and insets
- setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right,
- mContainerInset + mInsets.bottom);
- } else {
- // If there are fixed bounds, then we update the padding to reflect the fixed bounds.
- setPadding(mFixedBounds.left, mFixedBounds.top + mFixedBoundsContainerInset,
- getMeasuredWidth() - mFixedBounds.right,
- mInsets.bottom + mFixedBoundsContainerInset);
- }
-
- // Update the apps recycler view
- int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
- if (isRtl) {
- mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset);
- } else {
- mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
- }
-
- // Update the header
- if (hasSearchBar) {
- LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) mHeaderView.getLayoutParams();
- lp.leftMargin = lp.rightMargin = inset;
- }
- }
-
- /**
- * Update the background of the Apps view and children.
- */
- private void updateBackgrounds() {
- int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
- boolean hasSearchBar = (mSearchBarEditView != null) &&
- (mSearchBarEditView.getVisibility() == View.VISIBLE);
-
- // Update the background of the reveal view and list to be inset with the fixed bound
- // insets instead of the default insets
- mAppsRecyclerView.setBackground(new InsetDrawable(
- getContext().getResources().getDrawable(
- hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg),
- inset, 0, inset, 0));
- getRevealView().setBackground(new InsetDrawable(
- getContext().getResources().getDrawable(R.drawable.apps_reveal_bg),
- inset, 0, inset, 0));
- }
-
- /**
* Shows the search field.
*/
private void showSearchField() {
// Show the search bar and focus the search
+ final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+ getContext().getResources().getDisplayMetrics());
mSearchBarContainerView.setVisibility(View.VISIBLE);
mSearchBarContainerView.setAlpha(0f);
- mSearchBarContainerView.animate().alpha(1f).setDuration(FADE_IN_DURATION).withLayer()
+ mSearchBarContainerView.setTranslationX(translationX);
+ mSearchBarContainerView.animate()
+ .alpha(1f)
+ .translationX(0)
+ .setDuration(FADE_IN_DURATION)
+ .withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -605,38 +591,57 @@
InputMethodManager.SHOW_IMPLICIT);
}
});
- mSearchButtonView.animate().alpha(0f).setDuration(FADE_OUT_DURATION).withLayer();
+ mSearchButtonView.animate()
+ .alpha(0f)
+ .translationX(-translationX)
+ .setDuration(FADE_OUT_DURATION)
+ .withLayer();
}
/**
* Hides the search field.
*/
private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
+ final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
+ final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
+ getContext().getResources().getDisplayMetrics());
if (animated) {
// Hide the search bar and focus the recycler view
- mSearchBarContainerView.animate().alpha(0f).setDuration(FADE_IN_DURATION).withLayer()
+ mSearchBarContainerView.animate()
+ .alpha(0f)
+ .translationX(0)
+ .setDuration(FADE_IN_DURATION)
+ .withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
mSearchBarContainerView.setVisibility(View.INVISIBLE);
- mSearchBarEditView.setText("");
+ if (resetTextField) {
+ mSearchBarEditView.setText("");
+ }
mApps.setFilter(null);
if (returnFocusToRecyclerView) {
mAppsRecyclerView.requestFocus();
}
- scrollToTop();
}
});
- mSearchButtonView.animate().alpha(1f).setDuration(FADE_OUT_DURATION).withLayer();
+ mSearchButtonView.setTranslationX(-translationX);
+ mSearchButtonView.animate()
+ .alpha(1f)
+ .translationX(0)
+ .setDuration(FADE_OUT_DURATION)
+ .withLayer();
} else {
mSearchBarContainerView.setVisibility(View.INVISIBLE);
- mSearchBarEditView.setText("");
+ if (resetTextField) {
+ mSearchBarEditView.setText("");
+ }
mApps.setFilter(null);
mSearchButtonView.setAlpha(1f);
+ mSearchButtonView.setTranslationX(0f);
if (returnFocusToRecyclerView) {
mAppsRecyclerView.requestFocus();
}
- scrollToTop();
}
getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
}
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 62d9129..259740c 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -4,11 +4,10 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -84,7 +83,7 @@
private static final boolean FADE_OUT_SECTIONS = false;
- private HashMap<String, Point> mCachedSectionBounds = new HashMap<>();
+ private HashMap<String, PointF> mCachedSectionBounds = new HashMap<>();
private Rect mTmpBounds = new Rect();
@Override
@@ -94,75 +93,75 @@
}
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- String lastSectionName = null;
- int appIndexInSection = 0;
+ int childCount = parent.getChildCount();
int lastSectionTop = 0;
int lastSectionHeight = 0;
- for (int i = 0; i < parent.getChildCount(); i++) {
+ for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child);
if (shouldDrawItemSection(holder, child, i, items)) {
- int cellTopOffset = (2 * child.getPaddingTop());
+ // At this point, we only draw sections for each section break;
+ int viewTopOffset = (2 * child.getPaddingTop());
int pos = holder.getPosition();
AlphabeticalAppsList.AdapterItem item = items.get(pos);
- if (!item.sectionName.equals(lastSectionName)) {
- lastSectionName = item.sectionName;
+ AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo;
+
+ // Draw all the sections for this index
+ String lastSectionName = item.sectionName;
+ for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) {
+ AlphabeticalAppsList.AdapterItem nextItem = items.get(pos);
+ String sectionName = nextItem.sectionName;
+ if (nextItem.sectionInfo != sectionInfo) {
+ break;
+ }
+ if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) {
+ continue;
+ }
// Find the section code points
- String sectionBegin = null;
- String sectionEnd = null;
- int charOffset = 0;
- while (charOffset < item.sectionName.length()) {
- int codePoint = item.sectionName.codePointAt(charOffset);
- int codePointSize = Character.charCount(codePoint);
- if (charOffset == 0) {
- // The first code point
- sectionBegin = item.sectionName.substring(charOffset, charOffset + codePointSize);
- } else if ((charOffset + codePointSize) >= item.sectionName.length()) {
- // The last code point
- sectionEnd = item.sectionName.substring(charOffset, charOffset + codePointSize);
- }
- charOffset += codePointSize;
+ PointF sectionBounds = getAndCacheSectionBounds(sectionName);
+
+ // Calculate where to draw the section
+ int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
+ int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
+ mPaddingStart;
+ x += (int) ((mStartMargin - sectionBounds.x) / 2f);
+ int y = child.getTop() + sectionBaseline;
+
+ // Determine whether this is the last row with apps in that section, if
+ // so, then fix the section to the row allowing it to scroll past the
+ // baseline, otherwise, bound it to the baseline so it's in the viewport
+ int appIndexInSection = items.get(pos).sectionAppIndex;
+ int nextRowPos = Math.min(items.size() - 1,
+ pos + mAppsPerRow - (appIndexInSection % mAppsPerRow));
+ AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos);
+ boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName);
+ if (!fixedToRow) {
+ y = Math.max(sectionBaseline, y);
}
- Point sectionBeginBounds = getAndCacheSectionBounds(sectionBegin);
- int minTop = cellTopOffset + sectionBeginBounds.y;
- int top = child.getTop() + cellTopOffset + sectionBeginBounds.y;
- int left = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
- mPaddingStart;
- int col = appIndexInSection % mAppsPerRow;
- int nextRowPos = Math.min(pos - col + mAppsPerRow, items.size() - 1);
- int alpha = 255;
- boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(item.sectionName);
- if (fixedToRow) {
- alpha = Math.min(255, (int) (255 * (Math.max(0, top) / (float) minTop)));
- } else {
- // If we aren't fixed to the current row, then bound into the viewport
- top = Math.max(minTop, top);
+ // In addition, if it overlaps with the last section that was drawn, then
+ // offset it so that it does not overlap
+ if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) {
+ y += lastSectionTop - y + lastSectionHeight;
}
- if (lastSectionHeight > 0 && top <= (lastSectionTop + lastSectionHeight)) {
- top += lastSectionTop - top + lastSectionHeight;
- }
+
+ // Draw the section header
if (FADE_OUT_SECTIONS) {
+ int alpha = 255;
+ if (fixedToRow) {
+ alpha = Math.min(255,
+ (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
+ }
mSectionTextPaint.setAlpha(alpha);
}
- if (sectionEnd != null) {
- Point sectionEndBounds = getAndCacheSectionBounds(sectionEnd);
- c.drawText(sectionBegin + "/" + sectionEnd,
- left + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, top,
- mSectionTextPaint);
- } else {
- c.drawText(sectionBegin, left + (mStartMargin - sectionBeginBounds.x) / 2, top,
- mSectionTextPaint);
- }
- lastSectionTop = top;
- lastSectionHeight = sectionBeginBounds.y + mSectionHeaderOffset;
+ c.drawText(sectionName, x, y, mSectionTextPaint);
+
+ lastSectionTop = y;
+ lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset);
+ lastSectionName = sectionName;
}
- }
- if (holder.mIsSectionHeader) {
- appIndexInSection = 0;
- } else {
- appIndexInSection++;
+ i += (sectionInfo.numApps - item.sectionAppIndex);
}
}
}
@@ -173,16 +172,22 @@
// Do nothing
}
- private Point getAndCacheSectionBounds(String sectionName) {
- Point bounds = mCachedSectionBounds.get(sectionName);
+ /**
+ * Given a section name, return the bounds of the given section name.
+ */
+ private PointF getAndCacheSectionBounds(String sectionName) {
+ PointF bounds = mCachedSectionBounds.get(sectionName);
if (bounds == null) {
mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds);
- bounds = new Point(mTmpBounds.width(), mTmpBounds.height());
+ bounds = new PointF(mSectionTextPaint.measureText(sectionName), mTmpBounds.height());
mCachedSectionBounds.put(sectionName, bounds);
}
return bounds;
}
+ /**
+ * Returns whether to draw the section for the given child.
+ */
private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex,
List<AlphabeticalAppsList.AdapterItem> items) {
// Ensure item is not already removed
@@ -201,19 +206,12 @@
}
// Ensure we have a holder position
int pos = holder.getPosition();
- if (pos < 0 || pos >= items.size()) {
+ if (pos <= 0 || pos >= items.size()) {
return false;
}
- // Ensure this is not a section header
- if (items.get(pos).isSectionHeader) {
- return false;
- }
- // Only draw the header for the first item in a section, or whenever the sub-sections
- // changes (if AppsContainerView.GRID_MERGE_SECTIONS is true, but
- // AppsContainerView.GRID_MERGE_SECTION_HEADERS is false)
+ // Draw the section header for the first item in each section
return (childIndex == 0) ||
- items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader ||
- (!items.get(pos - 1).sectionName.equals(items.get(pos).sectionName));
+ (items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader);
}
}
diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseContainerRecyclerView.java
new file mode 100644
index 0000000..5b30e3d
--- /dev/null
+++ b/src/com/android/launcher3/BaseContainerRecyclerView.java
@@ -0,0 +1,113 @@
+/*
+ * 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.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import com.android.launcher3.util.Thunk;
+
+/**
+ * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling
+ * velocity is below a predefined threshold.
+ */
+public class BaseContainerRecyclerView extends RecyclerView
+ implements RecyclerView.OnItemTouchListener {
+
+ private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
+
+ /** Keeps the last known scrolling delta/velocity along y-axis. */
+ @Thunk int mDy = 0;
+ private float mDeltaThreshold;
+ private RecyclerView.OnScrollListener mScrollListenerProxy;
+
+ public BaseContainerRecyclerView(Context context) {
+ this(context, null);
+ }
+
+ public BaseContainerRecyclerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BaseContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
+
+ ScrollListener listener = new ScrollListener();
+ setOnScrollListener(listener);
+ }
+
+ private class ScrollListener extends OnScrollListener {
+ public ScrollListener() {
+ // Do nothing
+ }
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ mDy = dy;
+ if (mScrollListenerProxy != null) {
+ mScrollListenerProxy.onScrolled(recyclerView, dx, dy);
+ }
+ }
+ }
+
+ /**
+ * Sets an additional scroll listener, only needed for LMR1 version of the support lib.
+ */
+ public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) {
+ mScrollListenerProxy = listener;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ addOnItemTouchListener(this);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
+ if (shouldStopScroll(ev)) {
+ stopScroll();
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
+ // Do nothing.
+ }
+
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
+ }
+
+ /**
+ * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
+ */
+ protected boolean shouldStopScroll(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ if ((Math.abs(mDy) < mDeltaThreshold &&
+ getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
+ // now the touch events are being passed to the {@link WidgetCell} until the
+ // touch sequence goes over the touch slop.
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
new file mode 100644
index 0000000..2a84432
--- /dev/null
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -0,0 +1,100 @@
+/*
+ * 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.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * A base container view, which supports resizing.
+ */
+public class BaseContainerView extends FrameLayout implements Insettable {
+
+ protected Rect mInsets = new Rect();
+ protected Rect mFixedBounds = new Rect();
+ protected int mFixedBoundsContainerInset;
+
+ public BaseContainerView(Context context) {
+ this(context, null);
+ }
+
+ public BaseContainerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize(
+ R.dimen.container_fixed_bounds_inset);
+ }
+
+ @Override
+ final public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ onUpdateBackgrounds();
+ onUpdatePaddings();
+ }
+
+ /**
+ * Sets the fixed bounds for this container view.
+ */
+ final public void setFixedBounds(Rect fixedBounds) {
+ if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) {
+ mFixedBounds.set(fixedBounds);
+ if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
+ mFixedBounds.top = mInsets.top;
+ mFixedBounds.bottom = getMeasuredHeight();
+ }
+ // To ensure that the child RecyclerView has the full width to handle touches right to
+ // the edge of the screen, we only apply the top and bottom padding to the bounds
+ mFixedBounds.inset(0, mFixedBoundsContainerInset);
+ onFixedBoundsUpdated();
+ }
+ // Post the updates since they can trigger a relayout, and this call can be triggered from
+ // a layout pass itself.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ onUpdateBackgrounds();
+ onUpdatePaddings();
+ }
+ });
+ }
+
+ /**
+ * Update the UI in response to a change in the fixed bounds.
+ */
+ protected void onFixedBoundsUpdated() {
+ // Do nothing
+ }
+
+ /**
+ * Update the paddings in response to a change in the bounds or insets.
+ */
+ protected void onUpdatePaddings() {
+ // Do nothing
+ }
+
+ /**
+ * Update the backgrounds in response to a change in the bounds or insets.
+ */
+ protected void onUpdateBackgrounds() {
+ // Do nothing
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 65c6702..72eabf1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -3051,4 +3051,21 @@
public boolean findVacantCell(int spanX, int spanY, int[] outXY) {
return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
}
+
+ public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
+ int x2 = x + spanX - 1;
+ int y2 = y + spanY - 1;
+ if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) {
+ return false;
+ }
+ for (int i = x; i <= x2; i++) {
+ for (int j = y; j <= y2; j++) {
+ if (mOccupied[i][j]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
}
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 91f97fa..2efdb06 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -38,7 +38,6 @@
import android.widget.FrameLayout;
import android.widget.TextView;
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -153,6 +152,14 @@
return false;
}
+ private boolean isEventOverDropTargetBar(MotionEvent ev) {
+ getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect);
+ if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
+ return true;
+ }
+ return false;
+ }
+
public void setBlockTouch(boolean block) {
mBlockTouches = block;
}
@@ -188,10 +195,16 @@
}
}
- getDescendantRectRelativeToSelf(currentFolder, hitRect);
if (!isEventOverFolder(currentFolder, ev)) {
- mLauncher.closeFolder();
- return true;
+ if (isInAccessibleDrag()) {
+ // Do not close the folder if in drag and drop.
+ if (!isEventOverDropTargetBar(ev)) {
+ return true;
+ }
+ } else {
+ mLauncher.closeFolder();
+ return true;
+ }
}
}
return false;
@@ -228,11 +241,12 @@
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (accessibilityManager.isTouchExplorationEnabled()) {
final int action = ev.getAction();
- boolean isOverFolder;
+ boolean isOverFolderOrSearchBar;
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
- isOverFolder = isEventOverFolder(currentFolder, ev);
- if (!isOverFolder) {
+ isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ if (!isOverFolderOrSearchBar) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
return true;
@@ -240,12 +254,13 @@
mHoverPointClosesFolder = false;
break;
case MotionEvent.ACTION_HOVER_MOVE:
- isOverFolder = isEventOverFolder(currentFolder, ev);
- if (!isOverFolder && !mHoverPointClosesFolder) {
+ isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
return true;
- } else if (!isOverFolder) {
+ } else if (!isOverFolderOrSearchBar) {
return true;
}
mHoverPointClosesFolder = false;
@@ -268,6 +283,12 @@
}
}
+ private boolean isInAccessibleDrag() {
+ LauncherAccessibilityDelegate delegate = LauncherAppState
+ .getInstance().getAccessibilityDelegate();
+ return delegate != null && delegate.isInAccessibleDrag();
+ }
+
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
@@ -275,6 +296,10 @@
if (child == currentFolder) {
return super.onRequestSendAccessibilityEvent(child, event);
}
+
+ if (isInAccessibleDrag() && child instanceof SearchDropTargetBar) {
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
// Skip propagating onRequestSendAccessibilityEvent all for other children
// when a folder is open
return false;
@@ -288,6 +313,10 @@
if (currentFolder != null) {
// Only add the folder as a child for accessibility when it is open
childrenForAccessibility.add(currentFolder);
+
+ if (isInAccessibleDrag()) {
+ childrenForAccessibility.add(mLauncher.getSearchBar());
+ }
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
}
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index d98b0eb..3e20375 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -285,6 +285,9 @@
for (int i = 0; i < mContent.getChildCount(); i++) {
mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
}
+
+ mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS :
+ IMPORTANT_FOR_ACCESSIBILITY_AUTO);
mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index a1c909a..211bbfe 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -504,6 +504,10 @@
}
}
+ public int getAllocatedContentSize() {
+ return mAllocatedContentSize;
+ }
+
/**
* Reorders the items such that the {@param empty} spot moves to {@param target}
*/
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1de383c..5645759 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -528,7 +528,8 @@
if (LOGD) {
Log.d(TAG, "onAllAppsBoundsChanged(Rect): " + bounds);
}
- mAppsView.setFixedBounds(Launcher.this, bounds);
+ mAppsView.setFixedBounds(bounds);
+ mWidgetsView.setFixedBounds(bounds);
}
@Override
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
index 8a9a050..3992e63 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -1,6 +1,9 @@
package com.android.launcher3;
import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.DialogInterface;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
@@ -28,6 +31,7 @@
private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
private static final int MOVE = R.id.action_move;
private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
+ private static final int RESIZE = R.id.action_resize;
public enum DragType {
ICON,
@@ -62,6 +66,8 @@
launcher.getText(R.string.action_move)));
mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE,
launcher.getText(R.string.action_move_to_workspace)));
+ mActions.put(RESIZE, new AccessibilityAction(RESIZE,
+ launcher.getText(R.string.action_resize)));
}
@Override
@@ -87,6 +93,10 @@
if (item.container >= 0) {
info.addAction(mActions.get(MOVE_TO_WORKSPACE));
+ } else if (item instanceof LauncherAppWidgetInfo) {
+ if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) {
+ info.addAction(mActions.get(RESIZE));
+ }
}
} if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
info.addAction(mActions.get(ADD_TO_WORKSPACE));
@@ -102,7 +112,7 @@
return super.performAccessibilityAction(host, action, args);
}
- public boolean performAction(View host, final ItemInfo item, int action) {
+ public boolean performAction(final View host, final ItemInfo item, int action) {
if (action == REMOVE) {
if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) {
announceConfirmation(R.string.item_removed);
@@ -167,10 +177,97 @@
announceConfirmation(R.string.item_moved);
}
});
+ } else if (action == RESIZE) {
+ final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
+ final ArrayList<Integer> actions = getSupportedResizeActions(host, info);
+ CharSequence[] labels = new CharSequence[actions.size()];
+ for (int i = 0; i < actions.size(); i++) {
+ labels[i] = mLauncher.getText(actions.get(i));
+ }
+
+ new AlertDialog.Builder(mLauncher)
+ .setTitle(R.string.action_resize)
+ .setItems(labels, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ performResizeAction(actions.get(which), host, info);
+ dialog.dismiss();
+ }
+ })
+ .show();
}
return false;
}
+ private ArrayList<Integer> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
+ AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
+ ArrayList<Integer> actions = new ArrayList<>();
+
+ CellLayout layout = (CellLayout) host.getParent().getParent();
+ if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
+ if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
+ layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
+ actions.add(R.string.action_increase_width);
+ }
+
+ if (info.spanX > info.minSpanX && info.spanX > 1) {
+ actions.add(R.string.action_decrease_width);
+ }
+ }
+
+ if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
+ if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
+ layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
+ actions.add(R.string.action_increase_height);
+ }
+
+ if (info.spanY > info.minSpanY && info.spanY > 1) {
+ actions.add(R.string.action_decrease_height);
+ }
+ }
+ return actions;
+ }
+
+ private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
+ CellLayout layout = (CellLayout) host.getParent().getParent();
+ layout.markCellsAsUnoccupiedForView(host);
+
+ if (action == R.string.action_increase_width) {
+ if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)
+ && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY))
+ || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) {
+ lp.cellX --;
+ info.cellX --;
+ }
+ lp.cellHSpan ++;
+ info.spanX ++;
+ } else if (action == R.string.action_decrease_width) {
+ lp.cellHSpan --;
+ info.spanX --;
+ } else if (action == R.string.action_increase_height) {
+ if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) {
+ lp.cellY --;
+ info.cellY --;
+ }
+ lp.cellVSpan ++;
+ info.spanY ++;
+ } else if (action == R.string.action_decrease_height) {
+ lp.cellVSpan --;
+ info.spanY --;
+ }
+
+ layout.markCellsAsOccupiedForView(host);
+ Rect sizeRange = new Rect();
+ AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange);
+ ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
+ sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
+ host.requestLayout();
+ LauncherModel.updateItemInDatabase(mLauncher, info);
+ announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
+ }
+
@Thunk void announceConfirmation(int resId) {
announceConfirmation(mLauncher.getResources().getString(resId));
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 7efdf32..e81c8c2 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2810,6 +2810,8 @@
} else {
mHandler.post(r);
}
+ loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks),
+ false /* refresh */);
}
private void loadAllApps() {
@@ -2871,8 +2873,6 @@
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAllApplications(added);
- loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks,
- true /* refresh */);
if (DEBUG_LOADERS) {
Log.d(TAG, "bound " + added.size() + " apps in "
+ (SystemClock.uptimeMillis() - bindTime) + "ms");
@@ -2885,6 +2885,8 @@
// Cleanup any data stored for a deleted user.
ManagedProfileHeuristic.processAllUsers(profiles, mContext);
+ loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks),
+ true /* refresh */);
if (DEBUG_LOADERS) {
Log.d(TAG, "Icons processed in "
+ (SystemClock.uptimeMillis() - loadTime) + "ms");
diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
index fc105b4..ff99890 100644
--- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
@@ -24,23 +24,29 @@
* Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder.
*/
public class FolderAccessibilityHelper extends DragAndDropAccessibilityDelegate {
+
+ /**
+ * 0-index position for the first cell in {@link #mView} in {@link #mParent}.
+ */
private final int mStartPosition;
+ private final FolderPagedView mParent;
+
public FolderAccessibilityHelper(CellLayout layout) {
super(layout);
- FolderPagedView parent = (FolderPagedView) layout.getParent();
+ mParent = (FolderPagedView) layout.getParent();
- int index = parent.indexOfChild(layout);
- mStartPosition = 1 + index * layout.getCountX() * layout.getCountY();
+ int index = mParent.indexOfChild(layout);
+ mStartPosition = index * layout.getCountX() * layout.getCountY();
}
@Override
protected int intersectsValidDropTarget(int id) {
- return id;
+ return Math.min(id, mParent.getAllocatedContentSize() - mStartPosition - 1);
}
@Override
protected String getLocationDescriptionForIconDrop(int id) {
- return mContext.getString(R.string.move_to_position, id + mStartPosition);
+ return mContext.getString(R.string.move_to_position, id + mStartPosition + 1);
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
index 65694bf..6f15324 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
@@ -17,26 +17,13 @@
package com.android.launcher3.widget;
import android.content.Context;
-import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.BaseContainerRecyclerView;
/**
* The widgets recycler view container.
- * <p>
- * Overwritten to NOT intercept a touch sequence that started when the {@link RecycleView}
- * scrolling slowing down below the internally defined threshold.
*/
-public class WidgetsContainerRecyclerView extends RecyclerView
- implements RecyclerView.OnItemTouchListener {
-
- private static final int SCROLL_DELTA_THRESHOLD = 4;
-
- /** Keeps the last known scrolling delta/velocity along y-axis. */
- @Thunk int mDy = 0;
- private float mDeltaThreshold;
+public class WidgetsContainerRecyclerView extends BaseContainerRecyclerView {
public WidgetsContainerRecyclerView(Context context) {
this(context, null);
@@ -48,47 +35,6 @@
public WidgetsContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD;
-
- ScrollListener listener = new ScrollListener();
- setOnScrollListener(listener);
}
- private class ScrollListener extends RecyclerView.OnScrollListener {
- public ScrollListener() {
- }
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- mDy = dy;
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- addOnItemTouchListener(this);
- }
-
- @Override
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if ((Math.abs(mDy) < mDeltaThreshold &&
- getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
- // now the touch events are being passed to the {@link WidgetCell} until the
- // touch sequence goes over the touch slop.
- stopScroll();
- }
- }
- return false;
- }
-
- @Override
- public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
- // Do nothing.
- }
-
- public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
- }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 22e29f3..439227f 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -28,10 +28,9 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;
-
+import com.android.launcher3.BaseContainerView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DragController;
@@ -55,8 +54,8 @@
/**
* The widgets list view container.
*/
-public class WidgetsContainerView extends FrameLayout implements Insettable,
- View.OnLongClickListener, View.OnClickListener, DragSource{
+public class WidgetsContainerView extends BaseContainerView
+ implements View.OnLongClickListener, View.OnClickListener, DragSource{
private static final String TAG = "WidgetsContainerView";
private static final boolean DEBUG = false;
@@ -129,6 +128,7 @@
});
mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
getPaddingBottom());
+ onUpdatePaddings();
}
//
@@ -364,13 +364,17 @@
// Container rendering related.
//
- /*
- * @see Insettable#setInsets(Rect)
- */
@Override
- public void setInsets(Rect insets) {
- setPadding(mPadding.left + insets.left, mPadding.top + insets.top,
- mPadding.right + insets.right, mPadding.bottom + insets.bottom);
+ protected void onUpdatePaddings() {
+ if (mFixedBounds.isEmpty()) {
+ // If there are no fixed bounds, then use the default padding and insets
+ setPadding(mPadding.left + mInsets.left, mPadding.top + mInsets.top,
+ mPadding.right + mInsets.right, mPadding.bottom + mInsets.bottom);
+ } else {
+ // If there are fixed bounds, then we update the padding to reflect the fixed bounds.
+ setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
+ mInsets.bottom);
+ }
}
/**