Loading src/com/android/settings/applications/PackageManagerWrapper.java +10 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settings.applications; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import java.util.List; Loading @@ -29,24 +30,30 @@ import java.util.List; * the API version supported by Robolectric. */ public interface PackageManagerWrapper { /** * Returns the real {@code PackageManager} object. */ PackageManager getPackageManager(); /** * Calls {@code PackageManager.getInstalledApplicationsAsUser()}. * * @see android.content.pm.PackageManager.PackageManager#getInstalledApplicationsAsUser * @see android.content.pm.PackageManager#getInstalledApplicationsAsUser */ List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId); /** * Calls {@code PackageManager.hasSystemFeature()}. * * @see android.content.pm.PackageManager.PackageManager#hasSystemFeature * @see android.content.pm.PackageManager#hasSystemFeature */ boolean hasSystemFeature(String name); /** * Calls {@code PackageManager.queryIntentActivitiesAsUser()}. * * @see android.content.pm.PackageManager.PackageManager#queryIntentActivitiesAsUser * @see android.content.pm.PackageManager#queryIntentActivitiesAsUser */ List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId); } src/com/android/settings/applications/PackageManagerWrapperImpl.java +6 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,18 @@ import android.content.pm.ResolveInfo; import java.util.List; public class PackageManagerWrapperImpl implements PackageManagerWrapper { private final PackageManager mPm; public PackageManagerWrapperImpl(PackageManager pm) { mPm = pm; } @Override public PackageManager getPackageManager() { return mPm; } @Override public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) { return mPm.getInstalledApplicationsAsUser(flags, userId); Loading src/com/android/settings/search2/DatabaseResultLoader.java +2 −2 Original line number Diff line number Diff line Loading @@ -23,10 +23,11 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.support.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.search.Index; import com.android.settings.search.IndexDatabaseHelper; import com.android.settings.utils.AsyncLoader; import com.android.settings.R; import java.util.ArrayList; import java.util.Collections; Loading Loading @@ -107,7 +108,6 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> { icon = mContext.getDrawable(R.drawable.ic_search_history); } SearchResult.Builder builder = new SearchResult.Builder(); builder.addTitle(title) .addSummary(summaryOn) Loading src/com/android/settings/search2/InstalledAppResultLoader.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * 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.settings.search2; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.net.Uri; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import com.android.settings.applications.PackageManagerWrapper; import com.android.settings.utils.AsyncLoader; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Search loader for installed apps. */ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> { private static final int NAME_NO_MATCH = -1; private static final int NAME_EXACT_MATCH = 0; private final String mQuery; private final UserManager mUserManager; private final PackageManagerWrapper mPackageManager; public InstalledAppResultLoader(Context context, PackageManagerWrapper pmWrapper, String query) { super(context); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mPackageManager = pmWrapper; mQuery = query; } @Override public List<SearchResult> loadInBackground() { final List<SearchResult> results = new ArrayList<>(); final PackageManager pm = mPackageManager.getPackageManager(); for (UserInfo user : getUsersToCount()) { final List<ApplicationInfo> apps = mPackageManager.getInstalledApplicationsAsUser( PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0), user.id); for (ApplicationInfo info : apps) { if (info.isSystemApp()) { continue; } final CharSequence label = info.loadLabel(pm); final int wordDiff = getWordDifference(label.toString(), mQuery); if (wordDiff == NAME_NO_MATCH) { continue; } final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", info.packageName, null)); final SearchResult.Builder builder = new SearchResult.Builder(); builder.addIcon(info.loadIcon(pm)) .addTitle(info.loadLabel(pm)) .addRank(wordDiff) .addPayload(new IntentPayload(intent)); results.add(builder.build()); } } Collections.sort(results); return results; } @Override protected void onDiscardResult(List<SearchResult> result) { } private List<UserInfo> getUsersToCount() { return mUserManager.getProfiles(UserHandle.myUserId()); } /** * Returns "difference" between appName and query string. appName must contain all * characters from query, in the same order. If not, returns NAME_NO_MATCH. If they do match, * returns an int value representing how different they are, NAME_EXACT_MATCH means they match * perfectly, and larger values means they are less similar. * <p/> * Example: * appName: Abcde, query: Abcde, Returns NAME_EXACT_MATCH * appName: Abcde, query: ade, Returns 2 * appName: Abcde, query: ae, Returns 3 * appName: Abcde, query: ea, Returns NAME_NO_MATCH * appName: Abcde, query: xyz, Returns NAME_NO_MATCH */ private int getWordDifference(String appName, String query) { if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) { return NAME_NO_MATCH; } final char[] queryTokens = query.toString().toLowerCase().toCharArray(); final char[] valueText = appName.toLowerCase().toCharArray(); if (queryTokens.length > valueText.length) { return NAME_NO_MATCH; } int i = 0; int j = 0; while (i < valueText.length && j < queryTokens.length) { if (valueText[i++] == queryTokens[j]) { j++; } } if (j != queryTokens.length) { return NAME_NO_MATCH; } // Use the diff in length as a proxy of how close the 2 words match. Value range from 0 // to infinity. return valueText.length - queryTokens.length; } } src/com/android/settings/search2/IntentSearchViewHolder.java +12 −2 Original line number Diff line number Diff line Loading @@ -15,9 +15,11 @@ */ package com.android.settings.search2; import android.app.Fragment; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.android.settings.R; /** Loading @@ -25,6 +27,7 @@ import com.android.settings.R; * The DatabaseResultLoader is the primary use case for this ViewHolder. */ public class IntentSearchViewHolder extends SearchViewHolder { public final TextView titleView; public final TextView summaryView; public final ImageView iconView; Loading @@ -36,9 +39,16 @@ public class IntentSearchViewHolder extends SearchViewHolder { iconView = (ImageView) view.findViewById(R.id.icon); } public void onBind(SearchResult result) { @Override public void onBind(Fragment fragment, SearchResult result) { titleView.setText(result.title); summaryView.setText(result.summary); iconView.setImageDrawable(result.icon); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { fragment.startActivity(((IntentPayload) result.payload).intent); } }); } } Loading
src/com/android/settings/applications/PackageManagerWrapper.java +10 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.settings.applications; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import java.util.List; Loading @@ -29,24 +30,30 @@ import java.util.List; * the API version supported by Robolectric. */ public interface PackageManagerWrapper { /** * Returns the real {@code PackageManager} object. */ PackageManager getPackageManager(); /** * Calls {@code PackageManager.getInstalledApplicationsAsUser()}. * * @see android.content.pm.PackageManager.PackageManager#getInstalledApplicationsAsUser * @see android.content.pm.PackageManager#getInstalledApplicationsAsUser */ List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId); /** * Calls {@code PackageManager.hasSystemFeature()}. * * @see android.content.pm.PackageManager.PackageManager#hasSystemFeature * @see android.content.pm.PackageManager#hasSystemFeature */ boolean hasSystemFeature(String name); /** * Calls {@code PackageManager.queryIntentActivitiesAsUser()}. * * @see android.content.pm.PackageManager.PackageManager#queryIntentActivitiesAsUser * @see android.content.pm.PackageManager#queryIntentActivitiesAsUser */ List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId); }
src/com/android/settings/applications/PackageManagerWrapperImpl.java +6 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,18 @@ import android.content.pm.ResolveInfo; import java.util.List; public class PackageManagerWrapperImpl implements PackageManagerWrapper { private final PackageManager mPm; public PackageManagerWrapperImpl(PackageManager pm) { mPm = pm; } @Override public PackageManager getPackageManager() { return mPm; } @Override public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) { return mPm.getInstalledApplicationsAsUser(flags, userId); Loading
src/com/android/settings/search2/DatabaseResultLoader.java +2 −2 Original line number Diff line number Diff line Loading @@ -23,10 +23,11 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.support.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.search.Index; import com.android.settings.search.IndexDatabaseHelper; import com.android.settings.utils.AsyncLoader; import com.android.settings.R; import java.util.ArrayList; import java.util.Collections; Loading Loading @@ -107,7 +108,6 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> { icon = mContext.getDrawable(R.drawable.ic_search_history); } SearchResult.Builder builder = new SearchResult.Builder(); builder.addTitle(title) .addSummary(summaryOn) Loading
src/com/android/settings/search2/InstalledAppResultLoader.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * 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.settings.search2; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.net.Uri; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import com.android.settings.applications.PackageManagerWrapper; import com.android.settings.utils.AsyncLoader; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Search loader for installed apps. */ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> { private static final int NAME_NO_MATCH = -1; private static final int NAME_EXACT_MATCH = 0; private final String mQuery; private final UserManager mUserManager; private final PackageManagerWrapper mPackageManager; public InstalledAppResultLoader(Context context, PackageManagerWrapper pmWrapper, String query) { super(context); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mPackageManager = pmWrapper; mQuery = query; } @Override public List<SearchResult> loadInBackground() { final List<SearchResult> results = new ArrayList<>(); final PackageManager pm = mPackageManager.getPackageManager(); for (UserInfo user : getUsersToCount()) { final List<ApplicationInfo> apps = mPackageManager.getInstalledApplicationsAsUser( PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0), user.id); for (ApplicationInfo info : apps) { if (info.isSystemApp()) { continue; } final CharSequence label = info.loadLabel(pm); final int wordDiff = getWordDifference(label.toString(), mQuery); if (wordDiff == NAME_NO_MATCH) { continue; } final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", info.packageName, null)); final SearchResult.Builder builder = new SearchResult.Builder(); builder.addIcon(info.loadIcon(pm)) .addTitle(info.loadLabel(pm)) .addRank(wordDiff) .addPayload(new IntentPayload(intent)); results.add(builder.build()); } } Collections.sort(results); return results; } @Override protected void onDiscardResult(List<SearchResult> result) { } private List<UserInfo> getUsersToCount() { return mUserManager.getProfiles(UserHandle.myUserId()); } /** * Returns "difference" between appName and query string. appName must contain all * characters from query, in the same order. If not, returns NAME_NO_MATCH. If they do match, * returns an int value representing how different they are, NAME_EXACT_MATCH means they match * perfectly, and larger values means they are less similar. * <p/> * Example: * appName: Abcde, query: Abcde, Returns NAME_EXACT_MATCH * appName: Abcde, query: ade, Returns 2 * appName: Abcde, query: ae, Returns 3 * appName: Abcde, query: ea, Returns NAME_NO_MATCH * appName: Abcde, query: xyz, Returns NAME_NO_MATCH */ private int getWordDifference(String appName, String query) { if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) { return NAME_NO_MATCH; } final char[] queryTokens = query.toString().toLowerCase().toCharArray(); final char[] valueText = appName.toLowerCase().toCharArray(); if (queryTokens.length > valueText.length) { return NAME_NO_MATCH; } int i = 0; int j = 0; while (i < valueText.length && j < queryTokens.length) { if (valueText[i++] == queryTokens[j]) { j++; } } if (j != queryTokens.length) { return NAME_NO_MATCH; } // Use the diff in length as a proxy of how close the 2 words match. Value range from 0 // to infinity. return valueText.length - queryTokens.length; } }
src/com/android/settings/search2/IntentSearchViewHolder.java +12 −2 Original line number Diff line number Diff line Loading @@ -15,9 +15,11 @@ */ package com.android.settings.search2; import android.app.Fragment; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.android.settings.R; /** Loading @@ -25,6 +27,7 @@ import com.android.settings.R; * The DatabaseResultLoader is the primary use case for this ViewHolder. */ public class IntentSearchViewHolder extends SearchViewHolder { public final TextView titleView; public final TextView summaryView; public final ImageView iconView; Loading @@ -36,9 +39,16 @@ public class IntentSearchViewHolder extends SearchViewHolder { iconView = (ImageView) view.findViewById(R.id.icon); } public void onBind(SearchResult result) { @Override public void onBind(Fragment fragment, SearchResult result) { titleView.setText(result.title); summaryView.setText(result.summary); iconView.setImageDrawable(result.icon); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { fragment.startActivity(((IntentPayload) result.payload).intent); } }); } }