From 7fb266f30bf04359dc70d4c1e2a88e1e85841340 Mon Sep 17 00:00:00 2001 From: tmfang Date: Mon, 18 Feb 2019 11:08:24 +0800 Subject: [PATCH 1/4] Fix janky in App info screen If a Recycle view inside in a NestedScrollView, The times of calling recyclerView#onCreateViewHolder() is requal to its data set. When there are large set of apps need to be shown, RecyclerView will need to take many time to call onCreateViewHolder for each item. So, it causes the screen is janky or freeze UI when page is loading large data set. For now, we get rid of NestedScrollView, just use RecycleView and AppBarLayout to have same bahavior for spinner. Fixes: 124384314 Fixes: 123036231 Fixes: 122964660 Fixes: 123088178 Test: 1. Select show system in App info screen. Observe whether there is any janky. 2. Select show system in App info screen, and then change device orientation. Observer whether there is any janky. 3. Go to Settings > Apps & notifications> Advanced > Special app access > Select "battery optimization", and then Tap drop down and select "all apps". Observe whether ther is any janky. Change-Id: Icd65e91eec3b32476d80cc12e9c4baa71151306a --- res/layout/apps_filter_spinner.xml | 3 +- res/layout/manage_applications_apps.xml | 67 +++++++++---------- .../ManageApplications.java | 8 +-- .../ManageApplicationsTest.java | 8 +-- 4 files changed, 39 insertions(+), 47 deletions(-) diff --git a/res/layout/apps_filter_spinner.xml b/res/layout/apps_filter_spinner.xml index c9b1e074078..50f0fde26c7 100644 --- a/res/layout/apps_filter_spinner.xml +++ b/res/layout/apps_filter_spinner.xml @@ -18,7 +18,8 @@ + android:layout_height="wrap_content" + android:background="@android:color/transparent"> - - + android:layout_height="match_parent" + android:orientation="vertical" + android:visibility="gone"> - + android:layout_height="match_parent"> - + android:layout_height="match_parent" + android:clipToPadding="false" + settings:fastScrollEnabled="true" + settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable" + settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable" + settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" + settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/> - + - + - + - + - - - - - + Date: Wed, 23 Jan 2019 10:44:39 +0800 Subject: [PATCH 2/4] Visual update: update the icons for suggestions - remove blue tint - add the icons of suggestions for contextual homepage Bug: 122310542 Test: visual, rebuild Change-Id: I2bac139669a4218455195aebeba83d1649184895 --- AndroidManifest.xml | 10 +++---- res/drawable/ic_suggestion_dnd.xml | 28 ++++++++++++++++++++ res/drawable/ic_suggestion_night_display.xml | 25 +++++++++++++++++ res/drawable/ic_suggestion_security.xml | 28 ++++++++++++++++++++ res/drawable/ic_suggestion_wallpaper.xml | 25 +++++++++++++++++ res/drawable/ic_suggestion_wireless.xml | 25 +++++++++++++++++ res/layout/homepage_slice_half_tile.xml | 3 +-- 7 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 res/drawable/ic_suggestion_dnd.xml create mode 100644 res/drawable/ic_suggestion_night_display.xml create mode 100644 res/drawable/ic_suggestion_security.xml create mode 100644 res/drawable/ic_suggestion_wallpaper.xml create mode 100644 res/drawable/ic_suggestion_wireless.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 70dce8ff23b..a94801f1dfa 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -736,7 +736,7 @@ @@ -796,7 +796,7 @@ @@ -889,7 +889,7 @@ + android:icon="@drawable/ic_suggestion_night_display"> @@ -1628,7 +1628,7 @@ + android:icon="@drawable/ic_suggestion_security"> @@ -2635,7 +2635,7 @@ + android:icon="@drawable/ic_suggestion_wireless"> diff --git a/res/drawable/ic_suggestion_dnd.xml b/res/drawable/ic_suggestion_dnd.xml new file mode 100644 index 00000000000..816b148c406 --- /dev/null +++ b/res/drawable/ic_suggestion_dnd.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/res/drawable/ic_suggestion_night_display.xml b/res/drawable/ic_suggestion_night_display.xml new file mode 100644 index 00000000000..16cb04ef90b --- /dev/null +++ b/res/drawable/ic_suggestion_night_display.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/drawable/ic_suggestion_security.xml b/res/drawable/ic_suggestion_security.xml new file mode 100644 index 00000000000..e2d3a90e962 --- /dev/null +++ b/res/drawable/ic_suggestion_security.xml @@ -0,0 +1,28 @@ + + + + + \ No newline at end of file diff --git a/res/drawable/ic_suggestion_wallpaper.xml b/res/drawable/ic_suggestion_wallpaper.xml new file mode 100644 index 00000000000..32defcecdc1 --- /dev/null +++ b/res/drawable/ic_suggestion_wallpaper.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/drawable/ic_suggestion_wireless.xml b/res/drawable/ic_suggestion_wireless.xml new file mode 100644 index 00000000000..74b4cee4cdf --- /dev/null +++ b/res/drawable/ic_suggestion_wireless.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/layout/homepage_slice_half_tile.xml b/res/layout/homepage_slice_half_tile.xml index 46f3cdaf8e0..fc995617341 100644 --- a/res/layout/homepage_slice_half_tile.xml +++ b/res/layout/homepage_slice_half_tile.xml @@ -39,8 +39,7 @@ + android:layout_height="@dimen/homepage_card_icon_size"/> Date: Wed, 13 Feb 2019 16:07:57 +0800 Subject: [PATCH 3/4] Add a util method of creating Icon with Drawable - Add a util method to help create Icon with Drawable so that users don't need to handle bitmaps - Add support for creating with ColorDrawable Test: robotest Fixes: 124407373 Change-Id: I1897256821cc804a5e599d967feb84a3bcd689a9 --- src/com/android/settings/Utils.java | 25 +++++--- .../slices/BluetoothDevicesSlice.java | 2 +- .../slices/NotificationChannelSlice.java | 5 +- .../settings/media/MediaOutputSlice.java | 15 +---- .../wifi/slice/ContextualWifiSlice.java | 2 +- .../src/com/android/settings/UtilsTest.java | 58 +++++++++++++++---- .../settings/wifi/qrcode/QrCameraTest.java | 3 - 7 files changed, 67 insertions(+), 43 deletions(-) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 65cfdf60f12..37643b37b89 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -89,6 +89,7 @@ import android.widget.ListView; import android.widget.TabWidget; import androidx.annotation.StringRes; +import androidx.core.graphics.drawable.IconCompat; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; @@ -950,24 +951,30 @@ public final class Utils extends com.android.settingslib.Utils { bitmap = Bitmap.createScaledBitmap(((BitmapDrawable) original).getBitmap(), width, height, false); } else { - bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(bitmap); - original.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - original.draw(canvas); + bitmap = createBitmap(original, width, height); } return new BitmapDrawable(null, bitmap); } /** - * Converts the {@link Drawable} to a {@link Bitmap}. + * Create an Icon pointing to a drawable. */ - public static Bitmap drawableToBitmap(Drawable drawable) { + public static IconCompat createIconWithDrawable(Drawable drawable) { + Bitmap bitmap; if (drawable instanceof BitmapDrawable) { - return ((BitmapDrawable)drawable).getBitmap(); + bitmap = ((BitmapDrawable)drawable).getBitmap(); + } else { + final int width = drawable.getIntrinsicWidth(); + final int height = drawable.getIntrinsicHeight(); + bitmap = createBitmap(drawable, + width > 0 ? width : 1, + height > 0 ? height : 1); } + return IconCompat.createWithBitmap(bitmap); + } - final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + private static Bitmap createBitmap(Drawable drawable, int width, int height) { + final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); final Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java index 5673948dde8..6431faf024f 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java @@ -220,7 +220,7 @@ public class BluetoothDevicesSlice implements CustomSliceable { .getBtClassDrawableWithDescription(mContext, device); if (pair.first != null) { - return IconCompat.createWithBitmap(Utils.drawableToBitmap(pair.first)); + return Utils.createIconWithDrawable(pair.first); } else { return IconCompat.createWithResource(mContext, com.android.internal.R.drawable.ic_settings_bluetooth); diff --git a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java index 692dc0a9b95..c2c2ecea601 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java @@ -30,9 +30,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; @@ -243,7 +240,7 @@ public class NotificationChannelSlice implements CustomSliceable { return null; } - return IconCompat.createWithBitmap(Utils.drawableToBitmap(drawable)); + return Utils.createIconWithDrawable(drawable); } @VisibleForTesting diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java index 5c5eb8823c1..d52b441f9e0 100644 --- a/src/com/android/settings/media/MediaOutputSlice.java +++ b/src/com/android/settings/media/MediaOutputSlice.java @@ -23,8 +23,6 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.UserHandle; @@ -87,7 +85,7 @@ public class MediaOutputSlice implements CustomSliceable { final Drawable drawable = Utils.getBadgedIcon(mIconDrawableFactory, pm, mPackageName, UserHandle.myUserId()); - final IconCompat icon = IconCompat.createWithBitmap(getBitmapFromDrawable(drawable)); + final IconCompat icon = Utils.createIconWithDrawable(drawable); @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext); final SliceAction primarySliceAction = SliceAction.createDeeplink(getPrimaryAction(), icon, @@ -134,17 +132,6 @@ public class MediaOutputSlice implements CustomSliceable { return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); } - private Bitmap getBitmapFromDrawable(Drawable drawable) { - final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(bitmap); - - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - - return bitmap; - } - private ListBuilder.RowBuilder getMediaDeviceRow(MediaDevice device) { final String title = device.getName(); final PendingIntent broadcastAction = diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java index 7a6d1fa5480..5761ae59c77 100644 --- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java +++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java @@ -90,7 +90,7 @@ public class ContextualWifiSlice extends WifiSlice { } d.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); - return IconCompat.createWithBitmap(Utils.drawableToBitmap(d)); + return Utils.createIconWithDrawable(d); } @Override diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java index c10e0d64df9..37186cad8b1 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -34,6 +34,11 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.VectorDrawable; import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; @@ -48,6 +53,8 @@ import android.util.IconDrawableFactory; import android.widget.EditText; import android.widget.TextView; +import androidx.core.graphics.drawable.IconCompat; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -96,7 +103,7 @@ public class UtilsTest { } @Test - public void testGetWifiIpAddresses_succeeds() throws Exception { + public void getWifiIpAddresses_succeeds() throws Exception { when(wifiManager.getCurrentNetwork()).thenReturn(network); LinkAddress address = new LinkAddress(InetAddress.getByName("127.0.0.1"), 0); LinkProperties lp = new LinkProperties(); @@ -107,7 +114,7 @@ public class UtilsTest { } @Test - public void testGetWifiIpAddresses_nullLinkProperties() { + public void getWifiIpAddresses_nullLinkProperties() { when(wifiManager.getCurrentNetwork()).thenReturn(network); // Explicitly set the return value to null for readability sake. when(connectivityManager.getLinkProperties(network)).thenReturn(null); @@ -116,7 +123,7 @@ public class UtilsTest { } @Test - public void testGetWifiIpAddresses_nullNetwork() { + public void getWifiIpAddresses_nullNetwork() { // Explicitly set the return value to null for readability sake. when(wifiManager.getCurrentNetwork()).thenReturn(null); @@ -124,7 +131,7 @@ public class UtilsTest { } @Test - public void testInitializeVolumeDoesntBreakOnNullVolume() { + public void initializeVolumeDoesntBreakOnNullVolume() { VolumeInfo info = new VolumeInfo("id", 0, new DiskInfo("id", 0), ""); StorageManager storageManager = mock(StorageManager.class, RETURNS_DEEP_STUBS); when(storageManager.findVolumeById(anyString())).thenReturn(info); @@ -133,13 +140,13 @@ public class UtilsTest { } @Test - public void testGetInstallationStatus_notInstalled_shouldReturnUninstalled() { + public void getInstallationStatus_notInstalled_shouldReturnUninstalled() { assertThat(Utils.getInstallationStatus(new ApplicationInfo())) .isEqualTo(R.string.not_installed); } @Test - public void testGetInstallationStatus_enabled_shouldReturnInstalled() { + public void getInstallationStatus_enabled_shouldReturnInstalled() { final ApplicationInfo info = new ApplicationInfo(); info.flags = ApplicationInfo.FLAG_INSTALLED; info.enabled = true; @@ -148,7 +155,7 @@ public class UtilsTest { } @Test - public void testGetInstallationStatus_disabled_shouldReturnDisabled() { + public void getInstallationStatus_disabled_shouldReturnDisabled() { final ApplicationInfo info = new ApplicationInfo(); info.flags = ApplicationInfo.FLAG_INSTALLED; info.enabled = false; @@ -157,7 +164,7 @@ public class UtilsTest { } @Test - public void testIsProfileOrDeviceOwner_deviceOwnerApp_returnTrue() { + public void isProfileOrDeviceOwner_deviceOwnerApp_returnTrue() { when(mDevicePolicyManager.isDeviceOwnerAppOnAnyUser(PACKAGE_NAME)).thenReturn(true); assertThat(Utils.isProfileOrDeviceOwner(mUserManager, mDevicePolicyManager, PACKAGE_NAME)) @@ -165,7 +172,7 @@ public class UtilsTest { } @Test - public void testIsProfileOrDeviceOwner_profileOwnerApp_returnTrue() { + public void isProfileOrDeviceOwner_profileOwnerApp_returnTrue() { final List userInfos = new ArrayList<>(); userInfos.add(new UserInfo()); @@ -178,7 +185,7 @@ public class UtilsTest { } @Test - public void testSetEditTextCursorPosition_shouldGetExpectedEditTextLenght() { + public void setEditTextCursorPosition_shouldGetExpectedEditTextLenght() { final EditText editText = new EditText(mContext); final CharSequence text = "test"; editText.setText(text, TextView.BufferType.EDITABLE); @@ -189,7 +196,36 @@ public class UtilsTest { } @Test - public void testGetBadgedIcon_usePackageNameAndUserId() + public void createIconWithDrawable_BitmapDrawable() { + final Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + final BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), bitmap); + + final IconCompat icon = Utils.createIconWithDrawable(drawable); + + assertThat(icon.getBitmap()).isNotNull(); + } + + @Test + public void createIconWithDrawable_ColorDrawable() { + final ColorDrawable drawable = new ColorDrawable(Color.BLACK); + + final IconCompat icon = Utils.createIconWithDrawable(drawable); + + assertThat(icon.getBitmap()).isNotNull(); + } + + @Test + public void createIconWithDrawable_VectorDrawable() { + final VectorDrawable drawable = VectorDrawable.create(mContext.getResources(), + R.drawable.ic_settings_accent); + + final IconCompat icon = Utils.createIconWithDrawable(drawable); + + assertThat(icon.getBitmap()).isNotNull(); + } + + @Test + public void getBadgedIcon_usePackageNameAndUserId() throws PackageManager.NameNotFoundException { doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfoAsUser( PACKAGE_NAME, PackageManager.GET_META_DATA, USER_ID); diff --git a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java index a57fefc1370..bd2ce1e56cf 100644 --- a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java +++ b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java @@ -22,14 +22,11 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.util.Size; -import com.android.settings.R; - import com.google.zxing.BinaryBitmap; import com.google.zxing.LuminanceSource; import com.google.zxing.RGBLuminanceSource; From 5506e0c7326d47bf80e0e4ef610c106d85d4c639 Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Tue, 12 Feb 2019 15:55:12 -0800 Subject: [PATCH 4/4] Correct behavior of "Mobile data" button on opportunistic subscriptions. A check was added earlier to make sure in DSDS mode only one sub shows data on, which is not true for opportunistic subscriptions. For them, it should always be on (enforced by Telephony) and be disabled as it shouldn't be turned off. Bug: 124331860 Bug: 124683219 Test: manual, robolectric Change-Id: I53c8d881ff71ad25c418085d3eee63e55c786b8f --- .../MobileDataPreferenceController.java | 24 +++++++++++++------ .../MobileDataPreferenceControllerTest.java | 19 +++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java index f678e08032a..de54879abc2 100644 --- a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java +++ b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java @@ -22,21 +22,21 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.provider.Settings; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + import androidx.annotation.VisibleForTesting; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; -import com.android.settings.core.TogglePreferenceController; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnStart; -import com.android.settingslib.core.lifecycle.events.OnStop; - /** * Preference controller for "Mobile data" */ @@ -115,8 +115,18 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon @Override public boolean isChecked() { - return mTelephonyManager.isDataEnabled() - && mSubId == SubscriptionManager.getDefaultDataSubscriptionId(); + return mTelephonyManager.isDataEnabled(); + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + preference.setEnabled(!isOpportunistic()); + } + + private boolean isOpportunistic() { + SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(mSubId); + return info != null && info.isOpportunistic(); } public static Uri getObservableUri(int subId) { diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java index 4c242c147d0..1b7b4b47fc3 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java @@ -149,4 +149,23 @@ public class MobileDataPreferenceControllerTest { verify(mTelephonyManager).setDataEnabled(true); } + + @Test + public void isChecked_returnUserDataEnabled() { + mController.init(mFragmentManager, SUB_ID); + assertThat(mController.isChecked()).isFalse(); + + doReturn(true).when(mTelephonyManager).isDataEnabled(); + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void updateState_opportunistic_disabled() { + doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID); + mController.init(mFragmentManager, SUB_ID); + doReturn(true).when(mSubscriptionInfo).isOpportunistic(); + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isFalse(); + } }