diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java index c0527c90049..ebfaca9544e 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java @@ -37,6 +37,7 @@ import androidx.recyclerview.widget.ItemTouchHelper; import com.android.settings.R; import com.android.settings.core.InstrumentedFragment; +import com.android.settings.homepage.contextualcards.slices.BluetoothUpdateWorker; import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate; import com.android.settings.overlay.FeatureFactory; import com.android.settings.wifi.slice.ContextualWifiScanWorker; @@ -67,6 +68,7 @@ public class ContextualCardsFragment extends InstrumentedFragment implements final Context context = getContext(); if (savedInstanceState == null) { FeatureFactory.getFactory(context).getSlicesFeatureProvider().newUiSession(); + BluetoothUpdateWorker.initLocalBtManager(getContext()); } mContextualCardManager = new ContextualCardManager(context, getSettingsLifecycle(), savedInstanceState); diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java index 8b74313b8bf..19de4030e30 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java @@ -81,15 +81,12 @@ public class BluetoothDevicesSlice implements CustomSliceable { private static boolean sBluetoothEnabling; private final Context mContext; - private final AvailableMediaBluetoothDeviceUpdater mAvailableMediaBtDeviceUpdater; - private final SavedBluetoothDeviceUpdater mSavedBtDeviceUpdater; + private AvailableMediaBluetoothDeviceUpdater mAvailableMediaBtDeviceUpdater; + private SavedBluetoothDeviceUpdater mSavedBtDeviceUpdater; public BluetoothDevicesSlice(Context context) { mContext = context; - mAvailableMediaBtDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(mContext, - null /* fragment */, null /* devicePreferenceCallback */); - mSavedBtDeviceUpdater = new SavedBluetoothDeviceUpdater(mContext, - null /* fragment */, null /* devicePreferenceCallback */); + BluetoothUpdateWorker.initLocalBtManager(context); } @Override @@ -119,16 +116,8 @@ public class BluetoothDevicesSlice implements CustomSliceable { // Add the header of Bluetooth on listBuilder.addRow(getBluetoothOnHeader()); - // Get row builders of Bluetooth devices. - final List rows = getBluetoothRowBuilders(); - - // Determine the displayable row count. - final int displayableCount = Math.min(rows.size(), DEFAULT_EXPANDED_ROW_COUNT); - - // Add device rows up to the count. - for (int i = 0; i < displayableCount; i++) { - listBuilder.addRow(rows.get(i)); - } + // Add row builders of Bluetooth devices. + getBluetoothRowBuilders().forEach(row -> listBuilder.addRow(row)); return listBuilder.build(); } @@ -181,19 +170,18 @@ public class BluetoothDevicesSlice implements CustomSliceable { List getPairedBluetoothDevices() { final List bluetoothDeviceList = new ArrayList<>(); - // If Bluetooth is disable, skip to get the Bluetooth devices. + // If Bluetooth is disable, skip getting the Bluetooth devices. if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) { Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is disabled."); return bluetoothDeviceList; } - // Get the Bluetooth devices from LocalBluetoothManager. - final LocalBluetoothManager localBtManager = - com.android.settings.bluetooth.Utils.getLocalBtManager(mContext); + final LocalBluetoothManager localBtManager = BluetoothUpdateWorker.getLocalBtManager(); if (localBtManager == null) { - Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is unsupported."); + Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is not ready."); return bluetoothDeviceList; } + final Collection cachedDevices = localBtManager.getCachedDeviceManager().getCachedDevicesCopy(); @@ -292,8 +280,21 @@ public class BluetoothDevicesSlice implements CustomSliceable { private List getBluetoothRowBuilders() { final List bluetoothRows = new ArrayList<>(); + final List pairedDevices = getPairedBluetoothDevices(); + if (pairedDevices.isEmpty()) { + return bluetoothRows; + } + + // Initialize updaters without being blocked after paired devices is available because + // LocalBluetoothManager is ready. + lazyInitUpdaters(); + // Create row builders based on paired devices. - for (CachedBluetoothDevice device : getPairedBluetoothDevices()) { + for (CachedBluetoothDevice device : pairedDevices) { + if (bluetoothRows.size() >= DEFAULT_EXPANDED_ROW_COUNT) { + break; + } + String summary = device.getConnectionSummary(); if (summary == null) { summary = mContext.getString( @@ -321,6 +322,18 @@ public class BluetoothDevicesSlice implements CustomSliceable { return bluetoothRows; } + private void lazyInitUpdaters() { + if (mAvailableMediaBtDeviceUpdater == null) { + mAvailableMediaBtDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(mContext, + null /* fragment */, null /* devicePreferenceCallback */); + } + + if (mSavedBtDeviceUpdater == null) { + mSavedBtDeviceUpdater = new SavedBluetoothDeviceUpdater(mContext, + null /* fragment */, null /* devicePreferenceCallback */); + } + } + @VisibleForTesting SliceAction buildPrimaryBluetoothAction(CachedBluetoothDevice bluetoothDevice) { final Intent intent = new Intent(getUri().toString()) diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java index 1602f564998..8f15f2cd131 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java +++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java @@ -18,9 +18,14 @@ package com.android.settings.homepage.contextualcards.slices; import android.content.Context; import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Process; import android.util.Log; -import com.android.settings.bluetooth.Utils; +import androidx.annotation.Nullable; + import com.android.settings.slices.SliceBackgroundWorker; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -30,29 +35,46 @@ public class BluetoothUpdateWorker extends SliceBackgroundWorker implements Blue private static final String TAG = "BluetoothUpdateWorker"; - private final LocalBluetoothManager mLocalBluetoothManager; + private static LocalBluetoothManager sLocalBluetoothManager; + + private LoadBtManagerHandler mLoadBtManagerHandler; public BluetoothUpdateWorker(Context context, Uri uri) { super(context, uri); - mLocalBluetoothManager = Utils.getLocalBtManager(context); + mLoadBtManagerHandler = LoadBtManagerHandler.getInstance(context); + if (sLocalBluetoothManager == null) { + mLoadBtManagerHandler.startLoadingBtManager(this); + } + } + + /** Initialize {@link LocalBluetoothManager} in the background */ + public static void initLocalBtManager(Context context) { + if (sLocalBluetoothManager == null) { + LoadBtManagerHandler.getInstance(context).startLoadingBtManager(); + } + } + + @Nullable + static LocalBluetoothManager getLocalBtManager() { + return sLocalBluetoothManager; } @Override protected void onSlicePinned() { - if (mLocalBluetoothManager == null) { - Log.i(TAG, "onSlicePinned() Bluetooth is unsupported."); + final LocalBluetoothManager localBtManager = mLoadBtManagerHandler.getLocalBtManager(); + if (localBtManager == null) { return; } - mLocalBluetoothManager.getEventManager().registerCallback(this); + localBtManager.getEventManager().registerCallback(this); } @Override protected void onSliceUnpinned() { - if (mLocalBluetoothManager == null) { - Log.i(TAG, "onSliceUnpinned() Bluetooth is unsupported."); + final LocalBluetoothManager localBtManager = mLoadBtManagerHandler.getLocalBtManager(); + if (localBtManager == null) { return; } - mLocalBluetoothManager.getEventManager().unregisterCallback(this); + localBtManager.getEventManager().unregisterCallback(this); } @Override @@ -84,4 +106,59 @@ public class BluetoothUpdateWorker extends SliceBackgroundWorker implements Blue int bluetoothProfile) { notifySliceChange(); } + + private static class LoadBtManagerHandler extends Handler { + + private static LoadBtManagerHandler sHandler; + + private final Runnable mLoadBtManagerTask; + private final Context mContext; + private BluetoothUpdateWorker mWorker; + + private static LoadBtManagerHandler getInstance(Context context) { + if (sHandler == null) { + final HandlerThread workerThread = new HandlerThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND); + workerThread.start(); + sHandler = new LoadBtManagerHandler(context, workerThread.getLooper()); + } + return sHandler; + } + + private LoadBtManagerHandler(Context context, Looper looper) { + super(looper); + mContext = context; + mLoadBtManagerTask = () -> { + Log.d(TAG, "LoadBtManagerHandler: start loading..."); + final long startTime = System.currentTimeMillis(); + sLocalBluetoothManager = getLocalBtManager(); + Log.d(TAG, "LoadBtManagerHandler took " + (System.currentTimeMillis() - startTime) + + " ms"); + }; + } + + private LocalBluetoothManager getLocalBtManager() { + if (sLocalBluetoothManager != null) { + return sLocalBluetoothManager; + } + return LocalBluetoothManager.getInstance(mContext, + (context, btManager) -> { + if (mWorker != null) { + // notify change if the worker is ready + mWorker.notifySliceChange(); + } + }); + } + + private void startLoadingBtManager() { + if (!hasCallbacks(mLoadBtManagerTask)) { + post(mLoadBtManagerTask); + } + } + + private void startLoadingBtManager(BluetoothUpdateWorker worker) { + mWorker = worker; + startLoadingBtManager(); + } + } } \ No newline at end of file