Loading packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +63 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,10 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; Loading @@ -32,12 +36,16 @@ import android.os.SystemClock; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.LruCache; import android.util.Pair; import androidx.annotation.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.settingslib.R; import com.android.settingslib.Utils; import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.AdaptiveOutlineDrawable; import java.util.ArrayList; import java.util.Collection; Loading Loading @@ -100,6 +108,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private boolean mIsHearingAidProfileConnectedFail = false; // Group second device for Hearing Aid private CachedBluetoothDevice mSubDevice; @VisibleForTesting LruCache<String, BitmapDrawable> mDrawableCache; private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override Loading Loading @@ -131,6 +141,19 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mDevice = device; fillData(); mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; initDrawableCache(); } private void initDrawableCache() { int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; mDrawableCache = new LruCache<String, BitmapDrawable>(cacheSize) { @Override protected int sizeOf(String key, BitmapDrawable bitmap) { return bitmap.getBitmap().getByteCount() / 1024; } }; } /** Loading Loading @@ -381,6 +404,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (dev != null) { final boolean successful = dev.removeBond(); if (successful) { releaseLruCache(); if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null)); } Loading Loading @@ -500,7 +524,21 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } void refresh() { ThreadUtils.postOnBackgroundThread(() -> { if (BluetoothUtils.isAdvancedDetailsHeader(mDevice)) { Uri uri = BluetoothUtils.getUriMetaData(getDevice(), BluetoothDevice.METADATA_MAIN_ICON); if (uri != null && mDrawableCache.get(uri.toString()) == null) { mDrawableCache.put(uri.toString(), (BitmapDrawable) BluetoothUtils.getBtDrawableWithDescription( mContext, this).first); } } ThreadUtils.postOnMainThread(() -> { dispatchAttributesChanged(); }); }); } public void setJustDiscovered(boolean justDiscovered) { Loading Loading @@ -1178,4 +1216,28 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mSubDevice.mJustDiscovered = tmpJustDiscovered; fetchActiveDevices(); } /** * Get cached bluetooth icon with description */ public Pair<Drawable, String> getDrawableWithDescription() { Uri uri = BluetoothUtils.getUriMetaData(mDevice, BluetoothDevice.METADATA_MAIN_ICON); if (BluetoothUtils.isAdvancedDetailsHeader(mDevice) && uri != null) { BitmapDrawable drawable = mDrawableCache.get(uri.toString()); if (drawable != null) { Resources resources = mContext.getResources(); return new Pair<>(new AdaptiveOutlineDrawable( resources, drawable.getBitmap()), BluetoothUtils.getBtClassDrawableWithDescription(mContext, this).second); } refresh(); } return BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, this); } void releaseLruCache() { mDrawableCache.evictAll(); } } packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.media.AudioManager; import com.android.settingslib.R; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.widget.AdaptiveOutlineDrawable; import org.junit.Before; import org.junit.Test; Loading Loading @@ -957,4 +958,41 @@ public class CachedBluetoothDeviceTest { // Should not crash } @Test public void getDrawableWithDescription_isAdvancedDevice_returnAdvancedIcon() { when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)) .thenReturn("fake_uri".getBytes()); when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("true".getBytes()); mCachedDevice.refresh(); assertThat(mCachedDevice.getDrawableWithDescription().first).isInstanceOf( AdaptiveOutlineDrawable.class); } @Test public void getDrawableWithDescription_isNotAdvancedDevice_returnBluetoothIcon() { when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("false".getBytes()); mCachedDevice.refresh(); assertThat(mCachedDevice.getDrawableWithDescription().first).isNotInstanceOf( AdaptiveOutlineDrawable.class); } @Test public void releaseLruCache_lruCacheShouldBeRelease() { when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)) .thenReturn("fake_uri".getBytes()); when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("true".getBytes()); mCachedDevice.refresh(); mCachedDevice.releaseLruCache(); assertThat(mCachedDevice.mDrawableCache.size()).isEqualTo(0); } } Loading
packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +63 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,10 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; Loading @@ -32,12 +36,16 @@ import android.os.SystemClock; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.LruCache; import android.util.Pair; import androidx.annotation.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.settingslib.R; import com.android.settingslib.Utils; import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.AdaptiveOutlineDrawable; import java.util.ArrayList; import java.util.Collection; Loading Loading @@ -100,6 +108,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private boolean mIsHearingAidProfileConnectedFail = false; // Group second device for Hearing Aid private CachedBluetoothDevice mSubDevice; @VisibleForTesting LruCache<String, BitmapDrawable> mDrawableCache; private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override Loading Loading @@ -131,6 +141,19 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mDevice = device; fillData(); mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; initDrawableCache(); } private void initDrawableCache() { int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; mDrawableCache = new LruCache<String, BitmapDrawable>(cacheSize) { @Override protected int sizeOf(String key, BitmapDrawable bitmap) { return bitmap.getBitmap().getByteCount() / 1024; } }; } /** Loading Loading @@ -381,6 +404,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (dev != null) { final boolean successful = dev.removeBond(); if (successful) { releaseLruCache(); if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null)); } Loading Loading @@ -500,7 +524,21 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } void refresh() { ThreadUtils.postOnBackgroundThread(() -> { if (BluetoothUtils.isAdvancedDetailsHeader(mDevice)) { Uri uri = BluetoothUtils.getUriMetaData(getDevice(), BluetoothDevice.METADATA_MAIN_ICON); if (uri != null && mDrawableCache.get(uri.toString()) == null) { mDrawableCache.put(uri.toString(), (BitmapDrawable) BluetoothUtils.getBtDrawableWithDescription( mContext, this).first); } } ThreadUtils.postOnMainThread(() -> { dispatchAttributesChanged(); }); }); } public void setJustDiscovered(boolean justDiscovered) { Loading Loading @@ -1178,4 +1216,28 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mSubDevice.mJustDiscovered = tmpJustDiscovered; fetchActiveDevices(); } /** * Get cached bluetooth icon with description */ public Pair<Drawable, String> getDrawableWithDescription() { Uri uri = BluetoothUtils.getUriMetaData(mDevice, BluetoothDevice.METADATA_MAIN_ICON); if (BluetoothUtils.isAdvancedDetailsHeader(mDevice) && uri != null) { BitmapDrawable drawable = mDrawableCache.get(uri.toString()); if (drawable != null) { Resources resources = mContext.getResources(); return new Pair<>(new AdaptiveOutlineDrawable( resources, drawable.getBitmap()), BluetoothUtils.getBtClassDrawableWithDescription(mContext, this).second); } refresh(); } return BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, this); } void releaseLruCache() { mDrawableCache.evictAll(); } }
packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.media.AudioManager; import com.android.settingslib.R; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.widget.AdaptiveOutlineDrawable; import org.junit.Before; import org.junit.Test; Loading Loading @@ -957,4 +958,41 @@ public class CachedBluetoothDeviceTest { // Should not crash } @Test public void getDrawableWithDescription_isAdvancedDevice_returnAdvancedIcon() { when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)) .thenReturn("fake_uri".getBytes()); when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("true".getBytes()); mCachedDevice.refresh(); assertThat(mCachedDevice.getDrawableWithDescription().first).isInstanceOf( AdaptiveOutlineDrawable.class); } @Test public void getDrawableWithDescription_isNotAdvancedDevice_returnBluetoothIcon() { when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("false".getBytes()); mCachedDevice.refresh(); assertThat(mCachedDevice.getDrawableWithDescription().first).isNotInstanceOf( AdaptiveOutlineDrawable.class); } @Test public void releaseLruCache_lruCacheShouldBeRelease() { when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)) .thenReturn("fake_uri".getBytes()); when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("true".getBytes()); mCachedDevice.refresh(); mCachedDevice.releaseLruCache(); assertThat(mCachedDevice.mDrawableCache.size()).isEqualTo(0); } }