1 /* 2 * Copyright (C) 2017 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 */ 17 package com.android.settings.search; 18 19 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO; 20 21 import android.app.ActivityOptions; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ResolveInfo; 27 import android.os.Bundle; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.widget.Toolbar; 31 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 import androidx.fragment.app.FragmentActivity; 35 36 import com.android.settings.R; 37 import com.android.settings.Utils; 38 import com.android.settings.activityembedding.ActivityEmbeddingRulesController; 39 import com.android.settings.overlay.FeatureFactory; 40 import com.android.settingslib.search.SearchIndexableResources; 41 42 import com.google.android.setupcompat.util.WizardManagerHelper; 43 44 import java.util.List; 45 46 /** 47 * FeatureProvider for Settings Search 48 */ 49 public interface SearchFeatureProvider { 50 51 String KEY_HOMEPAGE_SEARCH_BAR = "homepage_search_bar"; 52 int REQUEST_CODE = 501; 53 54 /** 55 * Ensures the caller has necessary privilege to launch search result page. 56 * 57 * @throws IllegalArgumentException when caller is null 58 * @throws SecurityException when caller is not allowed to launch search result page 59 */ verifyLaunchSearchResultPageCaller(@onNull Context context, @NonNull String callerPackage)60 void verifyLaunchSearchResultPageCaller(@NonNull Context context, @NonNull String callerPackage) 61 throws SecurityException, IllegalArgumentException; 62 63 /** 64 * @return a {@link SearchIndexableResources} to be used for indexing search results. 65 */ getSearchIndexableResources()66 SearchIndexableResources getSearchIndexableResources(); 67 68 /** 69 * @return a package name of settings intelligence. 70 */ getSettingsIntelligencePkgName(Context context)71 default String getSettingsIntelligencePkgName(Context context) { 72 return context.getString(R.string.config_settingsintelligence_package_name); 73 } 74 75 /** 76 * Send the pre-index intent. 77 */ sendPreIndexIntent(Context context)78 default void sendPreIndexIntent(Context context){ 79 } 80 81 /** 82 * Initializes the search toolbar. 83 */ initSearchToolbar(@onNull FragmentActivity activity, @Nullable View toolbar, int pageId)84 default void initSearchToolbar(@NonNull FragmentActivity activity, @Nullable View toolbar, 85 int pageId) { 86 if (toolbar == null) { 87 return; 88 } 89 90 if (!WizardManagerHelper.isDeviceProvisioned(activity) 91 || !Utils.isPackageEnabled(activity, getSettingsIntelligencePkgName(activity)) 92 || WizardManagerHelper.isAnySetupWizard(activity.getIntent())) { 93 final ViewGroup parent = (ViewGroup) toolbar.getParent(); 94 if (parent != null) { 95 parent.setVisibility(View.GONE); 96 } 97 return; 98 } 99 // Please forgive me for what I am about to do. 100 // 101 // Need to make the navigation icon non-clickable so that the entire card is clickable 102 // and goes to the search UI. Also set the background to null so there's no ripple. 103 if (toolbar instanceof Toolbar) { 104 final View navView = ((Toolbar) toolbar).getNavigationView(); 105 navView.setClickable(false); 106 navView.setFocusable(false); 107 navView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 108 navView.setBackground(null); 109 } 110 111 final Context context = activity.getApplicationContext(); 112 final Intent intent = buildSearchIntent(context, pageId) 113 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 114 final List<ResolveInfo> resolveInfos = 115 activity.getPackageManager().queryIntentActivities(intent, 116 PackageManager.MATCH_DEFAULT_ONLY); 117 if (resolveInfos.isEmpty()) { 118 return; 119 } 120 121 final ComponentName searchComponentName = resolveInfos.get(0) 122 .getComponentInfo().getComponentName(); 123 // Set a component name since activity embedding requires a component name for 124 // registering a rule. 125 intent.setComponent(searchComponentName); 126 ActivityEmbeddingRulesController.registerTwoPanePairRuleForSettingsHome( 127 context, 128 searchComponentName, 129 intent.getAction(), 130 false /* finishPrimaryWithSecondary */, 131 true /* finishSecondaryWithPrimary */, 132 false /* clearTop */); 133 134 toolbar.setOnClickListener(tb -> startSearchActivity(context, activity, pageId, intent)); 135 136 toolbar.setHandwritingDelegatorCallback( 137 () -> startSearchActivity(context, activity, pageId, intent)); 138 toolbar.setAllowedHandwritingDelegatePackage(intent.getPackage()); 139 } 140 141 /** Start the search activity. */ startSearchActivity( Context context, FragmentActivity activity, int pageId, Intent intent)142 private static void startSearchActivity( 143 Context context, FragmentActivity activity, int pageId, Intent intent) { 144 FeatureFactory.getFeatureFactory().getSlicesFeatureProvider() 145 .indexSliceDataAsync(context); 146 147 FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() 148 .logSettingsTileClick(KEY_HOMEPAGE_SEARCH_BAR, pageId); 149 150 final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle(); 151 activity.startActivity(intent, bundle); 152 } 153 buildSearchIntent(Context context, int pageId)154 Intent buildSearchIntent(Context context, int pageId); 155 } 156