1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.allapps;
17 
18 import android.os.Handler;
19 
20 import com.android.launcher3.AppInfo;
21 import com.android.launcher3.util.ComponentKey;
22 
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.regex.Pattern;
26 
27 /**
28  * The default search implementation.
29  */
30 public class DefaultAppSearchAlgorithm {
31 
32     private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+");
33 
34     private final List<AppInfo> mApps;
35     protected final Handler mResultHandler;
36 
DefaultAppSearchAlgorithm(List<AppInfo> apps)37     public DefaultAppSearchAlgorithm(List<AppInfo> apps) {
38         mApps = apps;
39         mResultHandler = new Handler();
40     }
41 
cancel(boolean interruptActiveRequests)42     public void cancel(boolean interruptActiveRequests) {
43         if (interruptActiveRequests) {
44             mResultHandler.removeCallbacksAndMessages(null);
45         }
46     }
47 
doSearch(final String query, final AllAppsSearchBarController.Callbacks callback)48     public void doSearch(final String query,
49             final AllAppsSearchBarController.Callbacks callback) {
50         final ArrayList<ComponentKey> result = getTitleMatchResult(query);
51         mResultHandler.post(new Runnable() {
52 
53             @Override
54             public void run() {
55                 callback.onSearchResult(query, result);
56             }
57         });
58     }
59 
getTitleMatchResult(String query)60     protected ArrayList<ComponentKey> getTitleMatchResult(String query) {
61         // Do an intersection of the words in the query and each title, and filter out all the
62         // apps that don't match all of the words in the query.
63         final String queryTextLower = query.toLowerCase();
64         final String[] queryWords = SPLIT_PATTERN.split(queryTextLower);
65 
66         final ArrayList<ComponentKey> result = new ArrayList<>();
67         for (AppInfo info : mApps) {
68             if (matches(info, queryWords)) {
69                 result.add(info.toComponentKey());
70             }
71         }
72         return result;
73     }
74 
matches(AppInfo info, String[] queryWords)75     protected boolean matches(AppInfo info, String[] queryWords) {
76         String title = info.title.toString();
77         String[] words = SPLIT_PATTERN.split(title.toLowerCase());
78         for (int qi = 0; qi < queryWords.length; qi++) {
79             boolean foundMatch = false;
80             for (int i = 0; i < words.length; i++) {
81                 if (words[i].startsWith(queryWords[qi])) {
82                     foundMatch = true;
83                     break;
84                 }
85             }
86             if (!foundMatch) {
87                 // If there is a word in the query that does not match any words in this
88                 // title, so skip it.
89                 return false;
90             }
91         }
92         return true;
93     }
94 }
95