1 /* 2 * Copyright (C) 2018 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.car.carlauncher; 17 18 import android.annotation.Nullable; 19 import android.app.Activity; 20 import android.car.Car; 21 import android.car.CarNotConnectedException; 22 import android.car.content.pm.CarPackageManager; 23 import android.car.drivingstate.CarUxRestrictionsManager; 24 import android.content.BroadcastReceiver; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.ServiceConnection; 30 import android.content.pm.LauncherApps; 31 import android.content.pm.PackageManager; 32 import android.os.Bundle; 33 import android.os.IBinder; 34 import android.text.Editable; 35 import android.text.TextUtils; 36 import android.text.TextWatcher; 37 import android.util.Log; 38 import android.view.View; 39 import android.view.inputmethod.InputMethodManager; 40 import android.widget.EditText; 41 42 import androidx.recyclerview.widget.RecyclerView; 43 import java.util.Collections; 44 import java.util.List; 45 46 /** 47 * Activity that allows user to search in apps. 48 */ 49 public final class AppSearchActivity extends Activity { 50 51 private static final String TAG = "AppSearchActivity"; 52 53 private PackageManager mPackageManager; 54 private SearchResultAdapter mSearchResultAdapter; 55 private AppInstallUninstallReceiver mInstallUninstallReceiver; 56 private Car mCar; 57 private CarPackageManager mCarPackageManager; 58 private CarUxRestrictionsManager mCarUxRestrictionsManager; 59 60 private ServiceConnection mCarConnectionListener = new ServiceConnection() { 61 @Override 62 public void onServiceConnected(ComponentName name, IBinder service) { 63 try { 64 mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager( 65 Car.CAR_UX_RESTRICTION_SERVICE); 66 mSearchResultAdapter.setIsDistractionOptimizationRequired( 67 mCarUxRestrictionsManager 68 .getCurrentCarUxRestrictions() 69 .isRequiresDistractionOptimization()); 70 mCarUxRestrictionsManager.registerListener( 71 restrictionInfo -> 72 mSearchResultAdapter.setIsDistractionOptimizationRequired( 73 restrictionInfo.isRequiresDistractionOptimization())); 74 75 mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE); 76 mSearchResultAdapter.setAllApps(getAllApps()); 77 } catch (CarNotConnectedException e) { 78 Log.e(TAG, "Car not connected in CarConnectionListener", e); 79 } 80 } 81 82 @Override 83 public void onServiceDisconnected(ComponentName name) { 84 mCarUxRestrictionsManager = null; 85 mCarPackageManager = null; 86 } 87 }; 88 89 @Override onCreate(@ullable Bundle savedInstanceState)90 protected void onCreate(@Nullable Bundle savedInstanceState) { 91 super.onCreate(savedInstanceState); 92 mPackageManager = getPackageManager(); 93 mCar = Car.createCar(this, mCarConnectionListener); 94 95 setContentView(R.layout.app_search_activity); 96 findViewById(R.id.container).setOnTouchListener( 97 (view, event) -> { 98 hideKeyboard(); 99 return false; 100 }); 101 findViewById(R.id.exit_button_container).setOnClickListener(view -> finish()); 102 103 RecyclerView searchResultView = findViewById(R.id.search_result); 104 searchResultView.setClipToOutline(true); 105 mSearchResultAdapter = new SearchResultAdapter(this); 106 searchResultView.setAdapter(mSearchResultAdapter); 107 108 EditText searchBar = findViewById(R.id.app_search_bar); 109 searchBar.addTextChangedListener(new TextWatcher() { 110 @Override 111 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 112 } 113 114 @Override 115 public void onTextChanged(CharSequence s, int start, int before, int count) { 116 } 117 118 @Override 119 public void afterTextChanged(Editable s) { 120 if (TextUtils.isEmpty(s)) { 121 searchResultView.setVisibility(View.GONE); 122 mSearchResultAdapter.clearResults(); 123 } else { 124 searchResultView.setVisibility(View.VISIBLE); 125 mSearchResultAdapter.getFilter().filter(s.toString()); 126 } 127 } 128 }); 129 } 130 131 @Override onStart()132 protected void onStart() { 133 super.onStart(); 134 // register broadcast receiver for package installation and uninstallation 135 mInstallUninstallReceiver = new AppInstallUninstallReceiver(); 136 IntentFilter filter = new IntentFilter(); 137 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 138 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 139 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 140 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 141 filter.addDataScheme("package"); 142 registerReceiver(mInstallUninstallReceiver, filter); 143 144 // Connect to car service 145 mCar.connect(); 146 } 147 148 @Override onStop()149 protected void onStop() { 150 super.onPause(); 151 // disconnect from app install/uninstall receiver 152 if (mInstallUninstallReceiver != null) { 153 unregisterReceiver(mInstallUninstallReceiver); 154 mInstallUninstallReceiver = null; 155 } 156 // disconnect from car listeners 157 try { 158 if (mCarUxRestrictionsManager != null) { 159 mCarUxRestrictionsManager.unregisterListener(); 160 } 161 } catch (CarNotConnectedException e) { 162 Log.e(TAG, "Error unregistering listeners", e); 163 } 164 if (mCar != null) { 165 mCar.disconnect(); 166 } 167 } 168 getAllApps()169 private List<AppMetaData> getAllApps() { 170 AppLauncherUtils.LauncherAppsInfo appsInfo = AppLauncherUtils.getAllLauncherApps( 171 Collections.emptySet(), getSystemService(LauncherApps.class), mCarPackageManager, 172 mPackageManager); 173 return appsInfo.getApplicationsList(); 174 } 175 hideKeyboard()176 public void hideKeyboard() { 177 InputMethodManager inputMethodManager = (InputMethodManager) getSystemService( 178 Activity.INPUT_METHOD_SERVICE); 179 inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); 180 } 181 182 private class AppInstallUninstallReceiver extends BroadcastReceiver { 183 @Override onReceive(Context context, Intent intent)184 public void onReceive(Context context, Intent intent) { 185 String packageName = intent.getData().getSchemeSpecificPart(); 186 187 if (TextUtils.isEmpty(packageName)) { 188 Log.e(TAG, "System sent an empty app install/uninstall broadcast"); 189 return; 190 } 191 192 mSearchResultAdapter.setAllApps(getAllApps()); 193 } 194 } 195 } 196