Add activityInfo and getIcon() method in Tile class.

Moved loading icon logic from TileUtils into Tile#getIcon().

TileUtils only load things once and cache forever but getIcon()
loads a fresh icon every time to avoid any caching issue.

Bug: 77600770
Test: robotests
Change-Id: I706225e382ebd5b72e91edef43bfc7427fa64590
This commit is contained in:
Fan Zhang
2018-07-23 16:51:14 -07:00
parent bf2f826a6c
commit 83dde97b30
7 changed files with 98 additions and 52 deletions

View File

@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,17 +17,19 @@
package com.android.settingslib.drawer;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.TextUtils;
import android.widget.RemoteViews;
import java.util.ArrayList;
@@ -36,24 +38,23 @@ import java.util.ArrayList;
*/
public class Tile implements Parcelable {
private static final String TAG = "Tile";
private ActivityInfo mActivityInfo;
/**
* Title of the tile that is shown to the user.
*
* @attr ref android.R.styleable#PreferenceHeader_title
*/
public CharSequence title;
/**
* Optional summary describing what this tile controls.
*
* @attr ref android.R.styleable#PreferenceHeader_summary
*/
public CharSequence summary;
/**
* Optional icon to show for this tile.
* @attr ref android.R.styleable#PreferenceHeader_icon
*/
public Icon icon;
/**
* Whether the icon can be tinted. This should be set to true for monochrome (single-color)
* icons that can be tinted to match the design.
@@ -95,8 +96,8 @@ public class Tile implements Parcelable {
*/
public String key;
public Tile() {
// Empty
public Tile(ActivityInfo activityInfo) {
mActivityInfo = activityInfo;
}
@Override
@@ -106,14 +107,9 @@ public class Tile implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mActivityInfo, flags);
TextUtils.writeToParcel(title, dest, flags);
TextUtils.writeToParcel(summary, dest, flags);
if (icon != null) {
dest.writeByte((byte) 1);
icon.writeToParcel(dest, flags);
} else {
dest.writeByte((byte) 0);
}
if (intent != null) {
dest.writeByte((byte) 1);
intent.writeToParcel(dest, flags);
@@ -133,12 +129,36 @@ public class Tile implements Parcelable {
dest.writeBoolean(isIconTintable);
}
/**
* Optional icon to show for this tile.
*
* @attr ref android.R.styleable#PreferenceHeader_icon
*/
public Icon getIcon() {
if (mActivityInfo == null || metaData == null) {
return null;
}
int iconResId = metaData.getInt(META_DATA_PREFERENCE_ICON);
// Set the icon
if (iconResId == 0) {
// Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
// ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this
// level is too complex because we don't have a strong threading contract for this class
if (!metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
iconResId = mActivityInfo.icon;
}
}
if (iconResId != 0) {
return Icon.createWithResource(mActivityInfo.packageName, iconResId);
} else {
return null;
}
}
public void readFromParcel(Parcel in) {
mActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
if (in.readByte() != 0) {
icon = Icon.CREATOR.createFromParcel(in);
}
if (in.readByte() != 0) {
intent = Intent.CREATOR.createFromParcel(in);
}
@@ -162,6 +182,7 @@ public class Tile implements Parcelable {
public Tile createFromParcel(Parcel source) {
return new Tile(source);
}
public Tile[] newArray(int size) {
return new Tile[size];
}
@@ -169,7 +190,7 @@ public class Tile implements Parcelable {
public boolean isPrimaryProfileOnly() {
String profile = metaData != null ?
metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
profile = (profile != null ? profile : PROFILE_ALL);
return TextUtils.equals(profile, PROFILE_PRIMARY);
}

View File

@@ -24,7 +24,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -303,7 +302,7 @@ public class TileUtils {
Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name);
Tile tile = addedCache.get(key);
if (tile == null) {
tile = new Tile();
tile = new Tile(activityInfo);
tile.intent = new Intent().setClassName(
activityInfo.packageName, activityInfo.name);
tile.category = categoryKey;
@@ -329,7 +328,6 @@ public class TileUtils {
boolean forceTintExternalIcon) {
if (applicationInfo.isSystemApp()) {
boolean forceTintIcon = false;
int icon = 0;
CharSequence title = null;
String summary = null;
String keyHint = null;
@@ -347,9 +345,6 @@ public class TileUtils {
}
if (res != null && metaData != null) {
if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
}
if (metaData.containsKey(META_DATA_PREFERENCE_ICON_TINTABLE)) {
if (forceTintIcon) {
Log.w(LOG_TAG, "Ignoring icon tintable for " + activityInfo);
@@ -390,18 +385,6 @@ public class TileUtils {
title = activityInfo.loadLabel(pm).toString();
}
// Set the icon
if (icon == 0) {
// Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
// ICON_URI should be loaded in app UI when need the icon object.
if (!tile.metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
icon = activityInfo.icon;
}
}
if (icon != 0) {
tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
}
// Set title and summary for the preference
tile.title = title;
tile.summary = summary;

View File

@@ -16,8 +16,16 @@
package com.android.settingslib.drawer;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
@@ -30,12 +38,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ProfileSelectDialogTest {
@@ -58,7 +60,7 @@ public class ProfileSelectDialogTest {
@Test
public void testUpdateUserHandlesIfNeeded_Normal() {
final Tile tile = new Tile();
final Tile tile = new Tile(new ActivityInfo());
tile.intent = new Intent();
tile.userHandle.add(NORMAL_USER);
@@ -71,7 +73,7 @@ public class ProfileSelectDialogTest {
@Test
public void testUpdateUserHandlesIfNeeded_Remove() {
final Tile tile = new Tile();
final Tile tile = new Tile(new ActivityInfo());
tile.intent = new Intent();
tile.userHandle.add(REMOVED_USER);
tile.userHandle.add(NORMAL_USER);

View File

@@ -1,27 +1,37 @@
package com.android.settingslib.drawer;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
import static com.google.common.truth.Truth.assertThat;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import com.android.settingslib.R;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class TileTest {
private ActivityInfo mActivityInfo;
private Tile mTile;
@Before
public void setUp() {
mTile = new Tile();
mActivityInfo = new ActivityInfo();
mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
mActivityInfo.icon = R.drawable.ic_plus;
mTile = new Tile(mActivityInfo);
mTile.metaData = new Bundle();
}
@@ -47,4 +57,34 @@ public class TileTest {
mTile.metaData = null;
assertThat(mTile.isPrimaryProfileOnly()).isFalse();
}
@Test
public void getIcon_noActivityOrMetadata_returnNull() {
final Tile tile1 = new Tile((ActivityInfo) null);
assertThat(tile1.getIcon()).isNull();
final Tile tile2 = new Tile(new ActivityInfo());
assertThat(tile2.getIcon()).isNull();
}
@Test
public void getIcon_providedByUri_returnNull() {
mTile.metaData.putString(META_DATA_PREFERENCE_ICON_URI, "content://foobar/icon");
assertThat(mTile.getIcon()).isNull();
}
@Test
public void getIcon_hasIconMetadata_returnIcon() {
mTile.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_info);
assertThat(mTile.getIcon().getResId()).isEqualTo(R.drawable.ic_info);
}
@Test
public void getIcon_noIconMetadata_returnActivityIcon() {
mTile.metaData.putInt(META_DATA_PREFERENCE_ICON, 0);
assertThat(mTile.getIcon().getResId()).isEqualTo(mActivityInfo.icon);
}
}

View File

@@ -314,7 +314,7 @@ public class TileUtilsTest {
false /* checkCategory */, true /* forceTintExternalIcon */);
assertThat(outTiles.size()).isEqualTo(1);
assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159);
assertThat(outTiles.get(0).getIcon().getResId()).isEqualTo(314159);
assertThat(outTiles.get(0).summary).isEqualTo("static-summary");
// Case 2: Empty bundle.
@@ -332,7 +332,7 @@ public class TileUtilsTest {
false /* checkCategory */, true /* forceTintExternalIcon */);
assertThat(outTiles.size()).isEqualTo(1);
assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159);
assertThat(outTiles.get(0).getIcon().getResId()).isEqualTo(314159);
assertThat(outTiles.get(0).summary).isEqualTo("static-summary");
}

View File

@@ -237,7 +237,7 @@ public class InputMethodAndSubtypeUtilCompatTest {
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
.isFalse();
}
}
private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
InputMethodSubtype... subtypes) {
@@ -254,7 +254,7 @@ public class InputMethodAndSubtypeUtilCompatTest {
si.exported = true;
si.nonLocalizedLabel = "Dummy IME";
ri.serviceInfo = si;
return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
}
private static InputMethodSubtype createDummySubtype(

View File

@@ -58,7 +58,7 @@ public class ThreadUtilsTest {
}
@Test
public void testPostOnMainThread_shouldRunOnMainTread() throws Exception {
public void testPostOnMainThread_shouldRunOnMainTread() {
TestRunnable cr = new TestRunnable();
ShadowLooper.pauseMainLooper();
ThreadUtils.postOnMainThread(cr);