Merge "Don't close all apps when touching deep shortcuts container." into ub-launcher3-calgary-polish
diff --git a/res/drawable-hdpi/ic_all_apps_bg_hand.png b/res/drawable-hdpi/ic_all_apps_bg_hand.png
index 43b1bed..dff2f54 100644
--- a/res/drawable-hdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-hdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_1.png b/res/drawable-hdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index d2c4cc1..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_2.png b/res/drawable-hdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 57b7456..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_3.png b/res/drawable-hdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 54fe70b..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_4.png b/res/drawable-hdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 9c0f777..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_hand.png b/res/drawable-mdpi/ic_all_apps_bg_hand.png
index 8868d6b..0d1d7bb 100644
--- a/res/drawable-mdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-mdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_1.png b/res/drawable-mdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index 4c78288..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_2.png b/res/drawable-mdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 0ed311b..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_3.png b/res/drawable-mdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 2aa3d4e..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_4.png b/res/drawable-mdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 2cdea9c..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_hand.png b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
index 8a67245..e727d37 100644
--- a/res/drawable-xhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index c0ebaed..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 71cf250..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 3c69fc5..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 5f6ca38..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
index ed694f8..fffcc6b 100644
--- a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index 5cb0427..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index cd0322b..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 19ffc2d..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 311c3df..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
index 615374a..4d065d8 100644
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index 10f8c41..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 102d925..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 9be5b7a..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index d7fb29b..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_all_apps_bg_icon_1.xml b/res/drawable/ic_all_apps_bg_icon_1.xml
new file mode 100644
index 0000000..c9c0a75
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M44.28,30.96c4.84-10.68,0.09-23.27-10.59-28.11S10.42,2.74,5.58,13.42
+ C1,23.54,6.5,35.92,16.62,40.51l0,0l-3.23,7.12C27.84,47,39.79,40.86,44.28,30.96z" />
+ <path
+ android:fillColor="#E0E0E0"
+ android:pathData="M41.75,30.05c4.84-10.68,0.09-23.27-10.59-28.11S7.9,1.83,3.06,12.51
+ c-4.59,10.12,0.92,22.5,11.03,27.09l0,0l-3.23,7.12C25.31,46.09,37.26,39.94,41.75,30.05z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_2.xml b/res/drawable/ic_all_apps_bg_icon_2.xml
new file mode 100644
index 0000000..b6269e3
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M20.54,44.59c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
+ c-0.25-0.58-0.9-0.99-1.89-1.1L6.2,5.99C5.39,5.91,4.74,6.08,4.32,6.44l0,0C3.7,6.97,3.55,7.88,4.01,8.96l14.54,34.09
+ C19,44.13,19.75,44.65,20.54,44.59L20.54,44.59z" />
+ <path
+ android:fillColor="#E0E0E0"
+ android:pathData="M18.49,43.22c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
+ c-0.25-0.58-0.9-0.99-1.89-1.1L4.15,4.62C3.34,4.54,2.69,4.71,2.27,5.08l0,0C1.65,5.6,1.5,6.52,1.96,7.6L16.5,41.69
+ C16.96,42.76,17.7,43.28,18.49,43.22L18.49,43.22z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_3.xml b/res/drawable/ic_all_apps_bg_icon_3.xml
new file mode 100644
index 0000000..4c255a9
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_3.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M25.18,1.27c-12.32,0-23.41,9.99-23.41,22.31s11.09,22.31,23.41,22.31
+ s22.31-9.99,22.31-22.31S37.5,1.27,25.18,1.27z M25.18,33.55c-5.5,0-14.35-5.1-14.35-10.6s8.32-12.19,13.82-12.19
+ c5.5,0,10.49,7.33,10.49,12.83S30.68,33.55,25.18,33.55z" />
+ <path
+ android:fillColor="#E0E0E0"
+ android:pathData="M22.93,0.22c-12.32,0-22.31,9.99-22.31,22.31s9.99,22.31,22.31,22.31
+ s22.31-9.99,22.31-22.31S35.25,0.22,22.93,0.22z M22.93,32.5c-5.5,0-9.97-4.46-9.97-9.97s4.46-9.97,9.97-9.97
+ c5.5,0,9.97,4.46,9.97,9.97S28.43,32.5,22.93,32.5z" />
+ <path
+ android:fillColor="#E0E0E0"
+ android:pathData="M14.81,22.53a8.12,8.12 0 1,0 16.24,0a8.12,8.12 0 1,0 -16.24,0z" />
+</vector>
diff --git a/res/drawable/ic_all_apps_bg_icon_4.xml b/res/drawable/ic_all_apps_bg_icon_4.xml
new file mode 100644
index 0000000..12e05bc
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_4.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M11.53,8.02l23.39-5.73c1.61-0.39,3.25,0.6,3.64,2.21l7.64,31.19
+ c0.39,1.61-0.6,3.25-2.21,3.64L12.8,46.97c-1.61,0.39-3.25-0.6-3.64-2.21L3.43,21.37L11.53,8.02z" />
+ <path
+ android:fillColor="#E0E0E0"
+ android:pathData="M9.2,6.53L32.59,0.8C34.2,0.4,35.84,1.4,36.23,3l7.64,31.19c0.39,1.61-0.6,3.25-2.21,3.64
+ l-31.19,7.64c-1.61,0.39-3.25-0.6-3.64-2.21L1.11,19.87L9.2,6.53z" />
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M9.27,6.47l1.91,7.8c0.4,1.62-0.59,3.24-2.21,3.64l-7.8,1.91L9.27,6.47z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index 256d24c..8a50c0c 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -20,5 +20,5 @@
android:viewportHeight="48.0">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M38.86 25.95c.08-.64.14-1.29.14-1.95s-.06-1.31-.14-1.95l4.23-3.31c.38-.3.49-.84.24-1.28l-4-6.93c-.25-.43-.77-.61-1.22-.43l-4.98 2.01c-1.03-.79-2.16-1.46-3.38-1.97L29 4.84c-.09-.47-.5-.84-1-.84h-8c-.5 0-.91.37-.99.84l-.75 5.3c-1.22.51-2.35 1.17-3.38 1.97L9.9 10.1c-.45-.17-.97 0-1.22.43l-4 6.93c-.25.43-.14.97.24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31.14 1.95l-4.22 3.31c-.38.3-.49.84-.24 1.28l4 6.93c.25.43.77.61 1.22.43l4.98-2.01c1.03.79 2.16 1.46 3.38 1.97l.75 5.3c.08.47.49.84.99.84h8c.5 0 .91-.37.99-.84l.75-5.3c1.22-.51 2.35-1.17 3.38-1.97l4.98 2.01c.45.17.97 0 1.22-.43l4-6.93c.25-.43.14-.97-.24-1.28l-4.22-3.31zM24 31c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
+ android:pathData="M38.86 25.95c.08-.64 .14-1.29 .14-1.95s-.06-1.31-.14-1.95l4.23-3.31c.38-.3 .49-.84 .24-1.28l-4-6.93c-.25-.43-.77-.61-1.22-.43l-4.98 2.01c-1.03-.79-2.16-1.46-3.38-1.97L29 4.84c-.09-.47-.5-.84-1-.84h-8c-.5 0-.91 .37-.99 .84l-.75 5.3c-1.22 .51-2.35 1.17-3.38 1.97L9.9 10.1c-.45-.17-.97 0-1.22 .43l-4 6.93c-.25 .43-.14 .97 .24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31 .14 1.95l-4.22 3.31c-.38 .3-.49 .84-.24 1.28l4 6.93c.25 .43 .77 .61 1.22 .43l4.98-2.01c1.03 .79 2.16 1.46 3.38 1.97l.75 5.3c.08 .47 .49 .84 .99 .84h8c.5 0 .91-.37 .99-.84l.75-5.3c1.22-.51 2.35-1.17 3.38-1.97l4.98 2.01c.45 .17 .97 0 1.22-.43l4-6.93c.25-.43 .14-.97-.24-1.28l-4.22-3.31zM24 31c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
</vector>
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 604b164..4c4d67c 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -62,7 +62,7 @@
*/
int isDisabled = ShortcutInfo.DEFAULT;
- AppInfo() {
+ public AppInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 2dde7ca..cf8abae 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -259,7 +259,8 @@
}
};
dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
- DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2),
+ mLauncher.getDragController().isExternalDrag() ? 1 : DRAG_VIEW_DROP_DURATION,
+ new DecelerateInterpolator(2),
new LinearInterpolator(), onAnimationEndRunnable,
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index bf4551b..f7737f4 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -19,6 +19,7 @@
import android.util.AttributeSet;
import android.view.DragEvent;
import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
@@ -27,6 +28,8 @@
*/
public class ExtendedEditText extends EditText {
+ private boolean mShowImeAfterFirstLayout;
+
/**
* Implemented by listeners of the back key.
*/
@@ -37,11 +40,11 @@
private OnBackKeyListener mBackKeyListener;
public ExtendedEditText(Context context) {
- super(context);
+ this(context, null, 0);
}
public ExtendedEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public ExtendedEditText(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -69,4 +72,29 @@
// We don't want this view to interfere with Launcher own drag and drop.
return false;
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mShowImeAfterFirstLayout) {
+ // soft input only shows one frame after the layout of the EditText happens,
+ post(new Runnable() {
+ @Override
+ public void run() {
+ showSoftInput();
+ mShowImeAfterFirstLayout = false;
+ }
+ });
+ }
+ }
+
+ public void showKeyboard() {
+ mShowImeAfterFirstLayout = !showSoftInput();
+ }
+
+ private boolean showSoftInput() {
+ return requestFocus() &&
+ ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
+ .showSoftInput(this, InputMethodManager.SHOW_FORCED);
+ }
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index c738480..f9424d4 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -25,6 +25,7 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -69,7 +70,7 @@
mLauncher = (Launcher) context;
mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
mBackgroundColor = ColorUtils.setAlphaComponent(
- context.getColor(R.color.all_apps_container_color), 0);
+ ContextCompat.getColor(context, R.color.all_apps_container_color), 0);
mBackground = new ColorDrawable(mBackgroundColor);
setBackground(mBackground);
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index a49162c..d3fb38e 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -853,8 +853,7 @@
values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
values.put(IconDB.COLUMN_LABEL, label);
- values.put(IconDB.COLUMN_SYSTEM_STATE,
- mIconProvider.getIconSystemState(mIconProvider.getIconSystemState(packageName)));
+ values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
return values;
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index e136bcd..398c9d2 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -100,6 +100,7 @@
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
return developmentSettingsEnabled
&& (info instanceof AppInfo || info instanceof ShortcutInfo
- || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo);
+ || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo)
+ && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index df87cc2..d8e58d8 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -33,6 +33,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
import org.json.JSONException;
@@ -146,6 +147,15 @@
}
PendingInstallShortcutInfo info = createPendingInfo(context, data);
if (info != null) {
+ if (!info.isLauncherActivity()) {
+ // Since its a custom shortcut, verify that it is safe to launch.
+ if (!PackageManagerHelper.hasPermissionForActivity(
+ context, info.launchIntent, null)) {
+ // Target cannot be launched, or requires some special permission to launch
+ Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0));
+ return;
+ }
+ }
queuePendingShortcutInfo(info, context);
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1d5ece2..123c7d8 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -35,7 +35,6 @@
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
-import android.content.ContentValues;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
@@ -116,10 +115,12 @@
import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -184,12 +185,10 @@
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
// Type: int
private static final String RUNTIME_STATE = "launcher.state";
- // Type: Content Values / parcelable
- private static final String RUNTIME_STATE_PENDING_ADD_ITEM = "launcher.add_item";
- // Type: parcelable
- private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
- // Type: parcelable
- private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id";
+ // Type: PendingRequestArgs
+ private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
+ // Type: ActivityResultInfo
+ private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
@@ -237,10 +236,6 @@
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
- @Thunk final ItemInfo mPendingAddInfo = new ItemInfo();
- private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
- private int mPendingAddWidgetId = -1;
-
private int[] mTmpAddItemCellCoordinates = new int[2];
@Thunk Hotseat mHotseat;
@@ -270,8 +265,6 @@
@Thunk boolean mWorkspaceLoading = true;
private boolean mPaused = true;
- private boolean mRestoring;
- private boolean mWaitingForResult;
private boolean mOnResumeNeedsLoad;
private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
@@ -308,7 +301,6 @@
private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
- private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
@@ -352,17 +344,13 @@
}
};
- private static PendingAddArguments sPendingAddItem;
-
- @Thunk static class PendingAddArguments {
- int requestCode;
- Intent intent;
- long container;
- long screenId;
- int cellX;
- int cellY;
- int appWidgetId;
- }
+ // Activity result which needs to be processed after workspace has loaded.
+ private ActivityResultInfo mPendingActivityResult;
+ /**
+ * Holds extra information required to handle a result from an external call, like
+ * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)}
+ */
+ private PendingRequestArgs mPendingRequestArgs;
private UserEventDispatcher mUserEventDispatcher;
@@ -453,20 +441,14 @@
Trace.endSection();
}
- if (!mRestoring) {
- if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
- // If the user leaves launcher, then we should just load items asynchronously when
- // they return.
- mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
- } else {
- // We only load the page synchronously if the user rotates (or triggers a
- // configuration change) while launcher is in the foreground
- if (!mModel.startLoader(mWorkspace.getRestorePage())) {
- // If we are not binding synchronously, show a fade in animation when
- // the first page bind completes.
- mDragLayer.setAlpha(0);
- }
- }
+ // We only load the page synchronously if the user rotates (or triggers a
+ // configuration change) while launcher is in the foreground
+ if (!mModel.startLoader(mWorkspace.getRestorePage())) {
+ // If we are not binding synchronously, show a fade in animation when
+ // the first page bind completes.
+ mDragLayer.setAlpha(0);
+ } else {
+ setWorkspaceLoading(true);
}
// For handling default keys
@@ -659,53 +641,61 @@
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
*/
- private long completeAdd(PendingAddArguments args) {
- long screenId = args.screenId;
- if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ private long completeAdd(
+ int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) {
+ long screenId = info.screenId;
+ if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
// When the screen id represents an actual screen (as opposed to a rank) we make sure
// that the drop page actually exists.
- screenId = ensurePendingDropLayoutExists(args.screenId);
+ screenId = ensurePendingDropLayoutExists(info.screenId);
}
- switch (args.requestCode) {
+ switch (requestCode) {
case REQUEST_CREATE_SHORTCUT:
- completeAddShortcut(args.intent, args.container, screenId, args.cellX,
- args.cellY);
+ completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info);
break;
case REQUEST_CREATE_APPWIDGET:
- completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
+ completeAddAppWidget(appWidgetId, info, null, null);
break;
case REQUEST_RECONFIGURE_APPWIDGET:
- completeRestoreAppWidget(args.appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
+ completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
break;
case REQUEST_BIND_PENDING_APPWIDGET: {
- int widgetId = args.appWidgetId;
- LauncherAppWidgetInfo info =
+ int widgetId = appWidgetId;
+ LauncherAppWidgetInfo widgetInfo =
completeRestoreAppWidget(widgetId, LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
- if (info != null) {
+ if (widgetInfo != null) {
// Since the view was just bound, also launch the configure activity if needed
LauncherAppWidgetProviderInfo provider = mAppWidgetManager
.getLauncherAppWidgetInfo(widgetId);
if (provider != null && provider.configure != null) {
- startRestoredWidgetReconfigActivity(provider, info);
+ startRestoredWidgetReconfigActivity(provider, widgetInfo);
}
}
break;
}
}
- // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
- // if you turned the screen off and then back while in All Apps, Launcher would not
- // return to the workspace. Clearing mAddInfo.container here fixes this issue
- resetAddInfo();
+
return screenId;
}
private void handleActivityResult(
final int requestCode, final int resultCode, final Intent data) {
+ if (isWorkspaceLoading()) {
+ // process the result once the workspace has loaded.
+ mPendingActivityResult = new ActivityResultInfo(requestCode, resultCode, data);
+ return;
+ }
+ mPendingActivityResult = null;
+
// Reset the startActivity waiting flag
- setWaitingForResult(false);
- final int pendingAddWidgetId = mPendingAddWidgetId;
- mPendingAddWidgetId = -1;
+ final PendingRequestArgs requestArgs = mPendingRequestArgs;
+ setWaitingForResult(null);
+ if (requestArgs == null) {
+ return;
+ }
+
+ final int pendingAddWidgetId = requestArgs.getWidgetId();
Runnable exitSpringLoaded = new Runnable() {
@Override
@@ -720,12 +710,14 @@
final int appWidgetId = data != null ?
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
if (resultCode == RESULT_CANCELED) {
- completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
+ completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs);
mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_OK) {
- addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
- mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
+ addAppWidgetImpl(
+ appWidgetId, requestArgs, null,
+ requestArgs.getWidgetProvider(),
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY);
}
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -741,7 +733,6 @@
boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
requestCode == REQUEST_CREATE_APPWIDGET);
- final boolean workspaceLocked = isWorkspaceLocked();
// We have special handling for widgets
if (isWidgetDrop) {
final int appWidgetId;
@@ -758,46 +749,36 @@
Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
"returned from the widget configuration activity.");
result = RESULT_CANCELED;
- completeTwoStageWidgetDrop(result, appWidgetId);
+ completeTwoStageWidgetDrop(result, appWidgetId, requestArgs);
final Runnable onComplete = new Runnable() {
@Override
public void run() {
exitSpringLoadedDragModeDelayed(false, 0, null);
}
};
- if (workspaceLocked) {
- // No need to remove the empty screen if we're mid-binding, as the
- // the bind will not add the empty screen.
- mWorkspace.postDelayed(onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
- } else {
- mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
- }
- } else {
- if (!workspaceLocked) {
- if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- // When the screen id represents an actual screen (as opposed to a rank)
- // we make sure that the drop page actually exists.
- mPendingAddInfo.screenId =
- ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
- }
- final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
- dropLayout.setDropPending(true);
- final Runnable onComplete = new Runnable() {
- @Override
- public void run() {
- completeTwoStageWidgetDrop(resultCode, appWidgetId);
- dropLayout.setDropPending(false);
- }
- };
- mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
- } else {
- PendingAddArguments args = preparePendingAddArgs(requestCode, data, appWidgetId,
- mPendingAddInfo);
- sPendingAddItem = args;
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+ } else {
+ if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ // When the screen id represents an actual screen (as opposed to a rank)
+ // we make sure that the drop page actually exists.
+ requestArgs.screenId =
+ ensurePendingDropLayoutExists(requestArgs.screenId);
}
+ final CellLayout dropLayout =
+ mWorkspace.getScreenWithId(requestArgs.screenId);
+
+ dropLayout.setDropPending(true);
+ final Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ completeTwoStageWidgetDrop(resultCode, appWidgetId, requestArgs);
+ dropLayout.setDropPending(false);
+ }
+ };
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
return;
}
@@ -806,13 +787,7 @@
|| requestCode == REQUEST_BIND_PENDING_APPWIDGET) {
if (resultCode == RESULT_OK) {
// Update the widget view.
- PendingAddArguments args = preparePendingAddArgs(requestCode, data,
- pendingAddWidgetId, mPendingAddInfo);
- if (workspaceLocked) {
- sPendingAddItem = args;
- } else {
- completeAdd(args);
- }
+ completeAdd(requestCode, data, pendingAddWidgetId, requestArgs);
}
// Leave the widget in the pending state if the user canceled the configure.
return;
@@ -820,23 +795,17 @@
if (requestCode == REQUEST_CREATE_SHORTCUT) {
// Handle custom shortcuts created using ACTION_CREATE_SHORTCUT.
- if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
- final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
- mPendingAddInfo);
- if (isWorkspaceLocked()) {
- sPendingAddItem = args;
- } else {
- completeAdd(args);
- mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
- }
+ if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) {
+ completeAdd(requestCode, data, -1, requestArgs);
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+
} else if (resultCode == RESULT_CANCELED) {
mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
}
mDragLayer.clearAnimatedView();
-
}
@Override
@@ -851,15 +820,18 @@
/** @Override for MNC */
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
- if (requestCode == REQUEST_PERMISSION_CALL_PHONE && sPendingAddItem != null
- && sPendingAddItem.requestCode == REQUEST_PERMISSION_CALL_PHONE) {
+ PendingRequestArgs pendingArgs = mPendingRequestArgs;
+ if (requestCode == REQUEST_PERMISSION_CALL_PHONE && pendingArgs != null
+ && pendingArgs.getRequestCode() == REQUEST_PERMISSION_CALL_PHONE) {
+ setWaitingForResult(null);
+
View v = null;
- CellLayout layout = getCellLayout(sPendingAddItem.container, sPendingAddItem.screenId);
+ CellLayout layout = getCellLayout(pendingArgs.container, pendingArgs.screenId);
if (layout != null) {
- v = layout.getChildAt(sPendingAddItem.cellX, sPendingAddItem.cellY);
+ v = layout.getChildAt(pendingArgs.cellX, pendingArgs.cellY);
}
- Intent intent = sPendingAddItem.intent;
- sPendingAddItem = null;
+ Intent intent = pendingArgs.getPendingIntent();
+
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startActivitySafely(v, intent, null);
@@ -875,19 +847,6 @@
}
}
- private PendingAddArguments preparePendingAddArgs(int requestCode, Intent data, int
- appWidgetId, ItemInfo info) {
- PendingAddArguments args = new PendingAddArguments();
- args.requestCode = requestCode;
- args.intent = data;
- args.container = info.container;
- args.screenId = info.screenId;
- args.cellX = info.cellX;
- args.cellY = info.cellY;
- args.appWidgetId = appWidgetId;
- return args;
- }
-
/**
* Check to see if a given screen id exists. If not, create it at the end, return the new id.
*
@@ -906,8 +865,9 @@
}
}
- @Thunk void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
- CellLayout cellLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
+ @Thunk void completeTwoStageWidgetDrop(
+ final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) {
+ CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId);
Runnable onCompleteRunnable = null;
int animationType = 0;
@@ -915,13 +875,12 @@
if (resultCode == RESULT_OK) {
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
- mPendingAddWidgetInfo);
+ requestArgs.getWidgetProvider());
boundWidget = layout;
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
- mPendingAddInfo.screenId, layout, null);
+ completeAddAppWidget(appWidgetId, requestArgs, layout, null);
exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
}
@@ -931,7 +890,7 @@
animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
}
if (mDragLayer.getAnimatedView() != null) {
- mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
+ mWorkspace.animateWidgetDrop(requestArgs, cellLayout,
(DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
animationType, boundWidget, true);
} else if (onCompleteRunnable != null) {
@@ -992,17 +951,16 @@
// view after launching an app, as they may be depending on the UI to be static to
// switch to another app, otherwise, if it was
showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
- false /* focusSearchBar */);
+ mAppsView.shouldRestoreImeState() /* focusSearchBar */);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
mOnResumeState = State.NONE;
mPaused = false;
- if (mRestoring || mOnResumeNeedsLoad) {
+ if (mOnResumeNeedsLoad) {
setWorkspaceLoading(true);
mModel.startLoader(getCurrentWorkspaceScreen());
- mRestoring = false;
mOnResumeNeedsLoad = false;
}
if (mBindOnResumeCallbacks.size() > 0) {
@@ -1274,7 +1232,8 @@
return mDefaultKeySsb.toString();
}
- private void clearTypedText() {
+ @Override
+ public void clearTypedText() {
mDefaultKeySsb.clear();
mDefaultKeySsb.clearSpans();
Selection.setSelection(mDefaultKeySsb, 0);
@@ -1317,18 +1276,12 @@
mWorkspace.setRestorePage(currentScreen);
}
- ContentValues itemValues = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_ITEM);
- if (itemValues != null) {
- mPendingAddInfo.readFromValues(itemValues);
- AppWidgetProviderInfo info = savedState.getParcelable(
- RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
- mPendingAddWidgetInfo = info == null ?
- null : LauncherAppWidgetProviderInfo.fromProviderInfo(this, info);
-
- mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
- setWaitingForResult(true);
- mRestoring = true;
+ PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
+ if (requestArgs != null) {
+ setWaitingForResult(requestArgs);
}
+
+ mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
}
/**
@@ -1504,12 +1457,19 @@
* @param data The intent describing the shortcut.
*/
private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
- int cellY) {
+ int cellY, PendingRequestArgs args) {
int[] cellXY = mTmpAddItemCellCoordinates;
CellLayout layout = getCellLayout(container, screenId);
ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
- if (info == null) {
+ if (info == null || args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
+ args.getPendingIntent().getComponent() == null) {
+ return;
+ }
+ if (!PackageManagerHelper.hasPermissionForActivity(
+ this, info.intent, args.getPendingIntent().getComponent().getPackageName())) {
+ // The app is trying to add a shortcut without sufficient permissions
+ Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
return;
}
final View view = createShortcut(info);
@@ -1543,10 +1503,8 @@
LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1]);
- if (!mRestoring) {
- mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
- isWorkspaceLocked());
- }
+ mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
+ isWorkspaceLocked());
}
/**
@@ -1554,10 +1512,9 @@
*
* @param appWidgetId The app widget id
*/
- @Thunk void completeAddAppWidget(int appWidgetId, long container, long screenId,
+ @Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
- ItemInfo info = mPendingAddInfo;
if (appWidgetInfo == null) {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
}
@@ -1568,24 +1525,21 @@
LauncherAppWidgetInfo launcherInfo;
launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
- launcherInfo.spanX = info.spanX;
- launcherInfo.spanY = info.spanY;
- launcherInfo.minSpanX = info.minSpanX;
- launcherInfo.minSpanY = info.minSpanY;
+ launcherInfo.spanX = itemInfo.spanX;
+ launcherInfo.spanY = itemInfo.spanY;
+ launcherInfo.minSpanX = itemInfo.minSpanX;
+ launcherInfo.minSpanY = itemInfo.minSpanY;
launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
LauncherModel.addItemToDatabase(this, launcherInfo,
- container, screenId, info.cellX, info.cellY);
+ itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
- if (!mRestoring) {
- if (hostView == null) {
- // Perform actual inflation because we're live
- hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
- }
- hostView.setVisibility(View.VISIBLE);
- addAppWidgetToWorkspace(hostView, launcherInfo, appWidgetInfo, isWorkspaceLocked());
+ if (hostView == null) {
+ // Perform actual inflation because we're live
+ hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
}
- resetAddInfo();
+ hostView.setVisibility(View.VISIBLE);
+ addAppWidgetToWorkspace(hostView, launcherInfo, appWidgetInfo, isWorkspaceLocked());
}
private void addAppWidgetToWorkspace(
@@ -1616,8 +1570,7 @@
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
- if (mAppsView != null && mWidgetsView != null &&
- mPendingAddInfo.container == ItemInfo.NO_ID) {
+ if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) {
if (!showWorkspace(false)) {
// If we are already on the workspace, then manually reset all apps
mAppsView.reset();
@@ -1829,7 +1782,7 @@
getWindow().closeAllPanels();
// Whatever we were doing is hereby canceled.
- setWaitingForResult(false);
+ setWaitingForResult(null);
}
@Override
@@ -1947,13 +1900,11 @@
closeFolder(false);
closeShortcutsContainer(false);
- if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
- mWaitingForResult) {
- ContentValues itemValues = new ContentValues();
- mPendingAddInfo.writeToValues(itemValues);
- outState.putParcelable(RUNTIME_STATE_PENDING_ADD_ITEM, itemValues);
- outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
+ if (mPendingRequestArgs != null) {
+ outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
+ }
+ if (mPendingActivityResult != null) {
+ outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
}
if (mLauncherCallbacks != null) {
@@ -2016,14 +1967,12 @@
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- onStartForResult(requestCode);
super.startActivityForResult(intent, requestCode, options);
}
@Override
public void startIntentSenderForResult (IntentSender intent, int requestCode,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
- onStartForResult(requestCode);
try {
super.startIntentSenderForResult(intent, requestCode,
fillInIntent, flagsMask, flagsValues, extraFlags, options);
@@ -2032,12 +1981,6 @@
}
}
- private void onStartForResult(int requestCode) {
- if (requestCode >= 0) {
- setWaitingForResult(true);
- }
- }
-
/**
* Indicates that we want global search for this activity by setting the globalSearch
* argument for {@link #startSearch} to true.
@@ -2055,13 +1998,10 @@
appSearchData.putString("source", "launcher-search");
}
- // TODO send proper bounds.
- Rect sourceBounds = null;
-
- boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
- appSearchData, sourceBounds);
- if (clearTextImmediately) {
- clearTypedText();
+ if (mLauncherCallbacks == null ||
+ !mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) {
+ // Starting search from the callbacks failed. Start the default global search.
+ startGlobalSearch(initialQuery, selectInitialQuery, appSearchData, null);
}
// We need to show the workspace after starting the search
@@ -2069,28 +2009,9 @@
}
/**
- * Start a text search.
- *
- * @return {@code true} if the search will start immediately, so any further keypresses
- * will be handled directly by the search UI. {@code false} if {@link Launcher} should continue
- * to buffer keypresses.
- */
- public boolean startSearch(String initialQuery,
- boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
- if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) {
- return mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData,
- sourceBounds);
- }
-
- startGlobalSearch(initialQuery, selectInitialQuery,
- appSearchData, sourceBounds);
- return false;
- }
-
- /**
* Starts the global search activity. This code is a copied from SearchManager
*/
- private void startGlobalSearch(String initialQuery,
+ public void startGlobalSearch(String initialQuery,
boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
final SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
@@ -2148,7 +2069,7 @@
}
public boolean isWorkspaceLocked() {
- return mWorkspaceLoading || mWaitingForResult;
+ return mWorkspaceLoading || mPendingRequestArgs != null;
}
public boolean isWorkspaceLoading() {
@@ -2163,9 +2084,9 @@
}
}
- private void setWaitingForResult(boolean value) {
+ private void setWaitingForResult(PendingRequestArgs args) {
boolean isLocked = isWorkspaceLocked();
- mWaitingForResult = value;
+ mPendingRequestArgs = args;
if (isLocked != isWorkspaceLocked()) {
onWorkspaceLockedChanged();
}
@@ -2177,33 +2098,23 @@
}
}
- private void resetAddInfo() {
- mPendingAddInfo.container = ItemInfo.NO_ID;
- mPendingAddInfo.screenId = -1;
- mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
- mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
- mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
- }
-
- void addAppWidgetFromDropImpl(final int appWidgetId, final ItemInfo info, final
- AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) {
+ void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
+ LauncherAppWidgetProviderInfo appWidgetInfo) {
if (LOGD) {
Log.d(TAG, "Adding widget from drop");
}
addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
}
- void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
- final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo,
+ void addAppWidgetImpl(int appWidgetId, ItemInfo info,
+ AppWidgetHostView boundWidget, LauncherAppWidgetProviderInfo appWidgetInfo,
int delay) {
if (appWidgetInfo.configure != null) {
- mPendingAddWidgetInfo = appWidgetInfo;
- mPendingAddWidgetId = appWidgetId;
+ setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, appWidgetInfo, info));
// Launch over to configure widget, if needed
mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
-
} else {
// Otherwise just add it
Runnable onComplete = new Runnable() {
@@ -2214,8 +2125,7 @@
null);
}
};
- completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
- appWidgetInfo);
+ completeAddAppWidget(appWidgetId, info, boundWidget, appWidgetInfo);
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
}
}
@@ -2228,17 +2138,22 @@
public void addPendingItem(PendingAddItemInfo info, long container, long screenId,
int[] cell, int spanX, int spanY) {
+ info.container = container;
+ info.screenId = screenId;
+ if (cell != null) {
+ info.cellX = cell[0];
+ info.cellY = cell[1];
+ }
+ info.spanX = spanX;
+ info.spanY = spanY;
+
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- int span[] = new int[2];
- span[0] = spanX;
- span[1] = spanY;
- addAppWidgetFromDrop((PendingAddWidgetInfo) info,
- container, screenId, cell, span);
+ addAppWidgetFromDrop((PendingAddWidgetInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- processShortcutFromDrop(info.componentName, container, screenId, cell);
+ processShortcutFromDrop(info);
break;
default:
throw new IllegalStateException("Unknown item type: " + info.itemType);
@@ -2247,51 +2162,17 @@
/**
* Process a shortcut drop.
- *
- * @param componentName The name of the component
- * @param screenId The ID of the screen where it should be added
- * @param cell The cell it should be added to, optional
*/
- private void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
- int[] cell) {
- resetAddInfo();
- mPendingAddInfo.container = container;
- mPendingAddInfo.screenId = screenId;
-
- if (cell != null) {
- mPendingAddInfo.cellX = cell[0];
- mPendingAddInfo.cellY = cell[1];
- }
-
- Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
- createShortcutIntent.setComponent(componentName);
- Utilities.startActivityForResultSafely(this, createShortcutIntent, REQUEST_CREATE_SHORTCUT);
+ private void processShortcutFromDrop(PendingAddItemInfo info) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName);
+ setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info));
+ Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
}
/**
* Process a widget drop.
- *
- * @param info The PendingAppWidgetInfo of the widget being added.
- * @param screenId The ID of the screen where it should be added
- * @param cell The cell it should be added to, optional
*/
- private void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
- int[] cell, int[] span) {
- resetAddInfo();
- mPendingAddInfo.container = info.container = container;
- mPendingAddInfo.screenId = info.screenId = screenId;
- mPendingAddInfo.minSpanX = info.minSpanX;
- mPendingAddInfo.minSpanY = info.minSpanY;
-
- if (cell != null) {
- mPendingAddInfo.cellX = cell[0];
- mPendingAddInfo.cellY = cell[1];
- }
- if (span != null) {
- mPendingAddInfo.spanX = span[0];
- mPendingAddInfo.spanY = span[1];
- }
-
+ private void addAppWidgetFromDrop(PendingAddWidgetInfo info) {
AppWidgetHostView hostView = info.boundWidget;
int appWidgetId;
if (hostView != null) {
@@ -2317,11 +2198,11 @@
if (success) {
addAppWidgetFromDropImpl(appWidgetId, info, null, info.info);
} else {
- mPendingAddWidgetInfo = info.info;
+ setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, info.info, info));
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
- mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+ mAppWidgetManager.getUser(info.info)
.addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
@@ -2540,14 +2421,13 @@
LauncherAppWidgetProviderInfo appWidgetInfo =
mAppWidgetManager.findProvider(info.providerName, info.user);
if (appWidgetInfo != null) {
- mPendingAddWidgetId = info.appWidgetId;
- mPendingAddInfo.copyFrom(info);
- mPendingAddWidgetInfo = appWidgetInfo;
+ setWaitingForResult(PendingRequestArgs
+ .forWidgetInfo(info.appWidgetId, appWidgetInfo, info));
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingAddWidgetId);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, info.appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, appWidgetInfo.provider);
- mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+ mAppWidgetManager.getUser(appWidgetInfo)
.addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
startActivityForResult(intent, REQUEST_BIND_PENDING_APPWIDGET);
}
@@ -2576,9 +2456,7 @@
private void startRestoredWidgetReconfigActivity(
LauncherAppWidgetProviderInfo provider, LauncherAppWidgetInfo info) {
- mPendingAddWidgetInfo = provider;
- mPendingAddInfo.copyFrom(info);
- mPendingAddWidgetId = info.appWidgetId;
+ setWaitingForResult(PendingRequestArgs.forWidgetInfo(info.appWidgetId, provider, info));
mAppWidgetManager.startConfigActivity(provider,
info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
}
@@ -2748,6 +2626,7 @@
int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
+ setWaitingForResult(new PendingRequestArgs(new ItemInfo()));
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
.setPackage(pickerPackage)
.putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
@@ -2864,9 +2743,9 @@
&& Intent.ACTION_CALL.equals(intent.getAction())
&& checkSelfPermission(Manifest.permission.CALL_PHONE) !=
PackageManager.PERMISSION_GRANTED) {
- // TODO: Rename sPendingAddItem to a generic name.
- sPendingAddItem = preparePendingAddArgs(REQUEST_PERMISSION_CALL_PHONE, intent,
- 0, info);
+
+ setWaitingForResult(PendingRequestArgs
+ .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
REQUEST_PERMISSION_CALL_PHONE);
} else {
@@ -3208,7 +3087,7 @@
ItemInfo info = (ItemInfo) v.getTag();
longClickCellInfo = new CellLayout.CellInfo(v, info);
itemUnderLongClick = longClickCellInfo.cell;
- resetAddInfo();
+ mPendingRequestArgs = null;
}
// The hotseat touch handling does not go through Workspace, and we always allow long press
@@ -3526,10 +3405,6 @@
// TODO
}
- public boolean launcherCallbacksProvidesSearch() {
- return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
- }
-
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
final boolean result = super.dispatchPopulateAccessibilityEvent(event);
@@ -4083,21 +3958,10 @@
setWorkspaceLoading(false);
- // If we received the result of any pending adds while the loader was running (e.g. the
- // widget configuration forced an orientation change), process them now.
- if (sPendingAddItem != null) {
- final long screenId = completeAdd(sPendingAddItem);
-
- // TODO: this moves the user to the page where the pending item was added. Ideally,
- // the screen would be guaranteed to exist after bind, and the page would be set through
- // the workspace restore process.
- mWorkspace.post(new Runnable() {
- @Override
- public void run() {
- mWorkspace.snapToScreenId(screenId);
- }
- });
- sPendingAddItem = null;
+ if (mPendingActivityResult != null) {
+ handleActivityResult(mPendingActivityResult.requestCode,
+ mPendingActivityResult.resultCode, mPendingActivityResult.data);
+ mPendingActivityResult = null;
}
InstallShortcutReceiver.disableAndFlushInstallQueue(this);
@@ -4513,8 +4377,8 @@
Log.d(TAG, "BEGIN launcher3 dump state for launcher " + this);
Log.d(TAG, "mSavedState=" + mSavedState);
Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
- Log.d(TAG, "mRestoring=" + mRestoring);
- Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
+ Log.d(TAG, "mPendingRequestArgs=" + mPendingRequestArgs);
+ Log.d(TAG, "mPendingActivityResult=" + mPendingActivityResult);
mModel.dumpState();
// TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 4a58e51..6394b90 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -79,9 +79,11 @@
@Deprecated
public void onWorkspaceLockedChanged();
- public boolean providesSearch();
- public boolean startSearch(String initialQuery, boolean selectInitialQuery,
- Bundle appSearchData, Rect sourceBounds);
+ /**
+ * Starts a search with {@param initialQuery}. Return false if search was not started.
+ */
+ public boolean startSearch(
+ String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
public boolean hasCustomContentToLeft();
public void populateCustomContentContainer();
public View getQsbBar();
diff --git a/src/com/android/launcher3/LauncherExterns.java b/src/com/android/launcher3/LauncherExterns.java
index c7a8668..887859c 100644
--- a/src/com/android/launcher3/LauncherExterns.java
+++ b/src/com/android/launcher3/LauncherExterns.java
@@ -29,4 +29,6 @@
public SharedPreferences getSharedPrefs();
public void setLauncherOverlay(Launcher.LauncherOverlay overlay);
+
+ void clearTypedText();
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 9d7be16..fb93743 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -158,7 +158,7 @@
*/
Intent promisedIntent;
- ShortcutInfo() {
+ public ShortcutInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d5f1363..0bb8cbf 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -76,7 +76,6 @@
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutsContainerListener;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -85,6 +84,7 @@
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.MultiStateAlphaController;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -594,7 +594,18 @@
}
// Add the first page
CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
-
+ final VerticalFlingDetector detector = new VerticalFlingDetector(mLauncher){
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ if (shouldConsumeTouch(v)) return true;
+ if (super.onTouch(v, ev)) {
+ mLauncher.startSearch("", false, null, false);
+ }
+ return false;
+ }
+ };
+ firstPage.setOnTouchListener(detector);
+ firstPage.setOnInterceptTouchListener(detector);
// Always add a QSB on the first screen.
if (qsb == null) {
// In transposed layout, we add the QSB in the Grid. As workspace does not touch the
@@ -686,7 +697,6 @@
// created CellLayout.
CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
-
newScreen.setOnLongClickListener(mLongClickListener);
newScreen.setOnClickListener(mLauncher);
newScreen.setSoundEffectsEnabled(false);
@@ -1172,6 +1182,10 @@
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
+ return shouldConsumeTouch(v);
+ }
+
+ private boolean shouldConsumeTouch(View v) {
return (workspaceInModalState() || !isFinishedSwitchingState())
|| (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
}
@@ -2907,7 +2921,7 @@
private void cleanupAddToFolder() {
if (mDragOverFolderIcon != null) {
- mDragOverFolderIcon.onDragExit(null);
+ mDragOverFolderIcon.onDragExit();
mDragOverFolderIcon = null;
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index dafa73f..cfd07e6 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -96,14 +96,14 @@
public AllAppsBackgroundDrawable(Context context) {
Resources res = context.getResources();
mHand = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_hand,
- 0.575f, 0.1f, Gravity.CENTER_HORIZONTAL);
+ 0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
mIcons = new TransformedImageDrawable[4];
mIcons[0] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_1,
0.375f, 0, Gravity.CENTER_HORIZONTAL);
mIcons[1] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_2,
- 0.3125f, 0.25f, Gravity.CENTER_HORIZONTAL);
+ 0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
mIcons[2] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_3,
- 0.475f, 0.4f, Gravity.CENTER_HORIZONTAL);
+ 0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
mIcons[3] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_4,
0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 13e2357..290accb 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -25,6 +25,7 @@
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -713,4 +714,8 @@
public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
targetParent.containerType = mAppsRecyclerView.getContainerType(v);
}
+
+ public boolean shouldRestoreImeState() {
+ return !TextUtils.isEmpty(mSearchInput.getText());
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 9a48367..41abb4c 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -163,8 +163,7 @@
* Focuses the search field to handle key events.
*/
public void focusSearchField() {
- mInput.requestFocus();
- mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
+ mInput.showKeyboard();
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 5888230..9fcc6a4 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -6,6 +6,7 @@
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
+import android.support.v4.content.ContextCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.Log;
import android.view.MotionEvent;
@@ -101,7 +102,7 @@
R.dimen.all_apps_bezel_swipe_height);
mEvaluator = new ArgbEvaluator();
- mAllAppsBackgroundColor = l.getColor(R.color.all_apps_container_color);
+ mAllAppsBackgroundColor = ContextCompat.getColor(l, R.color.all_apps_container_color);
}
@Override
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
index 10740ec..ac22dd2 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
@@ -22,15 +22,12 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.regex.Pattern;
/**
* The default search implementation.
*/
public class DefaultAppSearchAlgorithm {
- private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+");
-
private final List<AppInfo> mApps;
protected final Handler mResultHandler;
@@ -61,34 +58,79 @@
// Do an intersection of the words in the query and each title, and filter out all the
// apps that don't match all of the words in the query.
final String queryTextLower = query.toLowerCase();
- final String[] queryWords = SPLIT_PATTERN.split(queryTextLower);
-
final ArrayList<ComponentKey> result = new ArrayList<>();
for (AppInfo info : mApps) {
- if (matches(info, queryWords)) {
+ if (matches(info, queryTextLower)) {
result.add(info.toComponentKey());
}
}
return result;
}
- protected boolean matches(AppInfo info, String[] queryWords) {
+ protected boolean matches(AppInfo info, String query) {
+ int queryLength = query.length();
+
String title = info.title.toString();
- String[] words = SPLIT_PATTERN.split(title.toLowerCase());
- for (int qi = 0; qi < queryWords.length; qi++) {
- boolean foundMatch = false;
- for (int i = 0; i < words.length; i++) {
- if (words[i].startsWith(queryWords[qi])) {
- foundMatch = true;
- break;
- }
- }
- if (!foundMatch) {
- // If there is a word in the query that does not match any words in this
- // title, so skip it.
- return false;
+ int titleLength = title.length();
+
+ if (titleLength < queryLength || queryLength <= 0) {
+ return false;
+ }
+
+ int lastType;
+ int thisType = Character.UNASSIGNED;
+ int nextType = Character.getType(title.codePointAt(0));
+
+ int end = titleLength - queryLength;
+ for (int i = 0; i <= end; i++) {
+ lastType = thisType;
+ thisType = nextType;
+ nextType = i < (titleLength - 1) ?
+ Character.getType(title.codePointAt(i + 1)) : Character.UNASSIGNED;
+ if (isBreak(thisType, lastType, nextType) &&
+ title.substring(i, i + queryLength).equalsIgnoreCase(query)) {
+ return true;
}
}
- return true;
+ return false;
+ }
+
+ /**
+ * Returns true if the current point should be a break point. Following cases
+ * are considered as break points:
+ * 1) Any non space character after a space character
+ * 2) Any digit after a non-digit character
+ * 3) Any capital character after a digit or small character
+ * 4) Any capital character before a small character
+ */
+ protected boolean isBreak(int thisType, int prevType, int nextType) {
+ switch (thisType) {
+ case Character.UPPERCASE_LETTER:
+ if (nextType == Character.UPPERCASE_LETTER) {
+ return true;
+ }
+ // Follow through
+ case Character.TITLECASE_LETTER:
+ // Break point if previous was not a upper case
+ return prevType != Character.UPPERCASE_LETTER;
+ case Character.LOWERCASE_LETTER:
+ // Break point if previous was not a letter.
+ return prevType > Character.OTHER_LETTER;
+ case Character.DECIMAL_DIGIT_NUMBER:
+ case Character.LETTER_NUMBER:
+ case Character.OTHER_NUMBER:
+ // Break point if previous was not a number
+ return !(prevType == Character.DECIMAL_DIGIT_NUMBER
+ || prevType == Character.LETTER_NUMBER
+ || prevType == Character.OTHER_NUMBER);
+ case Character.MATH_SYMBOL:
+ case Character.CURRENCY_SYMBOL:
+ case Character.OTHER_PUNCTUATION:
+ case Character.DASH_PUNCTUATION:
+ // Always a break point for a symbol
+ return true;
+ default:
+ return false;
+ }
}
}
diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
new file mode 100644
index 0000000..156941a
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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.dragndrop;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
+/**
+ * DragSource used when the drag started at another window.
+ */
+public class AnotherWindowDragSource implements DragSource {
+
+ private final Context mContext;
+
+ AnotherWindowDragSource(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public boolean supportsFlingToDelete() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsAppInfoDropTarget() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDeleteDropTarget() {
+ return false;
+ }
+
+ @Override
+ public float getIntrinsicIconScaleFactor() {
+ return 1;
+ }
+
+ @Override
+ public void onFlingToDeleteCompleted() {
+ }
+
+ @Override
+ public void onDropCompleted(View target, DragObject d,
+ boolean isFlingToDelete, boolean success) {
+ if (!success) {
+ Launcher.getLauncher(mContext).exitSpringLoadedDragModeDelayed(false, 0, null);
+ }
+
+ }
+
+ @Override
+ public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+ // TODO: Probably log something
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 29e33e9..a93ee90 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -25,7 +25,6 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
-import android.util.Log;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -213,6 +212,12 @@
}
mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
+ mOptions = options;
+ if (mOptions.systemDndStartPoint != null) {
+ mMotionDownX = mOptions.systemDndStartPoint.x;
+ mMotionDownY = mOptions.systemDndStartPoint.y;
+ }
+
final int registrationX = mMotionDownX - dragLayerX;
final int registrationY = mMotionDownY - dragLayerY;
@@ -221,7 +226,6 @@
mLastDropTarget = null;
- mOptions = options;
mDragObject = new DropTarget.DragObject();
final Resources res = mLauncher.getResources();
@@ -241,7 +245,7 @@
mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
- mDragDriver = DragDriver.create(this, dragInfo, dragView);
+ mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions);
}
mDragObject.dragSource = source;
@@ -293,6 +297,10 @@
return mDragDriver != null || (mOptions != null && mOptions.isAccessibleDrag);
}
+ public boolean isExternalDrag() {
+ return (mOptions != null && mOptions.systemDndStartPoint != null);
+ }
+
/**
* Stop dragging without dropping.
*/
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 2164708..4db8c07 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -17,18 +17,19 @@
package com.android.launcher3.dragndrop;
import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
import android.content.Intent;
-import android.graphics.Canvas;
-import android.graphics.Point;
import android.view.DragEvent;
import android.view.MotionEvent;
-import android.view.View;
-import com.android.launcher3.AnotherWindowDropTarget;
import com.android.launcher3.DropTarget;
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
/**
* Base class for driving a drag/drop operation.
@@ -50,7 +51,7 @@
/**
* Handles ending of the DragView animation.
*/
- public abstract void onDragViewAnimationEnd();
+ public void onDragViewAnimationEnd() { }
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
@@ -89,100 +90,46 @@
return true;
}
- public static DragDriver create(
- DragController dragController, ItemInfo dragInfo, DragView dragView) {
- if (FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER && Utilities.isNycOrAbove()) {
- return new SystemDragDriver(dragController, dragInfo.getIntent(), dragView);
+ public static DragDriver create(Context context, DragController dragController,
+ DragObject dragObject, DragOptions options) {
+ if (Utilities.isNycOrAbove() && options.systemDndStartPoint != null) {
+ return new SystemDragDriver(dragController, context, dragObject);
} else {
return new InternalDragDriver(dragController);
}
}
-
-};
+}
/**
* Class for driving a system (i.e. framework) drag/drop operation.
*/
class SystemDragDriver extends DragDriver {
- /** Intent associated with the drag operation, or null is there no associated intent. */
- private final Intent mDragIntent;
- private final DragView mDragView;
- boolean mIsFrameworkDragActive = false;
+ private final DragObject mDragObject;
+ private final Context mContext;
+
boolean mReceivedDropEvent = false;
float mLastX = 0;
float mLastY = 0;
- public SystemDragDriver(DragController dragController, Intent dragIntent, DragView dragView) {
+ public SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
super(dragController);
- mDragIntent = dragIntent;
- mDragView = dragView;
- }
-
- private static class ShadowBuilder extends View.DragShadowBuilder {
- final DragView mDragView;
-
- public ShadowBuilder(DragView dragView) {
- mDragView = dragView;
- }
-
- @Override
- public void onProvideShadowMetrics (Point size, Point touch) {
- mDragView.provideDragShadowMetrics(size, touch);
- }
-
- @Override
- public void onDrawShadow(Canvas canvas) {
- mDragView.drawDragShadow(canvas);
- }
- };
-
- @Override
- public void onDragViewAnimationEnd() {
- // Clip data for the drag operation. If there is an intent, create an intent-based ClipData,
- // which will be passed to a global DND.
- // If there is no intent, craft a fake ClipData and start a local DND operation; this
- // ClipData will be ignored.
- final ClipData dragData = mDragIntent != null ?
- ClipData.newIntent("", mDragIntent) :
- ClipData.newPlainText("", "");
-
- View.DragShadowBuilder shadowBuilder = new ShadowBuilder(mDragView);
- // TODO: DND flags are in flux, once settled, use the appropriate constant.
- final int flagGlobal = 1 << 0;
- final int flagOpaque = 1 << 9;
- final int flags = (mDragIntent != null ? flagGlobal : 0) | flagOpaque;
-
- mIsFrameworkDragActive = true;
-
- if (!mDragView.startDrag(dragData, shadowBuilder, null, flags)) {
- mIsFrameworkDragActive = false;
- mEventListener.onDriverDragCancel();
- return;
- }
-
- // Starting from this point, the driver takes over showing the drag shadow, so hiding the
- // drag view.
- mDragView.setVisibility(View.INVISIBLE);
+ mDragObject = dragObject;
+ mContext = context;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
- return !mIsFrameworkDragActive && super.onTouchEvent(ev);
+ return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- return !mIsFrameworkDragActive && super.onInterceptTouchEvent(ev);
+ return false;
}
@Override
public boolean onDragEvent (DragEvent event) {
- if (!mIsFrameworkDragActive) {
- // We are interested only in drag events started by this driver.
- return false;
- }
-
final int action = event.getAction();
switch (action) {
@@ -192,8 +139,6 @@
return true;
case DragEvent.ACTION_DRAG_ENTERED:
- mLastX = event.getX();
- mLastY = event.getY();
return true;
case DragEvent.ACTION_DRAG_LOCATION:
@@ -205,35 +150,66 @@
case DragEvent.ACTION_DROP:
mLastX = event.getX();
mLastY = event.getY();
- mReceivedDropEvent = true;
- return true;
+ mReceivedDropEvent =
+ updateInfoFromClipData(event.getClipData(), event.getClipDescription());
+ return mReceivedDropEvent;
case DragEvent.ACTION_DRAG_EXITED:
- mLastX = event.getX();
- mLastY = event.getY();
mEventListener.onDriverDragExitWindow();
return true;
case DragEvent.ACTION_DRAG_ENDED:
- final boolean dragAccepted = event.getResult();
- final boolean acceptedByAnotherWindow = dragAccepted && !mReceivedDropEvent;
-
- // When the system drag ends, its drag shadow disappears. Resume showing the drag
- // view for the possible final animation.
- mDragView.setVisibility(View.VISIBLE);
-
- final DropTarget dropTargetOverride = acceptedByAnotherWindow ?
- new AnotherWindowDropTarget(mDragView.getContext()) : null;
-
- mEventListener.onDriverDragEnd(mLastX, mLastY, dropTargetOverride);
- mIsFrameworkDragActive = false;
+ if (mReceivedDropEvent) {
+ mEventListener.onDriverDragEnd(mLastX, mLastY, null);
+ } else {
+ mEventListener.onDriverDragCancel();
+ }
return true;
default:
return false;
}
}
-};
+
+ private boolean updateInfoFromClipData(ClipData data, ClipDescription desc) {
+ if (data == null) {
+ return false;
+ }
+ ArrayList<Intent> intents = new ArrayList<>();
+ int itemCount = data.getItemCount();
+ for (int i = 0; i < itemCount; i++) {
+ Intent intent = data.getItemAt(i).getIntent();
+ if (intent == null) {
+ continue;
+ }
+
+ // Give preference to shortcut intents.
+ if (!Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction())) {
+ intents.add(intent);
+ continue;
+ }
+ ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, intent);
+ if (info != null) {
+ mDragObject.dragInfo = info;
+ return true;
+ }
+ return true;
+ }
+
+ // Try creating shortcuts just using the intent and label
+ Intent fullIntent = new Intent().putExtra(Intent.EXTRA_SHORTCUT_NAME, desc.getLabel());
+ for (Intent intent : intents) {
+ fullIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
+ ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, fullIntent);
+ if (info != null) {
+ mDragObject.dragInfo = info;
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
/**
* Class for driving an internal (i.e. not using framework) drag/drop operation.
@@ -244,8 +220,5 @@
}
@Override
- public void onDragViewAnimationEnd() {}
-
- @Override
public boolean onDragEvent (DragEvent event) { return false; }
};
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 5863828..016347b 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -21,14 +21,22 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.content.ClipDescription;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -45,12 +53,14 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.PinchToOverviewListener;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -62,6 +72,7 @@
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
+import java.net.URISyntaxException;
import java.util.ArrayList;
/**
@@ -431,8 +442,46 @@
return false;
}
+ @TargetApi(Build.VERSION_CODES.N)
+ private void handleSystemDragStart(DragEvent event) {
+ if (!FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER || !Utilities.isNycOrAbove()) {
+ return;
+ }
+ if (mLauncher.isWorkspaceLocked()) {
+ return;
+ }
+
+ ClipDescription description = event.getClipDescription();
+ if (!description.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
+ return;
+ }
+ ShortcutInfo info = new ShortcutInfo();
+ // Set a dummy intent until we get the final value
+ info.intent = new Intent();
+
+ // Since we are not going through the workspace for starting the drag, set drag related
+ // information on the workspace before starting the drag.
+ ExternalDragPreviewProvider previewProvider =
+ new ExternalDragPreviewProvider(mLauncher, info);
+ mLauncher.getWorkspace().prepareDragWithProvider(previewProvider);
+
+ DragOptions options = new DragOptions();
+ options.systemDndStartPoint = new Point((int) event.getX(), (int) event.getY());
+
+ int halfPadding = previewProvider.previewPadding / 2;
+ mDragController.startDrag(
+ Bitmap.createBitmap(1, 1, Config.ARGB_8888),
+ 0, 0,
+ new AnotherWindowDragSource(mLauncher), info,
+ new Point(- halfPadding, halfPadding),
+ previewProvider.getPreviewBounds(), 1f, options);
+ }
+
@Override
public boolean onDragEvent (DragEvent event) {
+ if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+ handleSystemDragStart(event);
+ }
return mDragController.onDragEvent(event);
}
@@ -758,11 +807,15 @@
// If duration < 0, this is a cue to compute the duration based on the distance
if (duration < 0) {
- duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
- if (dist < maxDist) {
- duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
+ if (mDragController != null && mDragController.isExternalDrag()) {
+ duration = 1;
+ } else {
+ duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
+ if (dist < maxDist) {
+ duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
+ }
+ duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
}
- duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
}
// Fall back to cubic ease out interpolator for the animation if none is specified
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index a7f2872..3d52a48 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -16,6 +16,8 @@
package com.android.launcher3.dragndrop;
+import android.graphics.Point;
+
/**
* Set of options to control the drag and drop behavior.
*/
@@ -23,4 +25,7 @@
/** Whether or not an accessible drag operation is in progress. */
public boolean isAccessibleDrag = false;
+
+ /** Specifies the start location for the system DnD, null when using internal DnD */
+ public Point systemDndStartPoint = null;
}
diff --git a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
new file mode 100644
index 0000000..6b14be7
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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.dragndrop;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} which provides a dummy outline when drag starts from
+ * a different window.
+ * It just draws an empty circle to a placeholder outline.
+ */
+public class ExternalDragPreviewProvider extends DragPreviewProvider {
+
+ private final Launcher mLauncher;
+ private final ItemInfo mAddInfo;
+
+ private final int[] mOutlineSize;
+
+ public ExternalDragPreviewProvider(Launcher launcher, ItemInfo addInfo) {
+ super(null);
+ mLauncher = launcher;
+ mAddInfo = addInfo;
+
+ mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false);
+ }
+
+ public Rect getPreviewBounds() {
+ Rect rect = new Rect();
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ rect.left = DRAG_BITMAP_PADDING / 2;
+ rect.top = (mOutlineSize[1] - dp.cellHeightPx) / 2;
+ rect.right = rect.left + dp.iconSizePx;
+ rect.bottom = rect.top + dp.iconSizePx;
+ return rect;
+ }
+
+ @Override
+ public Bitmap createDragOutline(Canvas canvas) {
+ final Bitmap b = Bitmap.createBitmap(mOutlineSize[0], mOutlineSize[1], Bitmap.Config.ALPHA_8);
+ canvas.setBitmap(b);
+
+ Paint paint = new Paint();
+ paint.setColor(Color.WHITE);
+ paint.setStyle(Paint.Style.FILL);
+
+ // Use 0.9f times the radius for the actual circle to account for icon normalization.
+ float radius = getPreviewBounds().width() * 0.5f;
+ canvas.drawCircle(DRAG_BITMAP_PADDING / 2 + radius,
+ DRAG_BITMAP_PADDING / 2 + radius, radius * 0.9f, paint);
+
+ HolographicOutlineHelper.obtain(mLauncher).applyExpensiveOutlineWithBlur(b, canvas);
+ canvas.setBitmap(null);
+ return b;
+ }
+}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 19956a9..b64d12c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -50,6 +50,7 @@
import android.widget.TextView;
import com.android.launcher3.Alarm;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -163,10 +164,8 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mRearrangeOnClose = false;
boolean mItemsInvalidated = false;
- private ShortcutInfo mCurrentDragInfo;
private View mCurrentDragView;
private boolean mIsExternalDrag;
- boolean mSuppressOnAdd = false;
private boolean mDragInProgress = false;
private boolean mDeleteFolderOnDropCompleted = false;
private boolean mSuppressFolderDeletion = false;
@@ -291,7 +290,6 @@
return false;
}
- mCurrentDragInfo = item;
mEmptyCellRank = item.rank;
mCurrentDragView = v;
@@ -322,7 +320,15 @@
}
mContent.removeItem(mCurrentDragView);
- mInfo.remove(mCurrentDragInfo, true);
+ if (dragObject.dragInfo instanceof ShortcutInfo) {
+ mItemsInvalidated = true;
+
+ // We do not want to get events for the item being removed, as they will get handled
+ // when the drop completes
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.remove((ShortcutInfo) dragObject.dragInfo, true);
+ }
+ }
mDragInProgress = true;
mItemAddedBackToSelfViaIcon = false;
}
@@ -664,9 +670,8 @@
mContent.verifyVisibleHighResIcons(mContent.getNextPage());
}
- public void beginExternalDrag(ShortcutInfo item) {
- mCurrentDragInfo = item;
- mEmptyCellRank = mContent.allocateRankForNewItem(item);
+ public void beginExternalDrag() {
+ mEmptyCellRank = mContent.allocateRankForNewItem();
mIsExternalDrag = true;
mDragInProgress = true;
@@ -845,9 +850,7 @@
}
private void clearDragInfo() {
- mCurrentDragInfo = null;
mCurrentDragView = null;
- mSuppressOnAdd = false;
mIsExternalDrag = false;
}
@@ -911,9 +914,9 @@
mContent.arrangeChildren(views, views.size());
mItemsInvalidated = true;
- mSuppressOnAdd = true;
- mFolderIcon.onDrop(d);
- mSuppressOnAdd = false;
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mFolderIcon.onDrop(d);
+ }
}
if (target != this) {
@@ -930,9 +933,7 @@
mDeleteFolderOnDropCompleted = false;
mDragInProgress = false;
mItemAddedBackToSelfViaIcon = false;
- mCurrentDragInfo = null;
mCurrentDragView = null;
- mSuppressOnAdd = false;
// Reordering may have occured, and we need to save the new item locations. We do this once
// at the end to prevent unnecessary database operations.
@@ -1274,7 +1275,14 @@
mContent.completePendingPageChanges();
View currentDragView;
- ShortcutInfo si = mCurrentDragInfo;
+ final ShortcutInfo si;
+ if (d.dragInfo instanceof AppInfo) {
+ // Came from all apps -- make a copy.
+ si = ((AppInfo) d.dragInfo).makeShortcut();
+ } else {
+ // ShortcutInfo
+ si = (ShortcutInfo) d.dragInfo;
+ }
if (mIsExternalDrag) {
currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
// Actually move the item in the database if it was an external drag. Call this
@@ -1311,11 +1319,11 @@
rearrangeChildren();
// Temporarily suppress the listener, as we did all the work already here.
- mSuppressOnAdd = true;
- mInfo.add(si, false);
- mSuppressOnAdd = false;
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.add(si, false);
+ }
+
// Clear the drag info, as it is no longer being dragged.
- mCurrentDragInfo = null;
mDragInProgress = false;
if (mContent.getPageCount() > 1) {
@@ -1338,10 +1346,7 @@
@Override
public void onAdd(ShortcutInfo item) {
- // If the item was dropped onto this open folder, we have done the work associated
- // with adding the item to the folder, as indicated by mSuppressOnAdd being set
- if (mSuppressOnAdd) return;
- mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item));
+ mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
mItemsInvalidated = true;
LauncherModel.addOrMoveItemInDatabase(
mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
@@ -1349,9 +1354,6 @@
public void onRemove(ShortcutInfo item) {
mItemsInvalidated = true;
- // If this item is being dragged from this open folder, we have already handled
- // the work associated with removing the item, so we don't have to do anything here.
- if (item == mCurrentDragInfo) return;
View v = getViewForInfo(item);
mContent.removeItem(v);
if (mState == STATE_ANIMATING) {
@@ -1490,4 +1492,20 @@
}
}
};
+
+ /**
+ * Temporary resource held while we don't want to handle info changes
+ */
+ private class SuppressInfoChanges implements AutoCloseable {
+
+ SuppressInfoChanges() {
+ mInfo.removeListener(Folder.this);
+ }
+
+ @Override
+ public void close() {
+ mInfo.addListener(Folder.this);
+ updateTextViewFocus();
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index eebbfe8..69c2b0f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -125,8 +125,6 @@
Paint mBgPaint = new Paint();
private Alarm mOpenAlarm = new Alarm();
- @Thunk
- ItemInfo mDragInfo;
public FolderIcon(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -195,8 +193,6 @@
return super.onSaveInstanceState();
}
-
-
public Folder getFolder() {
return mFolder;
}
@@ -242,22 +238,11 @@
// Workspace#onDropExternal.
mOpenAlarm.setAlarm(ON_OPEN_DELAY);
}
- mDragInfo = dragInfo;
}
OnAlarmListener mOnOpenListener = new OnAlarmListener() {
public void onAlarm(Alarm alarm) {
- ShortcutInfo item;
- if (mDragInfo instanceof AppInfo) {
- // Came from all apps -- make a copy.
- item = ((AppInfo) mDragInfo).makeShortcut();
- item.spanX = 1;
- item.spanY = 1;
- } else {
- // ShortcutInfo
- item = (ShortcutInfo) mDragInfo;
- }
- mFolder.beginExternalDrag(item);
+ mFolder.beginExternalDrag();
mLauncher.openFolder(FolderIcon.this);
}
};
@@ -284,7 +269,7 @@
animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
// This will animate the dragView (srcView) into the new folder
- onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
+ onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
}
public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
@@ -298,18 +283,13 @@
onCompleteRunnable);
}
- public void onDragExit(Object dragInfo) {
- onDragExit();
- }
-
public void onDragExit() {
mBackground.animateToRest();
mOpenAlarm.cancelAlarm();
}
private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
- float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
- DragObject d) {
+ float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
item.cellX = -1;
item.cellY = -1;
@@ -379,7 +359,7 @@
item = (ShortcutInfo) d.dragInfo;
}
mFolder.notifyDrop();
- onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
+ onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
}
private void computePreviewDrawingParams(int drawableSize, int totalSize) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3df1d24..1171d48 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -195,7 +195,7 @@
* 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.
*/
- public int allocateRankForNewItem(ShortcutInfo info) {
+ public int allocateRankForNewItem() {
int rank = getItemCount();
ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
views.add(rank, null);
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 747c21b..fb9d2f7 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -226,11 +226,6 @@
public void setActiveMarker(int activePage) {
if (mActivePage != activePage) {
mActivePage = activePage;
-
- // Simulate a scroll change
- int totalScroll = mNumPages - 1;
- int currentScroll = mIsRtl ? (totalScroll - mActivePage) : mActivePage;
- setScroll(currentScroll, totalScroll);
}
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index ca3a2dd..350bc8a 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -159,6 +159,11 @@
}
@Override
+ public void setContentDescription(CharSequence contentDescription) {
+ mAllAppsHandle.setContentDescription(contentDescription);
+ }
+
+ @Override
public void setScroll(int currentScroll, int totalScroll) {
if (getAlpha() == 0) {
return;
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 3c7ba32..cfa6efd 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -578,8 +578,7 @@
} else {
// Close animation is not running.
if (mDeferContainerRemoval) {
- mDeferContainerRemoval = false;
- mLauncher.getDragLayer().removeView(this);
+ close();
}
}
}
@@ -600,7 +599,6 @@
mOpenCloseAnimator.cancel();
}
mIsOpen = false;
- mLauncher.getDragController().removeDragListener(this);
final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
final int shortcutCount = getShortcutCount();
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 0affc81..6797c7b 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -139,13 +139,8 @@
}
@Override
- public boolean providesSearch() {
- return false;
- }
-
- @Override
public boolean startSearch(String initialQuery, boolean selectInitialQuery,
- Bundle appSearchData, Rect sourceBounds) {
+ Bundle appSearchData) {
return false;
}
diff --git a/src/com/android/launcher3/util/ActivityResultInfo.java b/src/com/android/launcher3/util/ActivityResultInfo.java
new file mode 100644
index 0000000..2261bee
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityResultInfo.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Utility class which stores information from onActivityResult
+ */
+public class ActivityResultInfo implements Parcelable {
+
+ public final int requestCode;
+ public final int resultCode;
+ public final Intent data;
+
+ public ActivityResultInfo(int requestCode, int resultCode, Intent data) {
+ this.requestCode = requestCode;
+ this.resultCode = resultCode;
+ this.data = data;
+ }
+
+ private ActivityResultInfo(Parcel parcel) {
+ requestCode = parcel.readInt();
+ resultCode = parcel.readInt();
+ data = parcel.readInt() != 0 ? Intent.CREATOR.createFromParcel(parcel) : null;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(requestCode);
+ dest.writeInt(resultCode);
+ if (data != null) {
+ dest.writeInt(1);
+ data.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ public static final Parcelable.Creator<ActivityResultInfo> CREATOR =
+ new Parcelable.Creator<ActivityResultInfo>() {
+ public ActivityResultInfo createFromParcel(Parcel source) {
+ return new ActivityResultInfo(source);
+ }
+
+ public ActivityResultInfo[] newArray(int size) {
+ return new ActivityResultInfo[size];
+ }
+ };
+}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 3c4c79a..3e15d05 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,10 +16,15 @@
package com.android.launcher3.util;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.text.TextUtils;
import com.android.launcher3.Utilities;
@@ -96,4 +101,53 @@
}
return excludePackages.get(0);
}
+
+ /**
+ * Returns true if {@param srcPackage} has the permission required to start the activity from
+ * {@param intent}. If {@param srcPackage} is null, then the activity should not need
+ * any permissions
+ */
+ public static boolean hasPermissionForActivity(Context context, Intent intent,
+ String srcPackage) {
+ PackageManager pm = context.getPackageManager();
+ ResolveInfo target = pm.resolveActivity(intent, 0);
+ if (target == null) {
+ // Not a valid target
+ return false;
+ }
+ if (TextUtils.isEmpty(target.activityInfo.permission)) {
+ // No permission is needed
+ return true;
+ }
+ if (TextUtils.isEmpty(srcPackage)) {
+ // The activity requires some permission but there is no source.
+ return false;
+ }
+
+ // Source does not have sufficient permissions.
+ if(pm.checkPermission(target.activityInfo.permission, srcPackage) !=
+ PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ if (!Utilities.ATLEAST_MARSHMALLOW) {
+ // These checks are sufficient for below M devices.
+ return true;
+ }
+
+ // On M and above also check AppOpsManager for compatibility mode permissions.
+ if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
+ // There is no app-op for this permission, which could have been disabled.
+ return true;
+ }
+
+ // There is no direct way to check if the app-op is allowed for a particular app. Since
+ // app-op is only enabled for apps running in compatibility mode, simply block such apps.
+
+ try {
+ return pm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
+ } catch (NameNotFoundException e) { }
+
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
new file mode 100644
index 0000000..4e402f3
--- /dev/null
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.content.ContentValues;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+
+/**
+ * Utility class to store information regarding a pending request made by launcher. This information
+ * can be saved across launcher instances.
+ */
+public class PendingRequestArgs extends ItemInfo implements Parcelable {
+
+ private static final int TYPE_NONE = 0;
+ private static final int TYPE_INTENT = 1;
+ private static final int TYPE_APP_WIDGET = 2;
+
+ private final int mArg1;
+ private final int mObjectType;
+ private final Parcelable mObject;
+
+ public PendingRequestArgs(ItemInfo info) {
+ mArg1 = 0;
+ mObjectType = TYPE_NONE;
+ mObject = null;
+
+ copyFrom(info);
+ }
+
+ private PendingRequestArgs(int arg1, int objectType, Parcelable object) {
+ mArg1 = arg1;
+ mObjectType = objectType;
+ mObject = object;
+ }
+
+ public PendingRequestArgs(Parcel parcel) {
+ readFromValues(ContentValues.CREATOR.createFromParcel(parcel));
+
+ mArg1 = parcel.readInt();
+ mObjectType = parcel.readInt();
+ if (parcel.readInt() != 0) {
+ mObject = mObjectType == TYPE_INTENT
+ ? Intent.CREATOR.createFromParcel(parcel)
+ : new LauncherAppWidgetProviderInfo(parcel);
+ } else {
+ mObject = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ ContentValues itemValues = new ContentValues();
+ writeToValues(itemValues);
+
+ dest.writeInt(mArg1);
+ dest.writeInt(mObjectType);
+ if (mObject != null) {
+ dest.writeInt(1);
+ mObject.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ public LauncherAppWidgetProviderInfo getWidgetProvider() {
+ return mObjectType == TYPE_APP_WIDGET ? (LauncherAppWidgetProviderInfo) mObject : null;
+ }
+
+ public int getWidgetId() {
+ return mObjectType == TYPE_APP_WIDGET ? mArg1 : 0;
+ }
+
+ public Intent getPendingIntent() {
+ return mObjectType == TYPE_INTENT ? (Intent) mObject : null;
+ }
+
+ public int getRequestCode() {
+ return mObjectType == TYPE_INTENT ? mArg1 : 0;
+ }
+
+ public static PendingRequestArgs forWidgetInfo(
+ int appWidgetId, LauncherAppWidgetProviderInfo widgetInfo, ItemInfo info) {
+ PendingRequestArgs args = new PendingRequestArgs(appWidgetId, TYPE_APP_WIDGET, widgetInfo);
+ args.copyFrom(info);
+ return args;
+ }
+
+ public static PendingRequestArgs forIntent(int requestCode, Intent intent, ItemInfo info) {
+ PendingRequestArgs args = new PendingRequestArgs(requestCode, TYPE_INTENT, intent);
+ args.copyFrom(info);
+ return args;
+ }
+
+ public static final Parcelable.Creator<PendingRequestArgs> CREATOR =
+ new Parcelable.Creator<PendingRequestArgs>() {
+ public PendingRequestArgs createFromParcel(Parcel source) {
+ return new PendingRequestArgs(source);
+ }
+
+ public PendingRequestArgs[] newArray(int size) {
+ return new PendingRequestArgs[size];
+ }
+ };
+}
diff --git a/src/com/android/launcher3/util/TransformingTouchDelegate.java b/src/com/android/launcher3/util/TransformingTouchDelegate.java
index da1de5e..3197ba9 100644
--- a/src/com/android/launcher3/util/TransformingTouchDelegate.java
+++ b/src/com/android/launcher3/util/TransformingTouchDelegate.java
@@ -33,6 +33,10 @@
private final RectF mBounds;
+ private final RectF mTouchCheckBounds;
+ private float mTouchExtension;
+ private boolean mWasTouchOutsideBounds;
+
private View mDelegateView;
private boolean mDelegateTargeted;
@@ -41,10 +45,22 @@
mDelegateView = delegateView;
mBounds = new RectF();
+ mTouchCheckBounds = new RectF();
}
public void setBounds(int left, int top, int right, int bottom) {
mBounds.set(left, top, right, bottom);
+ updateTouchBounds();
+ }
+
+ public void extendTouchBounds(float extension) {
+ mTouchExtension = extension;
+ updateTouchBounds();
+ }
+
+ private void updateTouchBounds() {
+ mTouchCheckBounds.set(mBounds);
+ mTouchCheckBounds.inset(-mTouchExtension, -mTouchExtension);
}
public void setDelegateView(View view) {
@@ -62,8 +78,9 @@
boolean sendToDelegate = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- mDelegateTargeted = mBounds.contains(event.getX(), event.getY());
+ mDelegateTargeted = mTouchCheckBounds.contains(event.getX(), event.getY());
if (mDelegateTargeted) {
+ mWasTouchOutsideBounds = !mBounds.contains(event.getX(), event.getY());
sendToDelegate = true;
}
break;
@@ -78,9 +95,15 @@
}
boolean handled = false;
if (sendToDelegate) {
- event.offsetLocation(-mBounds.left, -mBounds.top);
+ float x = event.getX();
+ float y = event.getY();
+ if (mWasTouchOutsideBounds) {
+ event.setLocation(mBounds.centerX(), mBounds.centerY());
+ } else {
+ event.offsetLocation(-mBounds.left, -mBounds.top);
+ }
handled = mDelegateView.dispatchTouchEvent(event);
- event.offsetLocation(mBounds.left, mBounds.top);
+ event.setLocation(x, y);
}
return handled;
}
diff --git a/src/com/android/launcher3/util/VerticalFlingDetector.java b/src/com/android/launcher3/util/VerticalFlingDetector.java
new file mode 100644
index 0000000..5f2b3f3
--- /dev/null
+++ b/src/com/android/launcher3/util/VerticalFlingDetector.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+public class VerticalFlingDetector implements View.OnTouchListener {
+
+ private static final float CUSTOM_SLOP_MULTIPLIER = 2.2f;
+ private static final int SEC_IN_MILLIS = 1000;
+
+ private VelocityTracker mVelocityTracker;
+ private float mMinimumFlingVelocity;
+ private float mMaximumFlingVelocity;
+ private float mDownX, mDownY;
+ private boolean mShouldCheckFling;
+ private double mCustomTouchSlop;
+
+ public VerticalFlingDetector(Context context) {
+ ViewConfiguration vc = ViewConfiguration.get(context);
+ mMinimumFlingVelocity = vc.getScaledMinimumFlingVelocity();
+ mMaximumFlingVelocity = vc.getScaledMaximumFlingVelocity();
+ mCustomTouchSlop = CUSTOM_SLOP_MULTIPLIER * vc.getScaledTouchSlop();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownX = ev.getX();
+ mDownY = ev.getY();
+ mShouldCheckFling = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mShouldCheckFling) {
+ break;
+ }
+ if (Math.abs(ev.getY() - mDownY) > mCustomTouchSlop &&
+ Math.abs(ev.getY() - mDownY) > Math.abs(ev.getX() - mDownX)) {
+ mShouldCheckFling = true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mShouldCheckFling) {
+ mVelocityTracker.computeCurrentVelocity(SEC_IN_MILLIS, mMaximumFlingVelocity);
+ // only when fling is detected in down direction
+ if (mVelocityTracker.getYVelocity() > mMinimumFlingVelocity) {
+ cleanUp();
+ return true;
+ }
+ }
+ // fall through.
+ case MotionEvent.ACTION_CANCEL:
+ cleanUp();
+ }
+ return false;
+ }
+
+ private void cleanUp() {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+}
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
index 69d8f0f..81fa337 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -27,7 +27,7 @@
// As opposed to the new spring-loaded workspace.
public static boolean LAUNCHER3_LEGACY_WORKSPACE_DND = false;
public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
- public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = false;
+ public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = true;
public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
diff --git a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
new file mode 100644
index 0000000..4d0a7a9
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 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.allapps;
+
+import android.content.ComponentName;
+import android.test.InstrumentationTestCase;
+
+import com.android.launcher3.AppInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DefaultAppSearchAlgorithm}
+ */
+public class DefaultAppSearchAlgorithmTest extends InstrumentationTestCase {
+
+ private List<AppInfo> mAppsList;
+ private DefaultAppSearchAlgorithm mAlgorithm;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAppsList = new ArrayList<>();
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mAlgorithm = new DefaultAppSearchAlgorithm(mAppsList);
+ }
+ });
+ }
+
+ public void testMatches() {
+ assertTrue(mAlgorithm.matches(getInfo("white cow"), "cow"));
+ assertTrue(mAlgorithm.matches(getInfo("whiteCow"), "cow"));
+ assertTrue(mAlgorithm.matches(getInfo("whiteCOW"), "cow"));
+ assertTrue(mAlgorithm.matches(getInfo("whitecowCOW"), "cow"));
+ assertTrue(mAlgorithm.matches(getInfo("white2cow"), "cow"));
+
+ assertFalse(mAlgorithm.matches(getInfo("whitecow"), "cow"));
+ assertFalse(mAlgorithm.matches(getInfo("whitEcow"), "cow"));
+
+ assertTrue(mAlgorithm.matches(getInfo("whitecowCow"), "cow"));
+ assertTrue(mAlgorithm.matches(getInfo("whitecow cow"), "cow"));
+ assertFalse(mAlgorithm.matches(getInfo("whitecowcow"), "cow"));
+ assertFalse(mAlgorithm.matches(getInfo("whit ecowcow"), "cow"));
+
+ assertTrue(mAlgorithm.matches(getInfo("cats&dogs"), "dog"));
+ assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "dog"));
+ assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "&"));
+
+ assertTrue(mAlgorithm.matches(getInfo("2+43"), "43"));
+ assertFalse(mAlgorithm.matches(getInfo("2+43"), "3"));
+
+ assertTrue(mAlgorithm.matches(getInfo("Q"), "q"));
+ assertTrue(mAlgorithm.matches(getInfo(" Q"), "q"));
+ }
+
+ private AppInfo getInfo(String title) {
+ AppInfo info = new AppInfo();
+ info.title = title;
+ info.componentName = new ComponentName("Test", title);
+ return info;
+ }
+}