Merge "Fixing regression in AllApps being aggressively dismissed after returning to Launcher." into ub-launcher3-burnaby
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f43106f..b61b90c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -192,15 +192,6 @@
</intent-filter>
</receiver>
- <!-- New user initialization; set up initial wallpaper -->
- <receiver
- android:name="com.android.launcher3.UserInitializeReceiver"
- android:exported="false">
- <intent-filter>
- <action android:name="android.intent.action.USER_INITIALIZE" />
- </intent-filter>
- </receiver>
-
<receiver android:name="com.android.launcher3.StartupReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java
index 578b8ea..50f779a 100644
--- a/WallpaperPicker/src/com/android/launcher3/CropView.java
+++ b/WallpaperPicker/src/com/android/launcher3/CropView.java
@@ -21,7 +21,6 @@
import android.graphics.Point;
import android.graphics.RectF;
import android.util.AttributeSet;
-import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
@@ -300,12 +299,12 @@
adjustment[0] = (edges.right - getWidth()) / scale;
}
if (edges.top > 0) {
- adjustment[1] = FloatMath.ceil(edges.top / scale);
+ adjustment[1] = (float) Math.ceil(edges.top / scale);
} else if (edges.bottom < getHeight()) {
adjustment[1] = (edges.bottom - getHeight()) / scale;
}
for (int dim = 0; dim <= 1; dim++) {
- if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]);
+ if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]);
}
mInverseRotateMatrix.mapPoints(adjustment);
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index c49286a..9332091 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -137,39 +137,32 @@
public static class UriWallpaperInfo extends WallpaperTileInfo {
private Uri mUri;
- private boolean mFirstClick = true;
- @Thunk BitmapRegionTileSource.UriBitmapSource mBitmapSource;
public UriWallpaperInfo(Uri uri) {
mUri = uri;
}
@Override
public void onClick(final WallpaperPickerActivity a) {
- final Runnable onLoad;
- if (!mFirstClick) {
- onLoad = null;
- } else {
- mFirstClick = false;
- a.mSetWallpaperButton.setEnabled(false);
- onLoad = new Runnable() {
- public void run() {
- if (mBitmapSource != null &&
- mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
- a.selectTile(mView);
- a.mSetWallpaperButton.setEnabled(true);
- } else {
- ViewGroup parent = (ViewGroup) mView.getParent();
- if (parent != null) {
- parent.removeView(mView);
- Toast.makeText(a.getContext(), R.string.image_load_fail,
- Toast.LENGTH_SHORT).show();
- }
+ a.setWallpaperButtonEnabled(false);
+ final BitmapRegionTileSource.UriBitmapSource bitmapSource =
+ new BitmapRegionTileSource.UriBitmapSource(
+ a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+ a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
+
+ @Override
+ public void run() {
+ if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ a.selectTile(mView);
+ a.setWallpaperButtonEnabled(true);
+ } else {
+ ViewGroup parent = (ViewGroup) mView.getParent();
+ if (parent != null) {
+ parent.removeView(mView);
+ Toast.makeText(a.getContext(), R.string.image_load_fail,
+ Toast.LENGTH_SHORT).show();
}
}
- };
- }
- mBitmapSource = new BitmapRegionTileSource.UriBitmapSource(
- a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
- a.setCropViewTileSource(mBitmapSource, true, false, null, onLoad);
+ }
+ });
}
@Override
public void onSave(final WallpaperPickerActivity a) {
@@ -203,11 +196,18 @@
mThumb = thumb;
}
@Override
- public void onClick(WallpaperPickerActivity a) {
+ public void onClick(final WallpaperPickerActivity a) {
+ a.setWallpaperButtonEnabled(false);
BitmapRegionTileSource.UriBitmapSource bitmapSource =
new BitmapRegionTileSource.UriBitmapSource(a.getContext(),
Uri.fromFile(mFile), 1024);
- a.setCropViewTileSource(bitmapSource, false, true, null, null);
+ a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
+
+ @Override
+ public void run() {
+ a.setWallpaperButtonEnabled(true);
+ }
+ });
}
@Override
public void onSave(WallpaperPickerActivity a) {
@@ -234,6 +234,7 @@
}
@Override
public void onClick(final WallpaperPickerActivity a) {
+ a.setWallpaperButtonEnabled(false);
BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
new BitmapRegionTileSource.ResourceBitmapSource(
mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
@@ -248,7 +249,13 @@
wallpaperSize.x, wallpaperSize.y, false);
return wallpaperSize.x / crop.width();
}
- }, null);
+ }, new Runnable() {
+
+ @Override
+ public void run() {
+ a.setWallpaperButtonEnabled(true);
+ }
+ });
}
@Override
public void onSave(WallpaperPickerActivity a) {
@@ -420,7 +427,7 @@
}
return;
}
- mSetWallpaperButton.setEnabled(true);
+ setWallpaperButtonEnabled(true);
WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
selectTile(v);
@@ -639,6 +646,10 @@
};
}
+ public void setWallpaperButtonEnabled(boolean enabled) {
+ mSetWallpaperButton.setEnabled(enabled);
+ }
+
@Thunk void selectTile(View v) {
if (mSelectedTile != null) {
mSelectedTile.setSelected(false);
diff --git a/res/layout/page_indicator_marker.xml b/res/layout/page_indicator_marker.xml
index 686d275..564a958 100644
--- a/res/layout/page_indicator_marker.xml
+++ b/res/layout/page_indicator_marker.xml
@@ -16,8 +16,8 @@
<com.android.launcher3.PageIndicatorMarker
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="16dp"
- android:layout_height="16dp"
+ android:layout_width="12dp"
+ android:layout_height="12dp"
android:layout_gravity="center_vertical">
<ImageView
android:id="@+id/inactive"
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 7a4d5e8..cd3a051 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2008 The Android Open Source Project
+ 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.
@@ -34,30 +34,44 @@
android:layout_width="20dp"
android:layout_height="20dp" />
- <com.android.launcher3.FolderCellLayout
+ <com.android.launcher3.FolderPagedView
android:id="@+id/folder_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:cacheColorHint="#ff333333"
- android:hapticFeedbackEnabled="false" />
+ launcher:pageIndicator="@+id/folder_page_indicator" />
</FrameLayout>
- <com.android.launcher3.FolderEditText
- android:id="@+id/folder_name"
+ <LinearLayout
+ android:id="@+id/folder_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#00000000"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
- android:hint="@string/folder_hint_text"
- android:imeOptions="flagNoExtractUi"
- android:paddingBottom="@dimen/folder_name_padding"
- android:paddingTop="@dimen/folder_name_padding"
- android:singleLine="true"
- android:textColor="#ff777777"
- android:textColorHighlight="#ffCCCCCC"
- android:textColorHint="#ff808080"
- android:textCursorDrawable="@null"
- android:textSize="14sp" />
+ android:orientation="vertical" >
+
+ <include
+ android:id="@+id/folder_page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="12dp"
+ android:layout_gravity="center_horizontal"
+ layout="@layout/page_indicator" />
+
+ <com.android.launcher3.FolderEditText
+ android:id="@+id/folder_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:background="#00000000"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_horizontal"
+ android:hint="@string/folder_hint_text"
+ android:imeOptions="flagNoExtractUi"
+ android:paddingBottom="@dimen/folder_name_padding"
+ android:paddingTop="@dimen/folder_name_padding"
+ android:singleLine="true"
+ android:textColor="#ff777777"
+ android:textColorHighlight="#ffCCCCCC"
+ android:textColorHint="#ff808080"
+ android:textCursorDrawable="@null"
+ android:textSize="14sp" />
+ </LinearLayout>
</com.android.launcher3.Folder>
\ No newline at end of file
diff --git a/res/layout/user_folder_scroll.xml b/res/layout/user_folder_scroll.xml
deleted file mode 100644
index 12e5097..0000000
--- a/res/layout/user_folder_scroll.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/quantum_panel"
- android:orientation="vertical" >
-
- <FrameLayout
- android:id="@+id/folder_content_wrapper"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
-
- <com.android.launcher3.FocusIndicatorView
- android:id="@+id/focus_indicator"
- android:layout_width="20dp"
- android:layout_height="20dp" />
-
- <com.android.launcher3.FolderPagedView
- android:id="@+id/folder_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:pageIndicator="@+id/folder_page_indicator" />
- </FrameLayout>
-
- <LinearLayout
- android:id="@+id/folder_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingStart="12dp"
- android:paddingEnd="8dp" >
-
- <com.android.launcher3.FolderEditText
- android:id="@+id/folder_name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:background="#00000000"
- android:fontFamily="sans-serif-condensed"
- android:gravity="start"
- android:hint="@string/folder_hint_text"
- android:imeOptions="flagNoExtractUi"
- android:paddingBottom="@dimen/folder_name_padding"
- android:paddingTop="@dimen/folder_name_padding"
- android:singleLine="true"
- android:textColor="#ff777777"
- android:textColorHighlight="#ffCCCCCC"
- android:textColorHint="#ff808080"
- android:textCursorDrawable="@null"
- android:textSize="14sp" />
-
- <include
- android:id="@+id/folder_page_indicator"
- android:layout_width="wrap_content"
- android:layout_height="12dp"
- android:layout_gravity="top"
- android:layout_marginTop="5dp"
- layout="@layout/page_indicator" />
-
- <LinearLayout
- android:id="@+id/folder_sort"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- android:gravity="end|center_vertical" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:text="@string/sort_alphabetical"
- android:textColor="#ff777777"
- android:textSize="14sp" />
-
- <Switch
- android:id="@+id/folder_sort_switch"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:duplicateParentState="true"
- android:focusable="false" />
- </LinearLayout>
- </LinearLayout>
-
-</com.android.launcher3.Folder>
\ No newline at end of file
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 1286a62..a5b25aa 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -41,7 +41,7 @@
android:layout_weight="1"
android:gravity="start"
android:singleLine="true"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:textColor="#FFFFFFFF"
@@ -57,7 +57,6 @@
android:id="@+id/widget_dims"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:layout_weight="0"
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index c7863c7..017b450 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -52,6 +52,8 @@
android:paddingTop="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
+ android:singleLine="true"
+ android:ellipsize="end"
android:gravity="start|center_vertical"
android:textColor="@color/widgets_view_section_text_color"
android:background="@color/widget_text_panel"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1b58d75..52306e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -38,6 +38,8 @@
<string name="uid_name">Android Core Apps</string>
<!-- Default folder name -->
<string name="folder_name"></string>
+ <!-- Work folder name -->
+ <string name="work_folder_name">Work</string>
<!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
<string name="activity_not_found">App isn\'t installed.</string>
<!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 786f2ce..22fb6a0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -82,7 +82,7 @@
boolean isLandscape;
boolean isTablet;
boolean isLargeTablet;
- boolean isLayoutRtl;
+ public boolean isLayoutRtl;
boolean transposeLayoutWithOrientation;
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 327fac4..8791c89 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -38,16 +38,6 @@
}
/**
- * A keyboard listener we set on all the workspace icons.
- */
-class FolderKeyEventListener implements View.OnKeyListener {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- return FocusHelper.handleFolderKeyEvent(v, keyCode, event);
- }
-}
-
-/**
* A keyboard listener we set on all the hotseat buttons.
*/
class HotseatIconKeyEventListener implements View.OnKeyListener {
@@ -91,110 +81,120 @@
*/
public static class PagedViewKeyListener implements View.OnKeyListener {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent e) {
- boolean consume = FocusLogic.shouldConsume(keyCode);
- if (e.getAction() == KeyEvent.ACTION_UP) {
- return consume;
- }
- if (DEBUG) {
- Log.v(TAG, String.format("Handle ALL APPS keyevent=[%s].",
- KeyEvent.keyCodeToString(keyCode)));
- }
-
- // Initialize variables.
- ViewGroup parentLayout;
- ViewGroup itemContainer;
- int countX;
- int countY;
- if (v.getParent() instanceof ShortcutAndWidgetContainer) {
- itemContainer = (ViewGroup) v.getParent();
- parentLayout = (ViewGroup) itemContainer.getParent();
- countX = ((CellLayout) parentLayout).getCountX();
- countY = ((CellLayout) parentLayout).getCountY();
- } else {
- if (LauncherAppState.isDogfoodBuild()) {
- throw new IllegalStateException("Parent of the focused item is not supported.");
- } else {
- return false;
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent e) {
+ boolean consume = FocusLogic.shouldConsume(keyCode);
+ if (e.getAction() == KeyEvent.ACTION_UP) {
+ return consume;
}
- }
+ if (DEBUG) {
+ Log.v(TAG, String.format("Handle ALL APPS and Folders keyevent=[%s].",
+ KeyEvent.keyCodeToString(keyCode)));
+ }
- final int iconIndex = itemContainer.indexOfChild(v);
- final PagedView container = (PagedView) parentLayout.getParent();
- final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout));
- final int pageCount = container.getChildCount();
- ViewGroup newParent = null;
- View child = null;
- // TODO(hyunyoungs): this matrix is not applicable on the last page.
- int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true);
+ // Initialize variables.
+ ViewGroup parentLayout;
+ ViewGroup itemContainer;
+ int countX;
+ int countY;
+ if (v.getParent() instanceof ShortcutAndWidgetContainer) {
+ itemContainer = (ViewGroup) v.getParent();
+ parentLayout = (ViewGroup) itemContainer.getParent();
+ countX = ((CellLayout) parentLayout).getCountX();
+ countY = ((CellLayout) parentLayout).getCountY();
+ } else {
+ if (LauncherAppState.isDogfoodBuild()) {
+ throw new IllegalStateException("Parent of the focused item is not supported.");
+ } else {
+ return false;
+ }
+ }
- // Process focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
- iconIndex, pageIndex, pageCount);
- if (newIconIndex == FocusLogic.NOOP) {
- handleNoopKey(keyCode, v);
+ final int iconIndex = itemContainer.indexOfChild(v);
+ final PagedView container = (PagedView) parentLayout.getParent();
+ final int pageIndex = container.indexToPage(container.indexOfChild(parentLayout));
+ final int pageCount = container.getChildCount();
+ ViewGroup newParent = null;
+ View child = null;
+ // TODO(hyunyoungs): this matrix is not applicable on the last page.
+ int[][] matrix = FocusLogic.createFullMatrix(countX, countY, true);
+
+ // Process focus.
+ int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, matrix,
+ iconIndex, pageIndex, pageCount);
+ if (newIconIndex == FocusLogic.NOOP) {
+ handleNoopKey(keyCode, v);
+ return consume;
+ }
+ switch (newIconIndex) {
+ case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
+ case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
+ int newPageIndex = pageIndex - 1;
+ if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
+ newPageIndex = pageIndex + 1;
+ }
+ newParent = getAppsCustomizePage(container, newPageIndex);
+ if (newParent != null) {
+ int row = FocusLogic.findRow(matrix, iconIndex);
+ container.snapToPage(newPageIndex);
+ // no need to create a new matrix.
+ child = newParent.getChildAt(matrix[countX-1][row]);
+ }
+ break;
+ case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
+ newParent = getAppsCustomizePage(container, pageIndex - 1);
+ if (newParent != null) {
+ container.snapToPage(pageIndex - 1);
+ child = newParent.getChildAt(0);
+ }
+ break;
+ case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
+ newParent = getAppsCustomizePage(container, pageIndex - 1);
+ if (newParent != null) {
+ container.snapToPage(pageIndex - 1);
+ child = newParent.getChildAt(newParent.getChildCount() - 1);
+ }
+ break;
+ case FocusLogic.NEXT_PAGE_FIRST_ITEM:
+ newParent = getAppsCustomizePage(container, pageIndex + 1);
+ if (newParent != null) {
+ container.snapToPage(pageIndex + 1);
+ child = newParent.getChildAt(0);
+ }
+ break;
+ case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
+ case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
+ newPageIndex = pageIndex + 1;
+ if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
+ newPageIndex = pageIndex -1;
+ }
+ newParent = getAppsCustomizePage(container, newPageIndex);
+ if (newParent != null) {
+ container.snapToPage(newPageIndex);
+ int row = FocusLogic.findRow(matrix, iconIndex);
+ child = newParent.getChildAt(matrix[0][row]);
+ }
+ break;
+ case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
+ child = container.getChildAt(0);
+ break;
+ case FocusLogic.CURRENT_PAGE_LAST_ITEM:
+ child = itemContainer.getChildAt(itemContainer.getChildCount() - 1);
+ break;
+ default: // Go to some item on the current page.
+ child = itemContainer.getChildAt(newIconIndex);
+ break;
+ }
+ if (child != null) {
+ child.requestFocus();
+ playSoundEffect(keyCode, v);
+ } else {
+ handleNoopKey(keyCode, v);
+ }
return consume;
}
- switch (newIconIndex) {
- case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
- newParent = getAppsCustomizePage(container, pageIndex -1);
- if (newParent != null) {
- int row = FocusLogic.findRow(matrix, iconIndex);
- container.snapToPage(pageIndex - 1);
- // no need to create a new matrix.
- child = newParent.getChildAt(matrix[countX-1][row]);
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
- newParent = getAppsCustomizePage(container, pageIndex - 1);
- if (newParent != null) {
- container.snapToPage(pageIndex - 1);
- child = newParent.getChildAt(0);
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
- newParent = getAppsCustomizePage(container, pageIndex - 1);
- if (newParent != null) {
- container.snapToPage(pageIndex - 1);
- child = newParent.getChildAt(newParent.getChildCount() - 1);
- }
- break;
- case FocusLogic.NEXT_PAGE_FIRST_ITEM:
- newParent = getAppsCustomizePage(container, pageIndex + 1);
- if (newParent != null) {
- container.snapToPage(pageIndex + 1);
- child = newParent.getChildAt(0);
- }
- break;
- case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
- newParent = getAppsCustomizePage(container, pageIndex + 1);
- if (newParent != null) {
- container.snapToPage(pageIndex + 1);
- int row = FocusLogic.findRow(matrix, iconIndex);
- child = newParent.getChildAt(matrix[0][row]);
- }
- break;
- case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
- child = container.getChildAt(0);
- break;
- case FocusLogic.CURRENT_PAGE_LAST_ITEM:
- child = itemContainer.getChildAt(itemContainer.getChildCount() - 1);
- break;
- default: // Go to some item on the current page.
- child = itemContainer.getChildAt(newIconIndex);
- break;
- }
- if (child != null) {
- child.requestFocus();
- playSoundEffect(keyCode, v);
- } else {
- handleNoopKey(keyCode, v);
- }
- return consume;
- }
- public void handleNoopKey(int keyCode, View v) { }
+ public void handleNoopKey(int keyCode, View v) { }
}
/**
@@ -209,8 +209,7 @@
return consume;
}
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile profile = app.getDynamicGrid().getDeviceProfile();
+ DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
if (DEBUG) {
Log.v(TAG, String.format(
"Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
@@ -358,14 +357,19 @@
}
break;
case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
+ case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
+ int newPageIndex = pageIndex - 1;
+ if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
+ newPageIndex = pageIndex + 1;
+ }
int row = FocusLogic.findRow(matrix, iconIndex);
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+ parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
if (parent != null) {
iconLayout = (CellLayout) parent.getParent();
matrix = FocusLogic.createSparseMatrix(iconLayout,
iconLayout.getCountX(), row);
newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
- FocusLogic.PIVOT, pageIndex - 1, pageCount);
+ FocusLogic.PIVOT, newPageIndex, pageCount);
newIcon = parent.getChildAt(newIconIndex);
}
break;
@@ -385,13 +389,18 @@
workspace.snapToPage(pageIndex + 1);
break;
case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
+ case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
+ newPageIndex = pageIndex + 1;
+ if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
+ newPageIndex = pageIndex - 1;
+ }
row = FocusLogic.findRow(matrix, iconIndex);
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
+ parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
if (parent != null) {
iconLayout = (CellLayout) parent.getParent();
matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY, matrix,
- FocusLogic.PIVOT, pageIndex, pageCount);
+ FocusLogic.PIVOT, newPageIndex, pageCount);
newIcon = parent.getChildAt(newIconIndex);
}
break;
@@ -418,62 +427,6 @@
return consume;
}
- /**
- * Handles key events for items in a Folder.
- */
- static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) {
- boolean consume = FocusLogic.shouldConsume(keyCode);
- if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
- return consume;
- }
- if (DEBUG) {
- Log.v(TAG, String.format("Handle FOLDER keyevent=[%s].",
- KeyEvent.keyCodeToString(keyCode)));
- }
-
- // Initialize the variables.
- ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
- final CellLayout layout = (CellLayout) parent.getParent();
- final Folder folder = (Folder) layout.getParent().getParent();
- View title = folder.mFolderName;
- Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
- final int countX = layout.getCountX();
- final int countY = layout.getCountY();
- final int iconIndex = findIndexOfView(parent, v);
- int pageIndex = workspace.indexOfChild(layout);
- int pageCount = workspace.getChildCount();
- int[][] map = FocusLogic.createFullMatrix(countX, countY, true /* incremental order */);
-
- // Process the focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX, countY, map, iconIndex,
- pageIndex, pageCount);
- View newIcon = null;
- switch (newIconIndex) {
- case FocusLogic.NOOP:
- if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- newIcon = title;
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
- case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
- case FocusLogic.NEXT_PAGE_FIRST_ITEM:
- case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
- case FocusLogic.CURRENT_PAGE_LAST_ITEM:
- if (DEBUG) {
- Log.v(TAG, "Page advance handling not supported on folder icons.");
- }
- break;
- default: // current page some item.
- newIcon = parent.getChildAt(newIconIndex);
- break;
- }
- if (newIcon != null) {
- newIcon.requestFocus();
- playSoundEffect(keyCode, v);
- }
- return consume;
- }
-
//
// Helper methods.
//
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 1e1d1ee..0eb1fd8 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -69,7 +69,6 @@
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
*/
private static final int MIN_CONTENT_DIMEN = 5;
- private static final boolean ALLOW_FOLDER_SCROLL = true;
static final int STATE_NONE = -1;
static final int STATE_SMALL = 0;
@@ -101,6 +100,8 @@
private final Alarm mReorderAlarm = new Alarm();
private final Alarm mOnExitAlarm = new Alarm();
+ private final Alarm mOnScrollHintAlarm = new Alarm();
+ @Thunk final Alarm mScrollPauseAlarm = new Alarm();
@Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
@@ -116,7 +117,7 @@
@Thunk FolderIcon mFolderIcon;
- @Thunk FolderContent mContent;
+ @Thunk FolderPagedView mContent;
@Thunk View mContentWrapper;
FolderEditText mFolderName;
@@ -149,11 +150,6 @@
// Folder scrolling
private int mScrollAreaOffset;
- private Alarm mOnScrollHintAlarm;
- @Thunk Alarm mScrollPauseAlarm;
-
- // TODO: Use {@link #mContent} once {@link #ALLOW_FOLDER_SCROLL} is removed.
- @Thunk FolderPagedView mPagedView;
@Thunk int mScrollHintDir = DragController.SCROLL_NONE;
@Thunk int mCurrentScrollDir = DragController.SCROLL_NONE;
@@ -186,18 +182,13 @@
// name is complete, we have something to focus on, thus hiding the cursor and giving
// reliable behavior when clicking the text field (since it will always gain focus on click).
setFocusableInTouchMode(true);
-
- if (ALLOW_FOLDER_SCROLL) {
- mOnScrollHintAlarm = new Alarm();
- mScrollPauseAlarm = new Alarm();
- }
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mContentWrapper = findViewById(R.id.folder_content_wrapper);
- mContent = (FolderContent) findViewById(R.id.folder_content);
+ mContent = (FolderPagedView) findViewById(R.id.folder_content);
mContent.setFolder(this);
mFolderName = (FolderEditText) findViewById(R.id.folder_name);
@@ -211,16 +202,16 @@
mFolderName.setInputType(mFolderName.getInputType() |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
- mFooter = ALLOW_FOLDER_SCROLL ? findViewById(R.id.folder_footer) : mFolderName;
+ mFooter = findViewById(R.id.folder_footer);
+ updateFooterHeight();
+ }
+
+ public void updateFooterHeight() {
// We find out how tall footer wants to be (it is set to wrap_content), so that
// we can allocate the appropriate amount of space for it.
int measureSpec = MeasureSpec.UNSPECIFIED;
mFooter.measure(measureSpec, measureSpec);
mFooterHeight = mFooter.getMeasuredHeight();
-
- if (ALLOW_FOLDER_SCROLL) {
- mPagedView = (FolderPagedView) mContent;
- }
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@@ -398,8 +389,7 @@
* @return A new UserFolder.
*/
static Folder fromXml(Context context) {
- return (Folder) LayoutInflater.from(context).inflate(
- ALLOW_FOLDER_SCROLL ? R.layout.user_folder_scroll : R.layout.user_folder, null);
+ return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
}
/**
@@ -424,12 +414,10 @@
public void animateOpen() {
if (!(getParent() instanceof DragLayer)) return;
- if (ALLOW_FOLDER_SCROLL) {
- mPagedView.completePendingPageChanges();
- if (!(mDragInProgress && mPagedView.mIsSorted)) {
- // Open on the first page.
- mPagedView.snapToPageImmediately(0);
- }
+ mContent.completePendingPageChanges();
+ if (!(mDragInProgress && mContent.mIsSorted)) {
+ // Open on the first page.
+ mContent.snapToPageImmediately(0);
}
Animator openFolderAnim = null;
@@ -533,10 +521,8 @@
mDragController.forceTouchMove();
}
- if (ALLOW_FOLDER_SCROLL) {
- FolderPagedView pages = (FolderPagedView) mContent;
- pages.verifyVisibleHighResIcons(pages.getNextPage());
- }
+ FolderPagedView pages = (FolderPagedView) mContent;
+ pages.verifyVisibleHighResIcons(pages.getNextPage());
}
public void beginExternalDrag(ShortcutInfo item) {
@@ -544,7 +530,8 @@
mEmptyCellRank = mContent.allocateRankForNewItem(item);
mIsExternalDrag = true;
mDragInProgress = true;
- if (ALLOW_FOLDER_SCROLL && mPagedView.mIsSorted) {
+
+ if (mContent.mIsSorted) {
mScrollPauseAlarm.setOnAlarmListener(null);
mScrollPauseAlarm.cancelAlarm();
mScrollPauseAlarm.setAlarm(SORTED_STICKY_REORDER_DELAY);
@@ -601,11 +588,9 @@
public void onDragEnter(DragObject d) {
mPrevTargetRank = -1;
mOnExitAlarm.cancelAlarm();
- if (ALLOW_FOLDER_SCROLL) {
- // Get the area offset such that the folder only closes if half the drag icon width
- // is outside the folder area
- mScrollAreaOffset = d.dragView.getDragRegionWidth() / 2 - d.xOffset;
- }
+ // Get the area offset such that the folder only closes if half the drag icon width
+ // is outside the folder area
+ mScrollAreaOffset = d.dragView.getDragRegionWidth() / 2 - d.xOffset;
}
OnAlarmListener mReorderAlarmListener = new OnAlarmListener() {
@@ -632,7 +617,7 @@
}
@Thunk void onDragOver(DragObject d, int reorderDelay) {
- if (ALLOW_FOLDER_SCROLL && mScrollPauseAlarm.alarmPending()) {
+ if (mScrollPauseAlarm.alarmPending()) {
return;
}
final float[] r = new float[2];
@@ -645,27 +630,23 @@
mPrevTargetRank = mTargetRank;
}
- if (!ALLOW_FOLDER_SCROLL) {
- return;
- }
-
float x = r[0];
- int currentPage = mPagedView.getNextPage();
+ int currentPage = mContent.getNextPage();
- float cellOverlap = mPagedView.getCurrentCellLayout().getCellWidth()
+ float cellOverlap = mContent.getCurrentCellLayout().getCellWidth()
* ICON_OVERSCROLL_WIDTH_FACTOR;
boolean isOutsideLeftEdge = x < cellOverlap;
boolean isOutsideRightEdge = x > (getWidth() - cellOverlap);
- if (currentPage > 0 && (mPagedView.rtlLayout ? isOutsideRightEdge : isOutsideLeftEdge)) {
+ if (currentPage > 0 && (mContent.rtlLayout ? isOutsideRightEdge : isOutsideLeftEdge)) {
showScrollHint(DragController.SCROLL_LEFT, d);
- } else if (currentPage < (mPagedView.getPageCount() - 1)
- && (mPagedView.rtlLayout ? isOutsideLeftEdge : isOutsideRightEdge)) {
+ } else if (currentPage < (mContent.getPageCount() - 1)
+ && (mContent.rtlLayout ? isOutsideLeftEdge : isOutsideRightEdge)) {
showScrollHint(DragController.SCROLL_RIGHT, d);
} else {
mOnScrollHintAlarm.cancelAlarm();
if (mScrollHintDir != DragController.SCROLL_NONE) {
- mPagedView.clearScrollHint();
+ mContent.clearScrollHint();
mScrollHintDir = DragController.SCROLL_NONE;
}
}
@@ -674,7 +655,7 @@
private void showScrollHint(int direction, DragObject d) {
// Show scroll hint on the right
if (mScrollHintDir != direction) {
- mPagedView.showScrollHint(direction);
+ mContent.showScrollHint(direction);
mScrollHintDir = direction;
}
@@ -714,13 +695,11 @@
}
mReorderAlarm.cancelAlarm();
- if (ALLOW_FOLDER_SCROLL) {
- mOnScrollHintAlarm.cancelAlarm();
- mScrollPauseAlarm.cancelAlarm();
- if (mScrollHintDir != DragController.SCROLL_NONE) {
- mPagedView.clearScrollHint();
- mScrollHintDir = DragController.SCROLL_NONE;
- }
+ mOnScrollHintAlarm.cancelAlarm();
+ mScrollPauseAlarm.cancelAlarm();
+ if (mScrollHintDir != DragController.SCROLL_NONE) {
+ mContent.clearScrollHint();
+ mScrollHintDir = DragController.SCROLL_NONE;
}
}
@@ -1088,21 +1067,19 @@
};
}
- if (ALLOW_FOLDER_SCROLL) {
- // If the icon was dropped while the page was being scrolled, we need to compute
- // the target location again such that the icon is placed of the final page.
- if (!mPagedView.rankOnCurrentPage(mEmptyCellRank)) {
- // Reorder again.
- mTargetRank = getTargetRank(d, null);
+ // If the icon was dropped while the page was being scrolled, we need to compute
+ // the target location again such that the icon is placed of the final page.
+ if (!mContent.rankOnCurrentPage(mEmptyCellRank)) {
+ // Reorder again.
+ mTargetRank = getTargetRank(d, null);
- // Rearrange items immediately.
- mReorderAlarmListener.onAlarm(mReorderAlarm);
+ // Rearrange items immediately.
+ mReorderAlarmListener.onAlarm(mReorderAlarm);
- mOnScrollHintAlarm.cancelAlarm();
- mScrollPauseAlarm.cancelAlarm();
- }
- mPagedView.completePendingPageChanges();
+ mOnScrollHintAlarm.cancelAlarm();
+ mScrollPauseAlarm.cancelAlarm();
}
+ mContent.completePendingPageChanges();
View currentDragView;
ShortcutInfo si = mCurrentDragInfo;
@@ -1252,10 +1229,10 @@
@Override
public void onAlarm(Alarm alarm) {
if (mCurrentScrollDir == DragController.SCROLL_LEFT) {
- mPagedView.scrollLeft();
+ mContent.scrollLeft();
mScrollHintDir = DragController.SCROLL_NONE;
} else if (mCurrentScrollDir == DragController.SCROLL_RIGHT) {
- mPagedView.scrollRight();
+ mContent.scrollRight();
mScrollHintDir = DragController.SCROLL_NONE;
} else {
// This should not happen
@@ -1286,69 +1263,4 @@
onDragOver(mDragObject, 1);
}
}
-
- public static interface FolderContent {
- void setFolder(Folder f);
-
- void removeItem(View v);
-
- boolean isFull();
- int getItemCount();
-
- int getDesiredWidth();
- int getDesiredHeight();
- void setFixedSize(int width, int height);
-
- /**
- * Iterates over all its items in a reading order.
- * @return the view for which the operator returned true.
- */
- View iterateOverItems(ItemOperator op);
- View getLastItem();
-
- String getAccessibilityDescription();
-
- /**
- * Binds items to the layout.
- * @return list of items that could not be bound, probably because we hit the max size limit.
- */
- ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> children);
-
- /**
- * Create space for a new item, and returns the rank for that item.
- * Resizes the content if necessary.
- */
- int allocateRankForNewItem(ShortcutInfo info);
-
- View createAndAddViewForRank(ShortcutInfo item, int rank);
-
- /**
- * Adds the {@param view} to the layout based on {@param rank} and updated the position
- * related attributes. It assumes that {@param item} is already attached to the view.
- */
- void addViewForRank(View view, ShortcutInfo item, int rank);
-
- /**
- * Reorders the items such that the {@param empty} spot moves to {@param target}
- */
- void realTimeReorder(int empty, int target);
-
- /**
- * @return the rank of the cell nearest to the provided pixel position.
- */
- int findNearestArea(int pixelX, int pixelY);
-
- /**
- * Updates position and rank of all the children in the view based.
- * @param list the ordered list of children.
- * @param itemCount if greater than the total children count, empty spaces are left
- * at the end.
- */
- void arrangeChildren(ArrayList<View> list, int itemCount);
-
- /**
- * Sets the focus on the first visible child.
- */
- void setFocusOnFirstChild();
- }
}
diff --git a/src/com/android/launcher3/FolderCellLayout.java b/src/com/android/launcher3/FolderCellLayout.java
deleted file mode 100644
index 8585add..0000000
--- a/src/com/android/launcher3/FolderCellLayout.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.launcher3.Workspace.ItemOperator;
-
-import java.util.ArrayList;
-
-public class FolderCellLayout extends CellLayout implements Folder.FolderContent {
-
- private static final int REORDER_ANIMATION_DURATION = 230;
- private static final int START_VIEW_REORDER_DELAY = 30;
- private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
-
- private static final int[] sTempPosArray = new int[2];
-
- private final FolderKeyEventListener mKeyListener = new FolderKeyEventListener();
- private final LayoutInflater mInflater;
- private final IconCache mIconCache;
-
- private final int mMaxCountX;
- private final int mMaxCountY;
- private final int mMaxNumItems;
-
- // Indicates the last number of items used to set up the grid size
- private int mAllocatedContentSize;
-
- private Folder mFolder;
- private FocusIndicatorView mFocusIndicatorView;
-
- public FolderCellLayout(Context context) {
- this(context, null);
- }
-
- public FolderCellLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public FolderCellLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- mMaxCountX = (int) grid.numColumns;
- mMaxCountY = (int) grid.numRows;
- mMaxNumItems = mMaxCountX * mMaxCountY;
-
- mInflater = LayoutInflater.from(context);
- mIconCache = app.getIconCache();
-
- setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
- getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
- setInvertIfRtl(true);
- }
-
- @Override
- public void setFolder(Folder folder) {
- mFolder = folder;
- mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
- }
-
- /**
- * Sets up the grid size such that {@param count} items can fit in the grid.
- * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
- * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
- */
- private void setupContentDimensions(int count) {
- mAllocatedContentSize = count;
- int countX = getCountX();
- int countY = getCountY();
- boolean done = false;
-
- while (!done) {
- int oldCountX = countX;
- int oldCountY = countY;
- if (countX * countY < count) {
- // Current grid is too small, expand it
- if ((countX <= countY || countY == mMaxCountY) && countX < mMaxCountX) {
- countX++;
- } else if (countY < mMaxCountY) {
- countY++;
- }
- if (countY == 0) countY++;
- } else if ((countY - 1) * countX >= count && countY >= countX) {
- countY = Math.max(0, countY - 1);
- } else if ((countX - 1) * countY >= count) {
- countX = Math.max(0, countX - 1);
- }
- done = countX == oldCountX && countY == oldCountY;
- }
- setGridSize(countX, countY);
- }
-
- @Override
- public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
- ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>();
- setupContentDimensions(Math.min(items.size(), mMaxNumItems));
-
- int countX = getCountX();
- int rank = 0;
- for (ShortcutInfo item : items) {
- if (rank >= mMaxNumItems) {
- extra.add(item);
- continue;
- }
-
- item.rank = rank;
- item.cellX = rank % countX;
- item.cellY = rank / countX;
- addNewView(item);
- rank++;
- }
- return extra;
- }
-
- @Override
- public int allocateRankForNewItem(ShortcutInfo info) {
- int rank = getItemCount();
- mFolder.rearrangeChildren(rank + 1);
- return rank;
- }
-
- @Override
- public View createAndAddViewForRank(ShortcutInfo item, int rank) {
- updateItemXY(item, rank);
- return addNewView(item);
- }
-
- @Override
- public void addViewForRank(View view, ShortcutInfo item, int rank) {
- updateItemXY(item, rank);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
- lp.cellX = item.cellX;
- lp.cellY = item.cellY;
- addViewToCellLayout(view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
- }
-
- @Override
- public void removeItem(View v) {
- removeView(v);
- }
-
- /**
- * Updates the item cellX and cellY position
- */
- private void updateItemXY(ShortcutInfo item, int rank) {
- item.rank = rank;
- int countX = getCountX();
- item.cellX = rank % countX;
- item.cellY = rank / countX;
- }
-
- private View addNewView(ShortcutInfo item) {
- final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
- R.layout.folder_application, getShortcutsAndWidgets(), false);
- textView.applyFromShortcutInfo(item, mIconCache, false);
- textView.setOnClickListener(mFolder);
- textView.setOnLongClickListener(mFolder);
- textView.setOnFocusChangeListener(mFocusIndicatorView);
- textView.setOnKeyListener(mKeyListener);
-
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(
- item.cellX, item.cellY, item.spanX, item.spanY);
- addViewToCellLayout(textView, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
- return textView;
- }
-
- /**
- * Refer {@link #findNearestArea(int, int, int, int, View, boolean, int[])}
- */
- @Override
- public int findNearestArea(int pixelX, int pixelY) {
- findNearestArea(pixelX, pixelY, 1, 1, null, false, sTempPosArray);
- if (mFolder.isLayoutRtl()) {
- sTempPosArray[0] = getCountX() - sTempPosArray[0] - 1;
- }
-
- // Convert this position to rank.
- return Math.min(mAllocatedContentSize - 1,
- sTempPosArray[1] * getCountX() + sTempPosArray[0]);
- }
-
- @Override
- public boolean isFull() {
- return getItemCount() >= mMaxNumItems;
- }
-
- @Override
- public int getItemCount() {
- return getShortcutsAndWidgets().getChildCount();
- }
-
- @Override
- public void arrangeChildren(ArrayList<View> list, int itemCount) {
- setupContentDimensions(itemCount);
- removeAllViews();
-
- int newX, newY;
- int rank = 0;
- int countX = getCountX();
- for (View v : list) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
- newX = rank % countX;
- newY = rank / countX;
- ItemInfo info = (ItemInfo) v.getTag();
- if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
- info.cellX = newX;
- info.cellY = newY;
- info.rank = rank;
- LauncherModel.addOrMoveItemInDatabase(getContext(), info,
- mFolder.mInfo.id, 0, info.cellX, info.cellY);
- }
- lp.cellX = info.cellX;
- lp.cellY = info.cellY;
- rank ++;
- addViewToCellLayout(v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
- }
- }
-
- @Override
- public View iterateOverItems(ItemOperator op) {
- for (int j = 0; j < getCountY(); j++) {
- for (int i = 0; i < getCountX(); i++) {
- View v = getChildAt(i, j);
- if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) {
- return v;
- }
- }
- }
- return null;
- }
-
- @Override
- public String getAccessibilityDescription() {
- return String.format(getContext().getString(R.string.folder_opened),
- getCountX(), getCountY());
- }
-
- @Override
- public void setFocusOnFirstChild() {
- View firstChild = getChildAt(0, 0);
- if (firstChild != null) {
- firstChild.requestFocus();
- }
- }
-
- @Override
- public View getLastItem() {
- int lastRank = getShortcutsAndWidgets().getChildCount() - 1;
- // count can be zero if the folder is not yet laid out.
- int count = getCountX();
- if (count > 0) {
- return getShortcutsAndWidgets().getChildAt(lastRank % count, lastRank / count);
- } else {
- return getShortcutsAndWidgets().getChildAt(lastRank);
- }
- }
-
- @Override
- public void realTimeReorder(int empty, int target) {
- boolean wrap;
- int startX;
- int endX;
- int startY;
- int delay = 0;
- float delayAmount = START_VIEW_REORDER_DELAY;
-
- int countX = getCountX();
- int emptyX = empty % getCountX();
- int emptyY = empty / countX;
-
- int targetX = target % countX;
- int targetY = target / countX;
-
- if (target > empty) {
- wrap = emptyX == countX - 1;
- startY = wrap ? emptyY + 1 : emptyY;
- for (int y = startY; y <= targetY; y++) {
- startX = y == emptyY ? emptyX + 1 : 0;
- endX = y < targetY ? countX - 1 : targetX;
- for (int x = startX; x <= endX; x++) {
- View v = getChildAt(x,y);
- if (animateChildToPosition(v, emptyX, emptyY,
- REORDER_ANIMATION_DURATION, delay, true, true)) {
- emptyX = x;
- emptyY = y;
- delay += delayAmount;
- delayAmount *= VIEW_REORDER_DELAY_FACTOR;
- }
- }
- }
- } else {
- wrap = emptyX == 0;
- startY = wrap ? emptyY - 1 : emptyY;
- for (int y = startY; y >= targetY; y--) {
- startX = y == emptyY ? emptyX - 1 : countX - 1;
- endX = y > targetY ? 0 : targetX;
- for (int x = startX; x >= endX; x--) {
- View v = getChildAt(x,y);
- if (animateChildToPosition(v, emptyX, emptyY,
- REORDER_ANIMATION_DURATION, delay, true, true)) {
- emptyX = x;
- emptyY = y;
- delay += delayAmount;
- delayAmount *= VIEW_REORDER_DELAY_FACTOR;
- }
- }
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 3240cbf..80b1564 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -37,6 +37,11 @@
public static final int FLAG_ITEMS_SORTED = 0x00000001;
/**
+ * It is a work folder
+ */
+ public static final int FLAG_WORK_FOLDER = 0x00000002;
+
+ /**
* Whether this folder has been opened
*/
boolean opened;
@@ -46,11 +51,11 @@
/**
* The apps and shortcuts
*/
- ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>();
+ public ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>();
ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
- FolderInfo() {
+ public FolderInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
user = UserHandleCompat.myUserHandle();
}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index 1c42d25..6174892 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
-import android.util.LayoutDirection;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -42,10 +41,15 @@
import java.util.Iterator;
import java.util.Map;
-public class FolderPagedView extends PagedView implements Folder.FolderContent {
+public class FolderPagedView extends PagedView {
private static final String TAG = "FolderPagedView";
+ private static final boolean ALLOW_FOLDER_SCROLL = true;
+
+ // To enable this flag, user_folder.xml needs to be modified to add sort button.
+ private static final boolean ALLOW_ITEM_SORTING = false;
+
private static final int REORDER_ANIMATION_DURATION = 230;
private static final int START_VIEW_REORDER_DELAY = 30;
private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
@@ -96,34 +100,36 @@
setDataIsReady();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE);
- mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE);
+ if (ALLOW_FOLDER_SCROLL) {
+ mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE);
+ mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE);
+ } else {
+ mMaxCountX = (int) grid.numColumns;
+ mMaxCountY = (int) grid.numRows;
+ }
+
mMaxItemsPerPage = mMaxCountX * mMaxCountY;
mInflater = LayoutInflater.from(context);
mIconCache = app.getIconCache();
- rtlLayout = getResources().getConfiguration().getLayoutDirection() == LayoutDirection.RTL;
+ rtlLayout = getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL;
}
- @Override
public void setFolder(Folder folder) {
mFolder = folder;
mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
mKeyListener = new PagedFolderKeyEventListener(folder);
-
- mSortButton = folder.findViewById(R.id.folder_sort);
- mSortButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- onSortClicked();
- }
- });
mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
- mSortSwitch = (Switch) folder.findViewById(R.id.folder_sort_switch);
+
+ if (ALLOW_ITEM_SORTING) {
+ // Initialize {@link #mSortSwitch} and {@link #mSortButton}.
+ }
}
+ /**
+ * Called when sort button is clicked.
+ */
private void onSortClicked() {
if (mSortOperationPending) {
return;
@@ -138,9 +144,11 @@
private void setIsSorted(boolean isSorted, boolean saveChanges) {
mIsSorted = isSorted;
- mSortSwitch.setChecked(isSorted);
- mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted,
- saveChanges ? mFolder.mLauncher : null);
+ if (ALLOW_ITEM_SORTING) {
+ mSortSwitch.setChecked(isSorted);
+ mFolder.mInfo.setOption(FolderInfo.FLAG_ITEMS_SORTED, isSorted,
+ saveChanges ? mFolder.mLauncher : null);
+ }
}
/**
@@ -282,26 +290,34 @@
}
}
- @Override
+ /**
+ * Binds items to the layout.
+ * @return list of items that could not be bound, probably because we hit the max size limit.
+ */
public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
- mIsSorted = mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED);
+ mIsSorted = ALLOW_ITEM_SORTING && mFolder.mInfo.hasOption(FolderInfo.FLAG_ITEMS_SORTED);
ArrayList<View> icons = new ArrayList<View>();
+ ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>();
+
for (ShortcutInfo item : items) {
- icons.add(createNewView(item));
+ if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) {
+ extra.add(item);
+ } else {
+ icons.add(createNewView(item));
+ }
}
arrangeChildren(icons, icons.size(), false);
- return new ArrayList<ShortcutInfo>();
+ return extra;
}
/**
* Create space for a new item at the end, and returns the rank for that item.
* Also sets the current page to the last page.
*/
- @Override
public int allocateRankForNewItem(ShortcutInfo info) {
int rank = getItemCount();
ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
- if (mIsSorted) {
+ if (ALLOW_ITEM_SORTING && mIsSorted) {
View tmp = new View(getContext());
tmp.setTag(info);
int index = Collections.binarySearch(views, tmp, new ViewComparator());
@@ -321,14 +337,16 @@
return rank;
}
- @Override
public View createAndAddViewForRank(ShortcutInfo item, int rank) {
View icon = createNewView(item);
addViewForRank(icon, item, rank);
return icon;
}
- @Override
+ /**
+ * Adds the {@param view} to the layout based on {@param rank} and updated the position
+ * related attributes. It assumes that {@param item} is already attached to the view.
+ */
public void addViewForRank(View view, ShortcutInfo item, int rank) {
int pagePos = rank % mMaxItemsPerPage;
int pageNo = rank / mMaxItemsPerPage;
@@ -388,14 +406,12 @@
return page;
}
- @Override
public void setFixedSize(int width, int height) {
for (int i = getChildCount() - 1; i >= 0; i --) {
((CellLayout) getChildAt(i)).setFixedSize(width, height);
}
}
- @Override
public void removeItem(View v) {
for (int i = getChildCount() - 1; i >= 0; i --) {
getPageAt(i).removeView(v);
@@ -412,7 +428,6 @@
* at the end, otherwise it is ignored.
*
*/
- @Override
public void arrangeChildren(ArrayList<View> list, int itemCount) {
arrangeChildren(list, itemCount, true);
}
@@ -488,19 +503,26 @@
setCurrentPage(0);
}
- setIsSorted(isSorted, saveChanges);
+ setEnableOverscroll(getPageCount() > 1);
// Update footer
- if (getPageCount() > 1) {
- mPageIndicator.setVisibility(View.VISIBLE);
- mSortButton.setVisibility(View.VISIBLE);
- mFolder.mFolderName.setGravity(rtlLayout ? Gravity.RIGHT : Gravity.LEFT);
- setEnableOverscroll(true);
+ if (ALLOW_ITEM_SORTING) {
+ setIsSorted(isSorted, saveChanges);
+ if (getPageCount() > 1) {
+ mPageIndicator.setVisibility(View.VISIBLE);
+ mSortButton.setVisibility(View.VISIBLE);
+ mFolder.mFolderName.setGravity(rtlLayout ? Gravity.RIGHT : Gravity.LEFT);
+ } else {
+ mPageIndicator.setVisibility(View.GONE);
+ mSortButton.setVisibility(View.GONE);
+ mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL);
+ }
} else {
- mPageIndicator.setVisibility(View.GONE);
- mSortButton.setVisibility(View.GONE);
- mFolder.mFolderName.setGravity(Gravity.CENTER_HORIZONTAL);
- setEnableOverscroll(false);
+ int indicatorVisibility = mPageIndicator.getVisibility();
+ mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE);
+ if (indicatorVisibility != mPageIndicator.getVisibility()) {
+ mFolder.updateFooterHeight();
+ }
}
}
@@ -521,7 +543,6 @@
return getPageCount() > 0 ? getPageAt(0).getDesiredHeight() : 0;
}
- @Override
public int getItemCount() {
int lastPageIndex = getChildCount() - 1;
if (lastPageIndex < 0) {
@@ -532,7 +553,9 @@
+ lastPageIndex * mMaxItemsPerPage;
}
- @Override
+ /**
+ * @return the rank of the cell nearest to the provided pixel position.
+ */
public int findNearestArea(int pixelX, int pixelY) {
int pageIndex = getNextPage();
CellLayout page = getPageAt(pageIndex);
@@ -550,12 +573,10 @@
R.drawable.ic_pageindicator_default_folder);
}
- @Override
public boolean isFull() {
- return false;
+ return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
}
- @Override
public View getLastItem() {
if (getChildCount() < 1) {
return null;
@@ -569,7 +590,10 @@
}
}
- @Override
+ /**
+ * Iterates over all its items in a reading order.
+ * @return the view for which the operator returned true.
+ */
public View iterateOverItems(ItemOperator op) {
for (int k = 0 ; k < getChildCount(); k++) {
CellLayout page = getPageAt(k);
@@ -585,13 +609,14 @@
return null;
}
- @Override
public String getAccessibilityDescription() {
return String.format(getContext().getString(R.string.folder_opened),
mGridCountX, mGridCountY);
}
- @Override
+ /**
+ * Sets the focus on the first visible child.
+ */
public void setFocusOnFirstChild() {
View firstChild = getCurrentCellLayout().getChildAt(0, 0);
if (firstChild != null) {
@@ -605,7 +630,7 @@
if (mFolder != null) {
mFolder.updateTextViewFocus();
}
- if (mSortOperationPending && getNextPage() == 0) {
+ if (ALLOW_ITEM_SORTING && mSortOperationPending && getNextPage() == 0) {
post(new Runnable() {
@Override
@@ -680,7 +705,9 @@
}
}
- @Override
+ /**
+ * Reorders the items such that the {@param empty} spot moves to {@param target}
+ */
public void realTimeReorder(int empty, int target) {
completePendingPageChanges();
int delay = 0;
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index f6238da..48b38f1 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -283,8 +283,8 @@
}
ContentValues values = updateCacheAndGetContentValues(app);
mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values,
- IconDB.COLUMN_COMPONENT + " = ?",
- new String[] { cn });
+ IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
+ new String[] {cn, Long.toString(userSerial)});
updatedPackages.add(component.getPackageName());
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 0db22a4..0c69154 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -22,6 +22,7 @@
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
@@ -147,6 +148,7 @@
if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0));
PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
+ info = convertToLauncherActivityIfPossible(info);
queuePendingShortcutInfo(info, context);
}
@@ -208,7 +210,7 @@
// Add the new apps to the model and bind them
if (!addShortcuts.isEmpty()) {
LauncherAppState app = LauncherAppState.getInstance();
- app.getModel().addAndBindAddedWorkspaceApps(context, addShortcuts);
+ app.getModel().addAndBindAddedWorkspaceItems(context, addShortcuts);
}
}
}
@@ -350,16 +352,7 @@
public ShortcutInfo getShortcutInfo() {
if (activityInfo != null) {
- final ShortcutInfo info = new ShortcutInfo();
- info.user = user;
- info.title = label;
- info.contentDescription = label;
- info.customIcon = false;
- info.intent = launchIntent;
- info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- info.flags = AppInfo.initFlags(activityInfo);
- info.firstInstallTime = activityInfo.getFirstInstallTime();
- return info;
+ return ShortcutInfo.fromActivityInfo(activityInfo, mContext);
} else {
return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data);
}
@@ -373,6 +366,10 @@
}
return packageName;
}
+
+ public boolean isLuncherActivity() {
+ return activityInfo != null;
+ }
}
private static PendingInstallShortcutInfo decode(String encoded, Context context) {
@@ -420,4 +417,40 @@
}
return null;
}
+
+ /**
+ * Tries to create a new PendingInstallShortcutInfo which represents the same target,
+ * but is an app target and not a shortcut.
+ * @return the newly created info or the original one.
+ */
+ private static PendingInstallShortcutInfo convertToLauncherActivityIfPossible(
+ PendingInstallShortcutInfo original) {
+ if (original.isLuncherActivity()) {
+ // Already an activity target
+ return original;
+ }
+ if (isValidShortcutLaunchIntent(original.launchIntent)
+ || !original.user.equals(UserHandleCompat.myUserHandle())) {
+ // We can only convert shortcuts which point to a main activity in the current user.
+ return original;
+ }
+
+ PackageManager pm = original.mContext.getPackageManager();
+ ResolveInfo info = pm.resolveActivity(original.launchIntent, 0);
+
+ if (info == null) {
+ return original;
+ }
+
+ // Ignore any conflicts in the label name, as that can change based on locale.
+ LauncherActivityInfoCompat launcherInfo = LauncherActivityInfoCompat
+ .fromResolveInfo(info, original.mContext);
+ return new PendingInstallShortcutInfo(launcherInfo, original.mContext);
+ }
+
+ public static boolean isLauncherActivity(Intent intent, Context context) {
+ Intent data = new Intent().putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
+ PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
+ return convertToLauncherActivityIfPossible(info).isLuncherActivity();
+ }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 10d804a..5679dfc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -132,7 +132,7 @@
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
LauncherStateTransitionAnimation.Callbacks {
- static final String TAG = "Launcher - MERONG";
+ static final String TAG = "Launcher";
static final boolean LOGD = true;
static final boolean PROFILE_STARTUP = false;
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
index 42f1914..cfc1bd9 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -124,7 +124,7 @@
mLauncher.showWorkspace(true, new Runnable() {
@Override
public void run() {
- mLauncher.getModel().addAndBindAddedWorkspaceApps(
+ mLauncher.getModel().addAndBindAddedWorkspaceItems(
mLauncher, addShortcuts, screenProvider, 0, true);
announceConfirmation(R.string.item_added_to_workspace);
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 3bd3850..6e77d06 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -149,7 +149,7 @@
return mIconCache;
}
- LauncherModel getModel() {
+ public LauncherModel getModel() {
return mModel;
}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 699cb37..9dd8dc5 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -21,6 +21,7 @@
public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
public static final String WALLPAPER_CROP_PREFERENCES_KEY =
"com.android.launcher3.WallpaperCropActivity";
+ public static final String MANAGED_USER_PREFERENCES_KEY = "com.android.launcher3.managedusers.prefs";
public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db";
public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db";
@@ -35,6 +36,7 @@
WALLPAPER_CROP_PREFERENCES_KEY + XML,
WALLPAPER_IMAGES_DB,
WIDGET_PREVIEWS_DB,
+ MANAGED_USER_PREFERENCES_KEY,
APP_ICONS_DB));
// TODO: Delete these files on upgrade
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 98ba09b..37f1ea8 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -59,6 +59,7 @@
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.Thunk;
import java.lang.ref.WeakReference;
@@ -87,7 +88,6 @@
static final boolean DEBUG_LOADERS = false;
private static final boolean DEBUG_RECEIVER = false;
private static final boolean REMOVE_UNRESTORED_ICONS = true;
- private static final boolean ADD_MANAGED_PROFILE_SHORTCUTS = false;
static final String TAG = "Launcher.Model";
@@ -107,11 +107,6 @@
@Thunk LoaderTask mLoaderTask;
@Thunk boolean mIsLoaderTaskRunning;
- /**
- * Maintain a set of packages per user, for which we added a shortcut on the workspace.
- */
- private static final String INSTALLED_SHORTCUTS_SET_PREFIX = "installed_shortcuts_set_for_user_";
-
// Specific runnable types that are run on the main thread deferred handler, this allows us to
// clear all queued binding runnables when the Launcher activity is destroyed.
private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
@@ -338,9 +333,9 @@
runOnWorkerThread(r);
}
- public void addAndBindAddedWorkspaceApps(final Context context,
+ public void addAndBindAddedWorkspaceItems(final Context context,
final ArrayList<ItemInfo> workspaceApps) {
- addAndBindAddedWorkspaceApps(context, workspaceApps,
+ addAndBindAddedWorkspaceItems(context, workspaceApps,
new ScreenPosProvider() {
@Override
@@ -518,7 +513,7 @@
* @param fallbackStartScreen the screen to start search for empty space if
* preferredScreen is not available.
*/
- public void addAndBindAddedWorkspaceApps(final Context context,
+ public void addAndBindAddedWorkspaceItems(final Context context,
final ArrayList<ItemInfo> workspaceApps,
final ScreenPosProvider preferredScreen,
final int fallbackStartScreen,
@@ -539,7 +534,7 @@
ArrayList<Long> workspaceScreens = loadWorkspaceScreensDb(context);
synchronized(sBgLock) {
for (ItemInfo item : workspaceApps) {
- if (!allowDuplicate) {
+ if (!allowDuplicate && item instanceof ShortcutInfo) {
// Short-circuit this logic if the icon exists somewhere on the workspace
if (shortcutExists(context, item.title.toString(),
item.getIntent(), item.user)) {
@@ -554,21 +549,21 @@
long screenId = coords.first;
int[] cordinates = coords.second;
- ShortcutInfo shortcutInfo;
- if (item instanceof ShortcutInfo) {
- shortcutInfo = (ShortcutInfo) item;
+ ItemInfo itemInfo;
+ if (item instanceof ShortcutInfo || item instanceof FolderInfo) {
+ itemInfo = item;
} else if (item instanceof AppInfo) {
- shortcutInfo = ((AppInfo) item).makeShortcut();
+ itemInfo = ((AppInfo) item).makeShortcut();
} else {
throw new RuntimeException("Unexpected info type");
}
// Add the shortcut to the db
- addItemToDatabase(context, shortcutInfo,
+ addItemToDatabase(context, itemInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, cordinates[0], cordinates[1]);
// Save the ShortcutInfo for binding in the workspace
- addedShortcutsFinal.add(shortcutInfo);
+ addedShortcutsFinal.add(itemInfo);
}
}
@@ -993,7 +988,7 @@
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
*/
- static void addItemToDatabase(Context context, final ItemInfo item, final long container,
+ public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
final long screenId, final int cellX, final int cellY) {
item.container = container;
item.cellX = cellX;
@@ -1097,7 +1092,6 @@
*/
static void deleteItemsFromDatabase(Context context, final ArrayList<? extends ItemInfo> items) {
final ContentResolver cr = context.getContentResolver();
-
Runnable r = new Runnable() {
public void run() {
for (ItemInfo item : items) {
@@ -2845,23 +2839,11 @@
mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache));
}
- if (ADD_MANAGED_PROFILE_SHORTCUTS && !user.equals(UserHandleCompat.myUserHandle())) {
- // Add shortcuts for packages which were installed while launcher was dead.
- String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
- + mUserManager.getSerialNumberForUser(user);
- Set<String> packagesAdded = prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET);
- HashSet<String> newPackageSet = new HashSet<String>();
-
- for (LauncherActivityInfoCompat info : apps) {
- String packageName = info.getComponentName().getPackageName();
- if (!packagesAdded.contains(packageName)
- && !newPackageSet.contains(packageName)) {
- InstallShortcutReceiver.queueInstallShortcut(info, mContext);
- }
- newPackageSet.add(packageName);
+ if (!user.equals(UserHandleCompat.myUserHandle())) {
+ ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
+ if (heuristic != null) {
+ heuristic.processUserApps(apps);
}
-
- prefs.edit().putStringSet(shortcutsSetKey, newPackageSet).commit();
}
}
// Huh? Shouldn't this be inside the Runnable below?
@@ -2884,6 +2866,8 @@
}
}
});
+ // Cleanup any data stored for a deleted user.
+ ManagedProfileHeuristic.processAllUsers(profiles, mContext);
if (DEBUG_LOADERS) {
Log.d(TAG, "Icons processed in "
@@ -2971,38 +2955,19 @@
final String[] packages = mPackages;
final int N = packages.length;
switch (mOp) {
- case OP_ADD:
+ case OP_ADD: {
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
mIconCache.updateIconsForPkg(packages[i], mUser);
mBgAllAppsList.addPackage(context, packages[i], mUser);
}
- // Auto add shortcuts for added packages.
- if (ADD_MANAGED_PROFILE_SHORTCUTS
- && !UserHandleCompat.myUserHandle().equals(mUser)) {
- SharedPreferences prefs = context.getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
- String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
- + mUserManager.getSerialNumberForUser(mUser);
- Set<String> shortcutSet = new HashSet<String>(
- prefs.getStringSet(shortcutsSetKey,Collections.EMPTY_SET));
-
- for (int i=0; i<N; i++) {
- if (!shortcutSet.contains(packages[i])) {
- shortcutSet.add(packages[i]);
- List<LauncherActivityInfoCompat> activities =
- mLauncherApps.getActivityList(packages[i], mUser);
- if (activities != null && !activities.isEmpty()) {
- InstallShortcutReceiver.queueInstallShortcut(
- activities.get(0), context);
- }
- }
- }
-
- prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit();
+ ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
+ if (heuristic != null) {
+ heuristic.processPackageAdd(mPackages);
}
break;
+ }
case OP_UPDATE:
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
@@ -3011,25 +2976,17 @@
mApp.getWidgetCache().removePackage(packages[i], mUser);
}
break;
- case OP_REMOVE:
- // Remove the packageName for the set of auto-installed shortcuts. This
- // will ensure that the shortcut when the app is installed again.
- if (ADD_MANAGED_PROFILE_SHORTCUTS
- && !UserHandleCompat.myUserHandle().equals(mUser)) {
- SharedPreferences prefs = context.getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
- String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
- + mUserManager.getSerialNumberForUser(mUser);
- HashSet<String> shortcutSet = new HashSet<String>(
- prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET));
- shortcutSet.removeAll(Arrays.asList(mPackages));
- prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit();
+ case OP_REMOVE: {
+ ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
+ if (heuristic != null) {
+ heuristic.processPackageRemoved(mPackages);
}
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
mIconCache.removeIconsForPkg(packages[i], mUser);
}
// Fall through
+ }
case OP_UNAVAILABLE:
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
@@ -3677,4 +3634,13 @@
public Callbacks getCallback() {
return mCallbacks != null ? mCallbacks.get() : null;
}
+
+ /**
+ * @return {@link FolderInfo} if its already loaded.
+ */
+ public FolderInfo findFolderById(Long folderId) {
+ synchronized (sBgLock) {
+ return sBgFolders.get(folderId);
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 1f59533..d75ef73 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -37,6 +37,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
+import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -63,7 +64,7 @@
private static final String TAG = "Launcher.LauncherProvider";
private static final boolean LOGD = false;
- private static final int DATABASE_VERSION = 23;
+ private static final int DATABASE_VERSION = 24;
static final String OLD_AUTHORITY = "com.android.launcher2.settings";
static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -617,7 +618,9 @@
break;
}
}
- case 23: {
+ case 23:
+ convertShortcutsToLauncherActivities(db);
+ case 24: {
// DB Upgraded successfully
return;
}
@@ -636,7 +639,6 @@
createEmptyDB(db);
}
-
/**
* Clears all the data for a fresh start.
*/
@@ -647,6 +649,63 @@
}
/**
+ * Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid
+ * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}.
+ */
+ private void convertShortcutsToLauncherActivities(SQLiteDatabase db) {
+ db.beginTransaction();
+ Cursor c = null;
+ SQLiteStatement updateStmt = null;
+
+ try {
+ // Only consider the primary user as other users can't have a shortcut.
+ long userSerial = UserManagerCompat.getInstance(mContext)
+ .getSerialNumberForUser(UserHandleCompat.myUserHandle());
+ c = db.query(TABLE_FAVORITES, new String[] {
+ Favorites._ID,
+ Favorites.INTENT,
+ }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial,
+ null, null, null, null);
+
+ updateStmt = db.compileStatement("UPDATE favorites SET itemType="
+ + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?");
+
+ final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
+ final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
+
+ while (c.moveToNext()) {
+ String intentDescription = c.getString(intentIndex);
+ Intent intent;
+ try {
+ intent = Intent.parseUri(intentDescription, 0);
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Unable to parse intent", e);
+ continue;
+ }
+
+ if (!InstallShortcutReceiver.isLauncherActivity(intent, mContext)) {
+ continue;
+ }
+
+ long id = c.getLong(idIndex);
+ updateStmt.bindLong(1, id);
+ updateStmt.execute();
+ }
+ db.setTransactionSuccessful();
+ } catch (SQLException ex) {
+ Log.w(TAG, "Error deduping shortcuts", ex);
+ } finally {
+ db.endTransaction();
+ if (c != null) {
+ c.close();
+ }
+ if (updateStmt != null) {
+ updateStmt.close();
+ }
+ }
+ }
+
+ /**
* Recreates workspace table and migrates data to the new table.
*/
public boolean recreateWorkspaceTable(SQLiteDatabase db) {
diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java
index 3bd0a78..a9b4955 100644
--- a/src/com/android/launcher3/LauncherScroller.java
+++ b/src/com/android/launcher3/LauncherScroller.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.hardware.SensorManager;
import android.os.Build;
-import android.util.FloatMath;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -409,7 +408,7 @@
float dx = (float) (mFinalX - mStartX);
float dy = (float) (mFinalY - mStartY);
- float hyp = FloatMath.sqrt(dx * dx + dy * dy);
+ float hyp = (float) Math.hypot(dx, dy);
float ndx = dx / hyp;
float ndy = dy / hyp;
@@ -426,7 +425,7 @@
mMode = FLING_MODE;
mFinished = false;
- float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
+ float velocity = (float) Math.hypot(velocityX, velocityY);
mVelocity = velocity;
mDuration = getSplineFlingDuration(velocity);
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 57bd5b2..78272a8 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -184,11 +184,6 @@
final WidgetsContainerView toView = mLauncher.getWidgetsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
- public void onRevealViewVisible(View revealView, View contentView,
- View allAppsButtonView) {
- revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark));
- }
- @Override
public float getMaterialRevealViewFinalAlpha(View revealView) {
return 0.3f;
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 9f7da6c..5bef845 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -24,7 +24,9 @@
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -274,5 +276,19 @@
public boolean shouldUseLowResIcon() {
return usingLowResIcon && container >= 0 && rank >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
}
+
+ public static ShortcutInfo fromActivityInfo(LauncherActivityInfoCompat info, Context context) {
+ final ShortcutInfo shortcut = new ShortcutInfo();
+ shortcut.user = info.getUser();
+ shortcut.title = info.getLabel().toString();
+ shortcut.contentDescription = UserManagerCompat.getInstance(context)
+ .getBadgedLabelForUser(info.getLabel(), info.getUser());
+ shortcut.customIcon = false;
+ shortcut.intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
+ shortcut.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ shortcut.flags = AppInfo.initFlags(info);
+ shortcut.firstInstallTime = info.getFirstInstallTime();
+ return shortcut;
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9173971..043ecb0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1410,7 +1410,22 @@
}
private float wallpaperOffsetForCurrentScroll() {
+ // TODO: do different behavior if it's a live wallpaper?
+ // Don't use up all the wallpaper parallax until you have at least
+ // MIN_PARALLAX_PAGE_SPAN pages
+ int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
+ int parallaxPageSpan;
+ if (mWallpaperIsLiveWallpaper) {
+ parallaxPageSpan = numScrollingPages - 1;
+ } else {
+ parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
+ }
+ mNumPagesForWallpaperParallax = parallaxPageSpan;
+
if (getChildCount() <= 1) {
+ if (isLayoutRtl()) {
+ return 1 - 1.0f/mNumPagesForWallpaperParallax;
+ }
return 0;
}
@@ -1430,28 +1445,20 @@
if (scrollRange == 0) {
return 0;
} else {
- // TODO: do different behavior if it's a live wallpaper?
// Sometimes the left parameter of the pages is animated during a layout transition;
// this parameter offsets it to keep the wallpaper from animating as well
int adjustedScroll =
getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
float offset = Math.min(1, adjustedScroll / (float) scrollRange);
offset = Math.max(0, offset);
- // Don't use up all the wallpaper parallax until you have at least
- // MIN_PARALLAX_PAGE_SPAN pages
- int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
- int parallaxPageSpan;
- if (mWallpaperIsLiveWallpaper) {
- parallaxPageSpan = numScrollingPages - 1;
- } else {
- parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
- }
- mNumPagesForWallpaperParallax = parallaxPageSpan;
// On RTL devices, push the wallpaper offset to the right if we don't have enough
// pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
- int padding = isLayoutRtl() ? parallaxPageSpan - numScrollingPages + 1 : 0;
- return offset * (padding + numScrollingPages - 1) / parallaxPageSpan;
+ if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
+ && isLayoutRtl()) {
+ return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
+ }
+ return offset * (numScrollingPages - 1) / parallaxPageSpan;
}
}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
index 90a4d1a..07ef0ef 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -17,7 +17,9 @@
package com.android.launcher3.compat;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
public abstract class LauncherActivityInfoCompat {
@@ -32,4 +34,11 @@
public abstract ApplicationInfo getApplicationInfo();
public abstract long getFirstInstallTime();
public abstract Drawable getBadgedIcon(int density);
+
+ /**
+ * Creates a LauncherActivityInfoCompat for the primary user.
+ */
+ public static LauncherActivityInfoCompat fromResolveInfo(ResolveInfo info, Context context) {
+ return new LauncherActivityInfoCompatV16(context, info);
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 1374b4e..a79d946 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -43,4 +43,5 @@
public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
public abstract Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user);
public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user);
+ public abstract long getUserCreationTime(UserHandleCompat user);
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index 32f972e..ffe698c 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -48,4 +48,9 @@
public CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user) {
return label;
}
+
+ @Override
+ public long getUserCreationTime(UserHandleCompat user) {
+ return 0;
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index 19eeabd..884d6fe 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -18,21 +18,27 @@
package com.android.launcher3.compat;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
-import android.os.UserManager;
+
+import com.android.launcher3.LauncherAppState;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UserManagerCompatVL extends UserManagerCompatV17 {
+ private static final String USER_CREATION_TIME_KEY = "user_creation_time_";
+
private final PackageManager mPm;
+ private final Context mContext;
UserManagerCompatVL(Context context) {
super(context);
mPm = context.getPackageManager();
+ mContext = context;
}
@Override
@@ -61,5 +67,17 @@
}
return mPm.getUserBadgedLabel(label, user.getUser());
}
+
+ @Override
+ public long getUserCreationTime(UserHandleCompat user) {
+ // TODO: Use system API once available.
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
+ if (!prefs.contains(key)) {
+ prefs.edit().putLong(key, System.currentTimeMillis()).apply();
+ }
+ return prefs.getLong(key, 0);
+ }
}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index 6e80c2f..8a08a4e 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -16,12 +16,13 @@
package com.android.launcher3.util;
-import android.content.res.Configuration;
import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewGroup;
import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
/**
* Calculates the next item that a {@link KeyEvent} should change the focus to.
@@ -42,7 +43,7 @@
*/
public class FocusLogic {
- private static final String TAG = "Focus";
+ private static final String TAG = "FocusLogic";
private static final boolean DEBUG = false;
// Item and page index related constant used by {@link #handleKeyEvent}.
@@ -51,12 +52,14 @@
public static final int PREVIOUS_PAGE_RIGHT_COLUMN = -2;
public static final int PREVIOUS_PAGE_FIRST_ITEM = -3;
public static final int PREVIOUS_PAGE_LAST_ITEM = -4;
+ public static final int PREVIOUS_PAGE_LEFT_COLUMN = -5;
- public static final int CURRENT_PAGE_FIRST_ITEM = -5;
- public static final int CURRENT_PAGE_LAST_ITEM = -6;
+ public static final int CURRENT_PAGE_FIRST_ITEM = -6;
+ public static final int CURRENT_PAGE_LAST_ITEM = -7;
- public static final int NEXT_PAGE_FIRST_ITEM = -7;
- public static final int NEXT_PAGE_LEFT_COLUMN = -8;
+ public static final int NEXT_PAGE_FIRST_ITEM = -8;
+ public static final int NEXT_PAGE_LEFT_COLUMN = -9;
+ public static final int NEXT_PAGE_RIGHT_COLUMN = -10;
// Matrix related constant.
public static final int EMPTY = -1;
@@ -85,18 +88,24 @@
cntX, cntY, iconIdx, pageIndex, pageCount));
}
+ DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid()
+ .getDeviceProfile();
int newIndex = NOOP;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/);
- if (newIndex == NOOP && pageIndex > 0) {
+ if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) {
newIndex = PREVIOUS_PAGE_RIGHT_COLUMN;
+ } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
+ newIndex = NEXT_PAGE_RIGHT_COLUMN;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/);
- if (newIndex == NOOP && pageIndex < pageCount - 1) {
+ if (!profile.isLayoutRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
newIndex = NEXT_PAGE_LEFT_COLUMN;
+ } else if (profile.isLayoutRtl && newIndex == NOOP && pageIndex > 0) {
+ newIndex = PREVIOUS_PAGE_LEFT_COLUMN;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
@@ -140,11 +149,18 @@
*/
// TODO: get rid of dynamic matrix creation.
public static int[][] createFullMatrix(int m, int n, boolean incrementOrder) {
+ DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid()
+ .getDeviceProfile();
int[][] matrix = new int [m][n];
+
for (int i=0; i < m;i++) {
for (int j=0; j < n; j++) {
if (incrementOrder) {
- matrix[i][j] = j * m + i;
+ if (!profile.isLayoutRtl) {
+ matrix[i][j] = j * m + i;
+ } else {
+ matrix[i][j] = j * m + m - i -1;
+ }
} else {
matrix[i][j] = EMPTY;
}
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
new file mode 100644
index 0000000..cefa71c
--- /dev/null
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -0,0 +1,277 @@
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Handles addition of app shortcuts for managed profiles.
+ * Methods of class should only be called on {@link LauncherModel#sWorkerThread}.
+ */
+public class ManagedProfileHeuristic {
+
+ private static final String TAG = "ManagedProfileHeuristic";
+
+ /**
+ * Maintain a set of packages installed per user.
+ */
+ private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
+
+ private static final String USER_FOLDER_ID_PREFIX = "user_folder_";
+
+ /**
+ * Duration (in milliseconds) for which app shortcuts will be added to work folder.
+ */
+ private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000;
+
+ public static ManagedProfileHeuristic get(Context context, UserHandleCompat user) {
+ if (Utilities.isLmpOrAbove() && !UserHandleCompat.myUserHandle().equals(user)) {
+ return new ManagedProfileHeuristic(context, user);
+ }
+ return null;
+ }
+
+ private final Context mContext;
+ private final UserHandleCompat mUser;
+ private final LauncherModel mModel;
+
+ private final SharedPreferences mPrefs;
+ private final long mUserSerial;
+ private final long mUserCreationTime;
+ private final String mPackageSetKey;
+
+ private ArrayList<ItemInfo> mHomescreenApps;
+ private ArrayList<ItemInfo> mWorkFolderApps;
+
+ private ManagedProfileHeuristic(Context context, UserHandleCompat user) {
+ mContext = context;
+ mUser = user;
+ mModel = LauncherAppState.getInstance().getModel();
+
+ UserManagerCompat userManager = UserManagerCompat.getInstance(context);
+ mUserSerial = userManager.getSerialNumberForUser(user);
+ mUserCreationTime = userManager.getUserCreationTime(user);
+ mPackageSetKey = INSTALLED_PACKAGES_PREFIX + mUserSerial;
+
+ mPrefs = mContext.getSharedPreferences(LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
+ Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Checks the list of user apps and adds icons for newly installed apps on the homescreen or
+ * workfolder.
+ */
+ public void processUserApps(List<LauncherActivityInfoCompat> apps) {
+ mHomescreenApps = new ArrayList<ItemInfo>();
+ mWorkFolderApps = new ArrayList<ItemInfo>();
+ HashSet<String> packageSet = getPackageSet();
+ boolean newPackageAdded = false;
+
+ for (LauncherActivityInfoCompat info : apps) {
+ String packageName = info.getComponentName().getPackageName();
+ if (!packageSet.contains(packageName)) {
+ packageSet.add(packageName);
+ newPackageAdded = true;
+
+ try {
+ PackageInfo pkgInfo = mContext.getPackageManager()
+ .getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+ markForAddition(info, pkgInfo.firstInstallTime);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Unknown package " + packageName, e);
+ }
+ }
+ }
+
+ if (newPackageAdded) {
+ mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
+ finalizeAdditions();
+ }
+ }
+
+ private void markForAddition(LauncherActivityInfoCompat info, long installTime) {
+ ArrayList<ItemInfo> targetList =
+ (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
+ mWorkFolderApps : mHomescreenApps;
+ targetList.add(ShortcutInfo.fromActivityInfo(info, mContext));
+ }
+
+ /**
+ * Adds and binds shortcuts marked to be added to the work folder.
+ */
+ private void finalizeWorkFolder() {
+ if (mWorkFolderApps.isEmpty()) {
+ return;
+ }
+
+ // Try to get a work folder.
+ String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
+ if (mPrefs.contains(folderIdKey)) {
+ long folderId = mPrefs.getLong(folderIdKey, 0);
+ final FolderInfo workFolder = mModel.findFolderById(folderId);
+
+ if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) {
+ // Could not get a work folder. Add all the icons to homescreen.
+ mHomescreenApps.addAll(mWorkFolderApps);
+ return;
+ }
+ saveWorkFolderShortcuts(folderId, workFolder.contents.size());
+
+ final ArrayList<ItemInfo> shortcuts = mWorkFolderApps;
+ // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
+ new MainThreadExecutor().execute(new Runnable() {
+
+ @Override
+ public void run() {
+ for (ItemInfo info : shortcuts) {
+ workFolder.add((ShortcutInfo) info);
+ }
+ }
+ });
+ } else {
+ // Create a new folder.
+ final FolderInfo workFolder = new FolderInfo();
+ workFolder.title = mContext.getText(R.string.work_folder_name);
+ workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
+
+ // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
+ for (ItemInfo info : mWorkFolderApps) {
+ workFolder.add((ShortcutInfo) info);
+ }
+
+ // Add the item to home screen and DB. This also generates an item id synchronously.
+ ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1);
+ itemList.add(workFolder);
+ mModel.addAndBindAddedWorkspaceItems(mContext, itemList);
+ mPrefs.edit().putLong(USER_FOLDER_ID_PREFIX + mUserSerial, workFolder.id).apply();
+
+ saveWorkFolderShortcuts(workFolder.id, 0);
+ }
+ }
+
+ /**
+ * Add work folder shortcuts to the DB.
+ */
+ private void saveWorkFolderShortcuts(long workFolderId, int startingRank) {
+ for (ItemInfo info : mWorkFolderApps) {
+ info.rank = startingRank++;
+ LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0);
+ }
+ }
+
+ /**
+ * Adds and binds all shortcuts marked for addition.
+ */
+ private void finalizeAdditions() {
+ finalizeWorkFolder();
+
+ if (!mHomescreenApps.isEmpty()) {
+ mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps);
+ }
+ }
+
+ /**
+ * Updates the list of installed apps and adds any new icons on homescreen or work folder.
+ */
+ public void processPackageAdd(String[] packages) {
+ mHomescreenApps = new ArrayList<ItemInfo>();
+ mWorkFolderApps = new ArrayList<ItemInfo>();
+ HashSet<String> packageSet = getPackageSet();
+ boolean newPackageAdded = false;
+ long installTime = System.currentTimeMillis();
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
+
+ for (String packageName : packages) {
+ if (!packageSet.contains(packageName)) {
+ packageSet.add(packageName);
+ newPackageAdded = true;
+
+ List<LauncherActivityInfoCompat> activities =
+ launcherApps.getActivityList(packageName, mUser);
+ if (!activities.isEmpty()) {
+ markForAddition(activities.get(0), installTime);
+ }
+ }
+ }
+
+ if (newPackageAdded) {
+ mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
+ finalizeAdditions();
+ }
+ }
+
+ /**
+ * Updates the list of installed packages for the user.
+ */
+ public void processPackageRemoved(String[] packages) {
+ HashSet<String> packageSet = getPackageSet();
+ boolean packageRemoved = false;
+
+ for (String packageName : packages) {
+ if (packageSet.remove(packageName)) {
+ packageRemoved = true;
+ }
+ }
+
+ if (packageRemoved) {
+ mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private HashSet<String> getPackageSet() {
+ return new HashSet<String>(mPrefs.getStringSet(mPackageSetKey, Collections.EMPTY_SET));
+ }
+
+ /**
+ * Verifies that entries corresponding to {@param users} exist and removes all invalid entries.
+ */
+ public static void processAllUsers(List<UserHandleCompat> users, Context context) {
+ if (!Utilities.isLmpOrAbove()) {
+ return;
+ }
+ UserManagerCompat userManager = UserManagerCompat.getInstance(context);
+ HashSet<String> validKeys = new HashSet<String>();
+ for (UserHandleCompat user : users) {
+ addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys);
+ }
+
+ SharedPreferences prefs = context.getSharedPreferences(
+ LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ for (String key : prefs.getAll().keySet()) {
+ if (!validKeys.contains(key)) {
+ editor.remove(key);
+ }
+ }
+ editor.apply();
+ }
+
+ private static void addAllUserKeys(long userSerial, HashSet<String> keysOut) {
+ keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial);
+ keysOut.add(USER_FOLDER_ID_PREFIX + userSerial);
+ }
+}
diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java
index d7edf22..1a1de55 100644
--- a/src/com/android/launcher3/widget/PackageItemInfo.java
+++ b/src/com/android/launcher3/widget/PackageItemInfo.java
@@ -39,11 +39,12 @@
*/
public boolean usingLowResIcon;
- public ComponentName componentName;
+ public String packageName;
int flags = 0;
- PackageItemInfo() {
+ PackageItemInfo(String packageName) {
+ this.packageName = packageName;
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index ccd67ce..d10c304 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -47,7 +47,7 @@
*/
public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
- private static final String TAG = "PagedViewWidget";
+ private static final String TAG = "WidgetCell";
private static final boolean DEBUG = false;
// Temporary preset width and height of the image to keep them aligned.
@@ -123,6 +123,15 @@
deletePreview(false);
}
+ public void reset() {
+ ImageView image = (ImageView) findViewById(R.id.widget_preview);
+ final TextView name = (TextView) findViewById(R.id.widget_name);
+ final TextView dims = (TextView) findViewById(R.id.widget_dims);
+ image.setImageDrawable(null);
+ name.setText(null);
+ dims.setText(null);
+ }
+
public void deletePreview(boolean recycleImage) {
if (recycleImage) {
final ImageView image = (ImageView) findViewById(R.id.widget_preview);
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index d0d1e60..afeb2d3 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -88,14 +88,13 @@
@Override
public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
- String packageName = mWidgetsModel.getPackageName(pos);
- List<Object> infoList = mWidgetsModel.getSortedWidgets(packageName);
+ List<Object> infoList = mWidgetsModel.getSortedWidgets(pos);
ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list));
if (DEBUG) {
Log.d(TAG, String.format(
- "onBindViewHolder [pos=%d, packageName=%s, widget#=%d, row.getChildCount=%d]",
- pos, packageName, infoList.size(), row.getChildCount()));
+ "onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]",
+ pos, infoList.size(), row.getChildCount()));
}
// Add more views.
@@ -120,10 +119,11 @@
}
// Bind the views in the application info section.
- PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(packageName);
+ PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(pos);
if (infoOut.usingLowResIcon) {
- mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
- false /* useLowResIcon */, infoOut);
+ // TODO(hyunyoungs): call this in none UI thread in the same way as BubbleTextView.
+ mIconCache.getTitleAndIconForApp(infoOut.packageName,
+ UserHandleCompat.myUserHandle(), false /* useLowResIcon */, infoOut);
}
((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title);
ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image);
@@ -132,7 +132,8 @@
// Bind the view in the widget horizontal tray region.
for (int i=0; i < infoList.size(); i++) {
WidgetCell widget = (WidgetCell) row.getChildAt(i);
- if (getWidgetPreviewLoader() == null || widget == null) {
+ widget.reset();
+ if (getWidgetPreviewLoader() == null) {
return;
}
if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
@@ -149,7 +150,6 @@
widget.setVisibility(View.VISIBLE);
widget.ensurePreview();
}
- // TODO(hyunyoungs): Draw the scrollable indicator.
}
@Override
@@ -174,15 +174,4 @@
}
return mWidgetPreviewLoader;
}
-
- /**
- * TODO(hyunyoungs): this is temporary. Figure out the width of each widget cell
- * and then check if the total sum is longer than the parent width.
- */
- private void addScrollableIndicator(int contentSize, ViewGroup parent) {
- if (contentSize > 2) {
- ViewGroup indicator = (ViewGroup) parent.findViewById(R.id.scrollable_indicator);
- indicator.setVisibility(View.VISIBLE);
- }
- }
}
diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java
index c400d63..71a7b94 100644
--- a/src/com/android/launcher3/widget/WidgetsModel.java
+++ b/src/com/android/launcher3/widget/WidgetsModel.java
@@ -31,12 +31,10 @@
private static final boolean DEBUG = false;
/* List of packages that is tracked by this model. */
- private List<String> mPackageNames = new ArrayList<>();
-
- private Map<String, PackageItemInfo> mPackageItemInfoList = new HashMap<>();
+ private List<PackageItemInfo> mPackageItemInfos = new ArrayList<>();
/* Map of widgets and shortcuts that are tracked per package. */
- private Map<String, ArrayList<Object>> mWidgetsList = new HashMap<>();
+ private Map<PackageItemInfo, ArrayList<Object>> mWidgetsList = new HashMap<>();
/* Notifies the adapter when data changes. */
private RecyclerView.Adapter mAdapter;
@@ -53,20 +51,16 @@
// Access methods that may be deleted if the private fields are made package-private.
public int getPackageSize() {
- return mPackageNames.size();
+ return mPackageItemInfos.size();
}
// Access methods that may be deleted if the private fields are made package-private.
- public String getPackageName(int pos) {
- return mPackageNames.get(pos);
+ public PackageItemInfo getPackageItemInfo(int pos) {
+ return mPackageItemInfos.get(pos);
}
- public PackageItemInfo getPackageItemInfo(String packageName) {
- return mPackageItemInfoList.get(packageName);
- }
-
- public List<Object> getSortedWidgets(String packageName) {
- return mWidgetsList.get(packageName);
+ public List<Object> getSortedWidgets(int pos) {
+ return mWidgetsList.get(mPackageItemInfos.get(pos));
}
public void addWidgetsAndShortcuts(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
@@ -74,9 +68,13 @@
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size());
}
+ // Temporary list for {@link PackageItemInfos} to avoid having to go through
+ // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
+ HashMap<String, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
+
// clear the lists.
- mPackageNames.clear();
mWidgetsList.clear();
+ mPackageItemInfos.clear();
// add and update.
for (Object o: widgetsShortcuts) {
@@ -90,47 +88,41 @@
} else {
Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s",
o.getClass().toString()));
-
}
- ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(packageName);
+ PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
+ ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(pInfo);
if (widgetsShortcutsList != null) {
widgetsShortcutsList.add(o);
} else {
widgetsShortcutsList = new ArrayList<Object>();
widgetsShortcutsList.add(o);
- mWidgetsList.put(packageName, widgetsShortcutsList);
- mPackageNames.add(packageName);
- }
- }
- for (String packageName: mPackageNames) {
- PackageItemInfo pInfo = mPackageItemInfoList.get(packageName);
- if (pInfo == null) {
- pInfo = new PackageItemInfo();
+
+ pInfo = new PackageItemInfo(packageName);
mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
true /* useLowResIcon */, pInfo);
- mPackageItemInfoList.put(packageName, pInfo);
+ mWidgetsList.put(pInfo, widgetsShortcutsList);
+ tmpPackageItemInfos.put(packageName, pInfo);
+ mPackageItemInfos.add(pInfo);
}
}
// sort.
- sortPackageList();
- for (String packageName: mPackageNames) {
- Collections.sort(mWidgetsList.get(packageName), mWidgetAndShortcutNameComparator);
+ sortPackageItemInfos();
+ for (PackageItemInfo p: mPackageItemInfos) {
+ Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
}
// notify.
mAdapter.notifyDataSetChanged();
}
- private void sortPackageList() {
- Collections.sort(mPackageNames, new Comparator<String>() {
+ private void sortPackageItemInfos() {
+ Collections.sort(mPackageItemInfos, new Comparator<PackageItemInfo>() {
@Override
- public int compare(String lhs, String rhs) {
- String lhsTitle = mPackageItemInfoList.get(lhs).title.toString();
- String rhsTitle = mPackageItemInfoList.get(rhs).title.toString();
- return lhsTitle.compareTo(rhsTitle);
+ public int compare(PackageItemInfo lhs, PackageItemInfo rhs) {
+ return lhs.title.toString().compareTo(rhs.title.toString());
}
});
}
-}
+}
\ No newline at end of file