Merge "Decrease the animation delay when closing Folder from 2+ page." into ub-launcher3-dorval-polish
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 4303302..21eb3fb 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -20,6 +20,7 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.util.ComponentKey;
 
+import java.text.Collator;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -61,8 +62,9 @@
         // apps that don't match all of the words in the query.
         final String queryTextLower = query.toLowerCase();
         final ArrayList<ComponentKey> result = new ArrayList<>();
+        StringMatcher matcher = StringMatcher.getInstance();
         for (AppInfo info : mApps) {
-            if (matches(info, queryTextLower)) {
+            if (matches(info, queryTextLower, matcher)) {
                 result.add(info.toComponentKey());
             }
         }
@@ -70,6 +72,10 @@
     }
 
     public static boolean matches(AppInfo info, String query) {
+        return matches(info, query, StringMatcher.getInstance());
+    }
+
+    public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
         int queryLength = query.length();
 
         String title = info.title.toString();
@@ -90,7 +96,7 @@
             nextType = i < (titleLength - 1) ?
                     Character.getType(title.codePointAt(i + 1)) : Character.UNASSIGNED;
             if (isBreak(thisType, lastType, nextType) &&
-                    title.substring(i, i + queryLength).equalsIgnoreCase(query)) {
+                    matcher.matches(query, title.substring(i, i + queryLength))) {
                 return true;
             }
         }
@@ -106,6 +112,13 @@
      *      4) Any capital character before a small character
      */
     private static boolean isBreak(int thisType, int prevType, int nextType) {
+        switch (prevType) {
+            case Character.UNASSIGNED:
+            case Character.SPACE_SEPARATOR:
+            case Character.LINE_SEPARATOR:
+            case Character.PARAGRAPH_SEPARATOR:
+                return true;
+        }
         switch (thisType) {
             case Character.UPPERCASE_LETTER:
                 if (nextType == Character.UPPERCASE_LETTER) {
@@ -132,8 +145,44 @@
                 // Always a break point for a symbol
                 return true;
             default:
-                // Always a break point at first character
-                return  prevType == Character.UNASSIGNED;
+                return  false;
+        }
+    }
+
+    public static class StringMatcher {
+
+        private static final char MAX_UNICODE = '\uFFFF';
+
+        private final Collator mCollator;
+
+        StringMatcher() {
+            // On android N and above, Collator uses ICU implementation which has a much better
+            // support for non-latin locales.
+            mCollator = Collator.getInstance();
+            mCollator.setStrength(Collator.PRIMARY);
+            mCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+        }
+
+        /**
+         * Returns true if {@param query} is a prefix of {@param target}
+         */
+        public boolean matches(String query, String target) {
+            switch (mCollator.compare(query, target)) {
+                case 0:
+                    return true;
+                case -1:
+                    // The target string can contain a modifier which would make it larger than
+                    // the query string (even though the length is same). If the query becomes
+                    // larger after appending a unicode character, it was originally a prefix of
+                    // the target string and hence should match.
+                    return mCollator.compare(query + MAX_UNICODE, target) > -1;
+                default:
+                    return false;
+            }
+        }
+
+        public static StringMatcher getInstance() {
+            return new StringMatcher();
         }
     }
 }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 459a585..85792d4 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1359,8 +1359,11 @@
         }
         mContent.completePendingPageChanges();
 
-        if (d.dragInfo instanceof PendingAddShortcutInfo) {
-            PendingAddShortcutInfo pasi = (PendingAddShortcutInfo) d.dragInfo;
+        PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
+                ? (PendingAddShortcutInfo) d.dragInfo : null;
+        ShortcutInfo pasiSi = pasi != null ? pasi.activityInfo.createShortcutInfo() : null;
+        if (pasi != null && pasiSi == null) {
+            // There is no ShortcutInfo, so we have to go through a configuration activity.
             pasi.container = mInfo.id;
             pasi.rank = mEmptyCellRank;
 
@@ -1370,7 +1373,9 @@
             mRearrangeOnClose = true;
         } else {
             final ShortcutInfo si;
-            if (d.dragInfo instanceof AppInfo) {
+            if (pasiSi != null) {
+                si = pasiSi;
+            } else if (d.dragInfo instanceof AppInfo) {
                 // Came from all apps -- make a copy.
                 si = ((AppInfo) d.dragInfo).makeShortcut();
             } else {
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index 6b992fc..29834d7 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -21,7 +21,9 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
 
 /**
  * A PageIndicator that briefly shows a fraction of a line when moving between pages.
@@ -128,6 +130,10 @@
         mLauncher = Launcher.getLauncher(context);
         mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
         setCaretDrawable(new CaretDrawable(context));
+
+        boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
+        mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
+        mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
     }
 
     @Override
@@ -219,6 +225,9 @@
      * - mostly opaque black if the hotseat is black (ignoring alpha)
      */
     public void updateColor(ExtractedColors extractedColors) {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            return;
+        }
         int originalLineAlpha = mLinePaint.getAlpha();
         int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
         if (color != Color.TRANSPARENT) {
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index e8f13a1..62b6903 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.widget;
 
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 
diff --git a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
index 58dc0c4..26ec69b 100644
--- a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -19,6 +19,7 @@
 import android.test.InstrumentationTestCase;
 
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.Utilities;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -75,6 +76,25 @@
         assertTrue(mAlgorithm.matches(getInfo("电子邮件"), "电子"));
         assertFalse(mAlgorithm.matches(getInfo("电子邮件"), "子"));
         assertFalse(mAlgorithm.matches(getInfo("电子邮件"), "邮件"));
+
+        assertFalse(mAlgorithm.matches(getInfo("Bot"), "ba"));
+        assertFalse(mAlgorithm.matches(getInfo("bot"), "ba"));
+    }
+
+    public void testMatchesVN() {
+        if (!Utilities.ATLEAST_NOUGAT) {
+            return;
+        }
+        assertTrue(mAlgorithm.matches(getInfo("다운로드"), "다"));
+        assertTrue(mAlgorithm.matches(getInfo("드라이브"), "드"));
+        assertTrue(mAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷ"));
+        assertTrue(mAlgorithm.matches(getInfo("운로 드라이브"), "ㄷ"));
+        assertTrue(mAlgorithm.matches(getInfo("abc"), "åbç"));
+        assertTrue(mAlgorithm.matches(getInfo("Alpha"), "ål"));
+
+        assertFalse(mAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷㄷ"));
+        assertFalse(mAlgorithm.matches(getInfo("로드라이브"), "ㄷ"));
+        assertFalse(mAlgorithm.matches(getInfo("abc"), "åç"));
     }
 
     private AppInfo getInfo(String title) {