Merge "Remove more things from search v1"
This commit is contained in:
committed by
Android (Google) Code Review
commit
dc660045bc
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.dashboard;
|
||||
|
||||
import static android.provider.SearchIndexablesContract.SITE_MAP_COLUMNS;
|
||||
import static com.android.settings.dashboard.DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.provider.SearchIndexablesContract.SiteMapColumns;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.support.v4.util.ArrayMap;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.IndexDatabaseHelper;
|
||||
import com.android.settings.search.IndexDatabaseHelper.IndexColumns;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A manager class that maintains a "site map" and look up breadcrumb for a certain page on demand.
|
||||
* <p/>
|
||||
* The methods on this class can only be called on a background thread.
|
||||
*/
|
||||
public class SiteMapManager {
|
||||
|
||||
private static final String TAG = "SiteMapManager";
|
||||
private static final boolean DEBUG_TIMING = false;
|
||||
|
||||
private static final String[] CLASS_TO_SCREEN_TITLE_COLUMNS = {
|
||||
IndexColumns.CLASS_NAME,
|
||||
IndexColumns.SCREEN_TITLE,
|
||||
};
|
||||
|
||||
private final List<SiteMapPair> mPairs = new ArrayList<>();
|
||||
|
||||
private boolean mInitialized;
|
||||
|
||||
/**
|
||||
* Given a fragment class name and its screen title, build a breadcrumb from Settings root to
|
||||
* this screen.
|
||||
* <p/>
|
||||
* Not all screens have a full breadcrumb path leading up to root, it's because either some
|
||||
* page in the breadcrumb path is not indexed, or it's only reachable via search.
|
||||
*/
|
||||
@WorkerThread
|
||||
public synchronized List<String> buildBreadCrumb(Context context, String clazz,
|
||||
String screenTitle) {
|
||||
init(context);
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final List<String> breadcrumbs = new ArrayList<>();
|
||||
if (!mInitialized) {
|
||||
Log.w(TAG, "SiteMap is not initialized yet, skipping");
|
||||
return breadcrumbs;
|
||||
}
|
||||
breadcrumbs.add(screenTitle);
|
||||
String currentClass = clazz;
|
||||
String currentTitle = screenTitle;
|
||||
// Look up current page's parent, if found add it to breadcrumb string list, and repeat.
|
||||
while (true) {
|
||||
final SiteMapPair pair = lookUpParent(currentClass, currentTitle);
|
||||
if (pair == null) {
|
||||
if (DEBUG_TIMING) {
|
||||
Log.d(TAG, "BreadCrumb timing: " + (System.currentTimeMillis() - startTime));
|
||||
}
|
||||
return breadcrumbs;
|
||||
}
|
||||
breadcrumbs.add(0, pair.parentTitle);
|
||||
currentClass = pair.parentClass;
|
||||
currentTitle = pair.parentTitle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a list of {@link SiteMapPair}s. Each pair knows about a single parent-child
|
||||
* page relationship.
|
||||
*
|
||||
* We get the knowledge of such mPairs from 2 sources:
|
||||
* 1. Static indexing time: we know which page(s) a parent can open by parsing its pref xml.
|
||||
* 2. IA: We know from {@link DashboardFeatureProvider} which page can be dynamically
|
||||
* injected to where.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@WorkerThread
|
||||
synchronized void init(Context context) {
|
||||
if (mInitialized) {
|
||||
// Make sure only init once.
|
||||
return;
|
||||
}
|
||||
final long startTime = System.currentTimeMillis();
|
||||
// First load site map from static index table.
|
||||
final Context appContext = context.getApplicationContext();
|
||||
final SQLiteDatabase db = IndexDatabaseHelper.getInstance(appContext).getReadableDatabase();
|
||||
Cursor sitemap = db.query(IndexDatabaseHelper.Tables.TABLE_SITE_MAP, SITE_MAP_COLUMNS, null,
|
||||
null, null, null, null);
|
||||
while (sitemap.moveToNext()) {
|
||||
final SiteMapPair pair = new SiteMapPair(
|
||||
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.PARENT_CLASS)),
|
||||
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.PARENT_TITLE)),
|
||||
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.CHILD_CLASS)),
|
||||
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.CHILD_TITLE)));
|
||||
mPairs.add(pair);
|
||||
}
|
||||
sitemap.close();
|
||||
|
||||
// Then prepare a local map that contains class name -> screen title mapping. This is needed
|
||||
// to figure out the display name for any fragment if it's injected dynamically through IA.
|
||||
final Map<String, String> classToTitleMap = new ArrayMap<>();
|
||||
final Cursor titleQuery = db.query(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX,
|
||||
CLASS_TO_SCREEN_TITLE_COLUMNS, null, null, null, null, null);
|
||||
while (titleQuery.moveToNext()) {
|
||||
classToTitleMap.put(
|
||||
titleQuery.getString(titleQuery.getColumnIndex(IndexColumns.CLASS_NAME)),
|
||||
titleQuery.getString(titleQuery.getColumnIndex(IndexColumns.SCREEN_TITLE)));
|
||||
}
|
||||
titleQuery.close();
|
||||
|
||||
// Loop through all IA categories and pages and build additional SiteMapPairs
|
||||
List<DashboardCategory> categories = FeatureFactory.getFactory(context)
|
||||
.getDashboardFeatureProvider(context).getAllCategories();
|
||||
|
||||
for (DashboardCategory category : categories) {
|
||||
// Find the category key first.
|
||||
final String parentClass = CATEGORY_KEY_TO_PARENT_MAP.get(category.key);
|
||||
if (parentClass == null) {
|
||||
continue;
|
||||
}
|
||||
// Use the key to look up parent (which page hosts this key)
|
||||
final String parentName = classToTitleMap.get(parentClass);
|
||||
if (parentName == null) {
|
||||
continue;
|
||||
}
|
||||
// Build parent-child mPairs for all children listed under this key.
|
||||
for (Tile tile : category.getTiles()) {
|
||||
final String childTitle = tile.title.toString();
|
||||
String childClass = null;
|
||||
if (tile.metaData != null) {
|
||||
childClass = tile.metaData.getString(
|
||||
SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
|
||||
}
|
||||
if (childClass == null) {
|
||||
continue;
|
||||
}
|
||||
mPairs.add(new SiteMapPair(parentClass, parentName, childClass, childTitle));
|
||||
}
|
||||
}
|
||||
// Done.
|
||||
mInitialized = true;
|
||||
if (DEBUG_TIMING) {
|
||||
Log.d(TAG, "Init timing: " + (System.currentTimeMillis() - startTime));
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private SiteMapPair lookUpParent(String clazz, String title) {
|
||||
for (SiteMapPair pair : mPairs) {
|
||||
if (TextUtils.equals(pair.childClass, clazz)
|
||||
&& TextUtils.equals(title, pair.childTitle)) {
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data model for a parent-child page pair.
|
||||
*/
|
||||
private static class SiteMapPair {
|
||||
public final String parentClass;
|
||||
public final String parentTitle;
|
||||
public final String childClass;
|
||||
public final String childTitle;
|
||||
|
||||
public SiteMapPair(String parentClass, String parentTitle, String childClass,
|
||||
String childTitle) {
|
||||
this.parentClass = parentClass;
|
||||
this.parentTitle = parentTitle;
|
||||
this.childClass = childClass;
|
||||
this.childTitle = childTitle;
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,6 +17,9 @@
|
||||
|
||||
package com.android.settings.search;
|
||||
|
||||
import static com.android.settings.search.DatabaseResultLoader.BASE_RANKS;
|
||||
import static com.android.settings.search.SearchResult.TOP_RANK;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
@@ -26,18 +29,12 @@ import android.os.BadParcelableException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.android.settings.search.DatabaseResultLoader.BASE_RANKS;
|
||||
import static com.android.settings.search.SearchResult.TOP_RANK;
|
||||
|
||||
/**
|
||||
* Controller to Build search results from {@link Cursor} Objects.
|
||||
*
|
||||
@@ -94,8 +91,7 @@ public class CursorToSearchResultConverter {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Set<SearchResult> convertCursor(SiteMapManager sitemapManager,
|
||||
Cursor cursorResults, int baseRank) {
|
||||
public Set<SearchResult> convertCursor(Cursor cursorResults, int baseRank) {
|
||||
if (cursorResults == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -103,8 +99,8 @@ public class CursorToSearchResultConverter {
|
||||
final Set<SearchResult> results = new HashSet<>();
|
||||
|
||||
while (cursorResults.moveToNext()) {
|
||||
SearchResult result = buildSingleSearchResultFromCursor(sitemapManager,
|
||||
contextMap, cursorResults, baseRank);
|
||||
SearchResult result = buildSingleSearchResultFromCursor(contextMap, cursorResults,
|
||||
baseRank);
|
||||
if (result != null) {
|
||||
results.add(result);
|
||||
}
|
||||
@@ -132,8 +128,8 @@ public class CursorToSearchResultConverter {
|
||||
return null;
|
||||
}
|
||||
|
||||
private SearchResult buildSingleSearchResultFromCursor(SiteMapManager sitemapManager,
|
||||
Map<String, Context> contextMap, Cursor cursor, int baseRank) {
|
||||
private SearchResult buildSingleSearchResultFromCursor(Map<String, Context> contextMap,
|
||||
Cursor cursor, int baseRank) {
|
||||
final int docId = cursor.getInt(COLUMN_INDEX_ID);
|
||||
final String pkgName = cursor.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
|
||||
final String title = cursor.getString(COLUMN_INDEX_TITLE);
|
||||
@@ -145,14 +141,12 @@ public class CursorToSearchResultConverter {
|
||||
final byte[] marshalledPayload = cursor.getBlob(COLUMN_INDEX_PAYLOAD);
|
||||
final ResultPayload payload = getUnmarshalledPayload(marshalledPayload, payloadType);
|
||||
|
||||
final List<String> breadcrumbs = getBreadcrumbs(sitemapManager, cursor);
|
||||
final int rank = getRank(title, baseRank, key);
|
||||
|
||||
final SearchResult.Builder builder = new SearchResult.Builder()
|
||||
.setStableId(docId)
|
||||
.setTitle(title)
|
||||
.setSummary(summaryOn)
|
||||
.addBreadcrumbs(breadcrumbs)
|
||||
.setRank(rank)
|
||||
.setIcon(getIconForPackage(contextMap, pkgName, className, iconResStr))
|
||||
.setPayload(payload);
|
||||
@@ -191,12 +185,6 @@ public class CursorToSearchResultConverter {
|
||||
return icon;
|
||||
}
|
||||
|
||||
private List<String> getBreadcrumbs(SiteMapManager siteMapManager, Cursor cursor) {
|
||||
final String screenTitle = cursor.getString(COLUMN_INDEX_SCREEN_TITLE);
|
||||
final String screenClass = cursor.getString(COLUMN_INDEX_CLASS_NAME);
|
||||
return siteMapManager == null ? null : siteMapManager.buildBreadCrumb(mContext, screenClass,
|
||||
screenTitle);
|
||||
}
|
||||
|
||||
/** Uses the breadcrumbs to determine the offset to the base rank.
|
||||
* There are three checks
|
||||
|
@@ -19,7 +19,8 @@ package com.android.settings.search;
|
||||
|
||||
|
||||
import static com.android.settings.search.CursorToSearchResultConverter.COLUMN_INDEX_ID;
|
||||
import static com.android.settings.search.CursorToSearchResultConverter.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
|
||||
import static com.android.settings.search.CursorToSearchResultConverter
|
||||
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
|
||||
import static com.android.settings.search.CursorToSearchResultConverter.COLUMN_INDEX_KEY;
|
||||
import static com.android.settings.search.DatabaseResultLoader.SELECT_COLUMNS;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.CLASS_NAME;
|
||||
@@ -27,7 +28,8 @@ import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEY_REF;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns
|
||||
.DATA_SUMMARY_ON_NORMALIZED;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED;
|
||||
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
|
||||
@@ -50,7 +52,6 @@ import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.provider.SearchIndexablesContract;
|
||||
import android.provider.SearchIndexablesContract.SiteMapColumns;
|
||||
@@ -58,7 +59,6 @@ import android.support.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.indexing.IndexData;
|
||||
import com.android.settings.search.indexing.IndexDataConverter;
|
||||
import com.android.settings.search.indexing.PreIndexData;
|
||||
@@ -68,7 +68,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Consumes the SearchIndexableProvider content providers.
|
||||
@@ -80,15 +79,6 @@ public class DatabaseIndexingManager {
|
||||
|
||||
private static final String LOG_TAG = "DatabaseIndexingManager";
|
||||
|
||||
private static final String METRICS_ACTION_SETTINGS_ASYNC_INDEX =
|
||||
"search_asynchronous_indexing";
|
||||
|
||||
public static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =
|
||||
"SEARCH_INDEX_DATA_PROVIDER";
|
||||
|
||||
@VisibleForTesting
|
||||
final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false);
|
||||
|
||||
private PreIndexDataCollector mCollector;
|
||||
private IndexDataConverter mConverter;
|
||||
|
||||
@@ -98,15 +88,6 @@ public class DatabaseIndexingManager {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public boolean isIndexingComplete() {
|
||||
return mIsIndexingComplete.get();
|
||||
}
|
||||
|
||||
public void indexDatabase(IndexingCallback callback) {
|
||||
IndexingTask task = new IndexingTask(callback);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate all data and non-indexable keys from each of the content-providers.
|
||||
* Only the first indexing for the default language gets static search results - subsequent
|
||||
@@ -363,39 +344,4 @@ public class DatabaseIndexingManager {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class IndexingTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
@VisibleForTesting
|
||||
IndexingCallback mCallback;
|
||||
private long mIndexStartTime;
|
||||
|
||||
public IndexingTask(IndexingCallback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
mIndexStartTime = System.currentTimeMillis();
|
||||
mIsIndexingComplete.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
performIndexing();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
int indexingTime = (int) (System.currentTimeMillis() - mIndexStartTime);
|
||||
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider()
|
||||
.histogram(mContext, METRICS_ACTION_SETTINGS_ASYNC_INDEX, indexingTime);
|
||||
|
||||
mIsIndexingComplete.set(true);
|
||||
if (mCallback != null) {
|
||||
mCallback.onIndexingFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
package com.android.settings.search;
|
||||
|
||||
/**
|
||||
* Callback for Settings search indexing.
|
||||
*/
|
||||
public interface IndexingCallback {
|
||||
|
||||
/**
|
||||
* Called when Indexing is finished.
|
||||
*/
|
||||
void onIndexingFinished();
|
||||
}
|
@@ -23,11 +23,8 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* FeatureProvider for Settings Search
|
||||
*/
|
||||
@@ -44,11 +41,6 @@ public interface SearchFeatureProvider {
|
||||
void verifyLaunchSearchResultPageCaller(Context context, @NonNull ComponentName caller)
|
||||
throws SecurityException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Returns the manager for looking up breadcrumbs.
|
||||
*/
|
||||
SiteMapManager getSiteMapManager();
|
||||
|
||||
/**
|
||||
* Synchronously updates the Settings database.
|
||||
*/
|
||||
@@ -56,16 +48,6 @@ public interface SearchFeatureProvider {
|
||||
|
||||
DatabaseIndexingManager getIndexingManager(Context context);
|
||||
|
||||
/**
|
||||
* @returns true when indexing is complete.
|
||||
*/
|
||||
boolean isIndexingComplete(Context context);
|
||||
|
||||
/**
|
||||
* @return a {@link ExecutorService} to be shared between search tasks.
|
||||
*/
|
||||
ExecutorService getExecutorService();
|
||||
|
||||
default String getSettingsIntelligencePkgName() {
|
||||
return "com.android.settings.intelligence";
|
||||
}
|
||||
|
@@ -22,13 +22,10 @@ import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.indexing.IndexData;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* FeatureProvider for the refactored search code.
|
||||
@@ -39,8 +36,6 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
|
||||
private static final String METRICS_ACTION_SETTINGS_INDEX = "search_synchronous_indexing";
|
||||
private DatabaseIndexingManager mDatabaseIndexingManager;
|
||||
private SiteMapManager mSiteMapManager;
|
||||
private ExecutorService mExecutorService;
|
||||
|
||||
@Override
|
||||
public void verifyLaunchSearchResultPageCaller(Context context, ComponentName caller) {
|
||||
@@ -68,18 +63,6 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
return mDatabaseIndexingManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndexingComplete(Context context) {
|
||||
return getIndexingManager(context).isIndexingComplete();
|
||||
}
|
||||
|
||||
public SiteMapManager getSiteMapManager() {
|
||||
if (mSiteMapManager == null) {
|
||||
mSiteMapManager = new SiteMapManager();
|
||||
}
|
||||
return mSiteMapManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIndex(Context context) {
|
||||
long indexStartTime = System.currentTimeMillis();
|
||||
@@ -89,14 +72,6 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
.histogram(context, METRICS_ACTION_SETTINGS_INDEX, indexingTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService getExecutorService() {
|
||||
if (mExecutorService == null) {
|
||||
mExecutorService = Executors.newCachedThreadPool();
|
||||
}
|
||||
return mExecutorService;
|
||||
}
|
||||
|
||||
protected boolean isSignatureWhitelisted(Context context, String callerPackage) {
|
||||
return false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user