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.settings.connecteddevice; 17 18 import android.app.Activity; 19 import android.app.settings.SettingsEnums; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.Bundle; 23 import android.text.TextUtils; 24 import android.util.Log; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.view.accessibility.AccessibilityEvent; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.Nullable; 31 import androidx.annotation.VisibleForTesting; 32 33 import com.android.settings.R; 34 import com.android.settings.SettingsActivity; 35 import com.android.settings.Utils; 36 import com.android.settings.bluetooth.AlwaysDiscoverable; 37 import com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController; 38 import com.android.settings.bluetooth.BluetoothSwitchPreferenceController; 39 import com.android.settings.dashboard.DashboardFragment; 40 import com.android.settings.password.PasswordUtils; 41 import com.android.settings.search.BaseSearchIndexProvider; 42 import com.android.settings.widget.MainSwitchBarController; 43 import com.android.settings.widget.SettingsMainSwitchBar; 44 import com.android.settingslib.core.lifecycle.Lifecycle; 45 import com.android.settingslib.search.SearchIndexable; 46 import com.android.settingslib.widget.FooterPreference; 47 48 /** 49 * Dedicated screen for allowing the user to toggle bluetooth which displays relevant information to 50 * the user based on related settings such as bluetooth scanning. 51 */ 52 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) 53 public class BluetoothDashboardFragment extends DashboardFragment { 54 55 private static final String TAG = "BluetoothDashboardFrag"; 56 private static final String SLICE_ACTION = "com.android.settings.SEARCH_RESULT_TRAMPOLINE"; 57 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 58 59 private FooterPreference mFooterPreference; 60 private SettingsMainSwitchBar mSwitchBar; 61 private BluetoothSwitchPreferenceController mController; 62 63 private @Nullable AlwaysDiscoverable mAlwaysDiscoverable; 64 65 @Override getMetricsCategory()66 public int getMetricsCategory() { 67 return SettingsEnums.BLUETOOTH_FRAGMENT; 68 } 69 70 @Override getLogTag()71 protected String getLogTag() { 72 return TAG; 73 } 74 75 @Override getHelpResource()76 public int getHelpResource() { 77 return R.string.help_uri_bluetooth_screen; 78 } 79 80 @Override getPreferenceScreenResId()81 protected int getPreferenceScreenResId() { 82 return R.xml.bluetooth_screen; 83 } 84 85 @Override onCreate(Bundle icicle)86 public void onCreate(Bundle icicle) { 87 super.onCreate(icicle); 88 if (!isCatalystEnabled()) { 89 mFooterPreference = findPreference(BluetoothFooterPreference.KEY); 90 } 91 } 92 93 @Override onAttach(Context context)94 public void onAttach(Context context) { 95 super.onAttach(context); 96 use(BluetoothDeviceRenamePreferenceController.class).setFragment(this); 97 } 98 99 @Override onActivityCreated(Bundle savedInstanceState)100 public void onActivityCreated(Bundle savedInstanceState) { 101 super.onActivityCreated(savedInstanceState); 102 if (isCatalystEnabled()) { 103 return; 104 } 105 String callingAppPackageName = PasswordUtils.getCallingAppPackageName( 106 getActivity().getActivityToken()); 107 String action = getIntent() != null ? getIntent().getAction() : ""; 108 if (DEBUG) { 109 Log.d(TAG, "onActivityCreated() calling package name is : " + callingAppPackageName 110 + ", action : " + action); 111 } 112 113 SettingsActivity activity = (SettingsActivity) getActivity(); 114 mSwitchBar = activity.getSwitchBar(); 115 mSwitchBar.setTitle(getContext().getString(R.string.bluetooth_main_switch_title)); 116 mSwitchBar.getRootView().setAccessibilityDelegate(new MainSwitchAccessibilityDelegate()); 117 mController = new BluetoothSwitchPreferenceController(activity, 118 new MainSwitchBarController(mSwitchBar), mFooterPreference); 119 mController.setAlwaysDiscoverable(isAlwaysDiscoverable(callingAppPackageName, action)); 120 Lifecycle lifecycle = getSettingsLifecycle(); 121 if (lifecycle != null) { 122 lifecycle.addObserver(mController); 123 } 124 } 125 126 @Override onStart()127 public void onStart() { 128 super.onStart(); 129 if (isCatalystEnabled()) { 130 Activity activity = requireActivity(); 131 String callingAppPackageName = PasswordUtils.getCallingAppPackageName( 132 activity.getActivityToken()); 133 Intent intent = activity.getIntent(); 134 String action = intent != null ? intent.getAction() : ""; 135 if (DEBUG) { 136 Log.d(TAG, "onActivityCreated() calling package name is : " + callingAppPackageName 137 + ", action : " + action); 138 } 139 if (isAlwaysDiscoverable(callingAppPackageName, action)) { 140 mAlwaysDiscoverable = new AlwaysDiscoverable(activity); 141 mAlwaysDiscoverable.start(); 142 } 143 } 144 } 145 146 @Override onStop()147 public void onStop() { 148 super.onStop(); 149 if (mAlwaysDiscoverable != null) { 150 mAlwaysDiscoverable.stop(); 151 mAlwaysDiscoverable = null; 152 } 153 } 154 155 @VisibleForTesting isAlwaysDiscoverable(@ullable String callingAppPackageName, @Nullable String action)156 boolean isAlwaysDiscoverable(@Nullable String callingAppPackageName, @Nullable String action) { 157 return TextUtils.equals(SLICE_ACTION, action) ? false 158 : TextUtils.equals(Utils.SETTINGS_PACKAGE_NAME, callingAppPackageName) 159 || TextUtils.equals(Utils.SYSTEMUI_PACKAGE_NAME, callingAppPackageName); 160 } 161 162 /** 163 * For Search. 164 */ 165 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 166 new BaseSearchIndexProvider(R.xml.bluetooth_screen); 167 168 @Override getPreferenceScreenBindingKey(@onNull Context context)169 public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) { 170 return BluetoothDashboardScreen.KEY; 171 } 172 173 private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate { 174 @Override onRequestSendAccessibilityEvent( @onNull ViewGroup host, @NonNull View view, @NonNull AccessibilityEvent event)175 public boolean onRequestSendAccessibilityEvent( 176 @NonNull ViewGroup host, @NonNull View view, @NonNull AccessibilityEvent event) { 177 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 178 && (event.getContentChangeTypes() 179 & AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED) 180 != 0) { 181 Log.d(TAG, "Skip accessibility event for CONTENT_CHANGE_TYPE_ENABLED"); 182 return false; 183 } 184 return super.onRequestSendAccessibilityEvent(host, view, event); 185 } 186 } 187 } 188