Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 276e3b7a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add search loader for installed apps."

parents cacb2f00 36d0d143
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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);
}
+6 −0
Original line number Diff line number Diff line
@@ -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);
+2 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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)
+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;
    }
}
+12 −2
Original line number Diff line number Diff line
@@ -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;

/**
@@ -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;
@@ -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