From d06aaa8fd00321e938664a813c58a54118b5731f Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Tue, 28 Feb 2017 10:57:45 -0800 Subject: [PATCH] New design for instant apps in app details header Bug: 35098444 Test: make RunSettingsRoboTests In the previous design for instant apps, some metadata about the app such as developer title, maturity rating, etc. was going to be shown in the app header. In the latest design, we instead are just showing a little label that says "Instant app". The two CL's for this topic work together to change this: frameworks/base : adds code to work around the problem that robolectric doesn't know about the new isInstantApp method of the ApplicationInfo class, so we need to avoid calling it during tests. pacakges/apps/Settings: removes the code that previously displayed the instant app metadata, and instead just insert the "Instant app" label. Change-Id: I2cbc70bf4827c401e862c58ea4ca7f8f9ba1cf58 --- res/layout/app_details.xml | 45 ++----- res/values/strings.xml | 3 + .../applications/AppHeaderController.java | 30 +---- .../applications/AppInfoWithHeader.java | 2 + .../applications/InstalledAppDetails.java | 1 + .../instantapps/InstantAppDetails.java | 110 ---------------- .../applications/AppHeaderControllerTest.java | 99 ++------------- .../applications/AppInfoWithHeaderTest.java | 6 +- .../applications/InstantDataBuilder.java | 118 ------------------ 9 files changed, 39 insertions(+), 375 deletions(-) delete mode 100644 src/com/android/settings/applications/instantapps/InstantAppDetails.java delete mode 100644 tests/robotests/src/com/android/settings/applications/InstantDataBuilder.java diff --git a/res/layout/app_details.xml b/res/layout/app_details.xml index 6b6a5787c41..00d4cca4b34 100644 --- a/res/layout/app_details.xml +++ b/res/layout/app_details.xml @@ -87,6 +87,17 @@ android:gravity="start" android:paddingTop="8dp"/> + + - - - - - - - - - - - \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 71fd0947d92..ce87b575184 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8220,6 +8220,9 @@ On + + Instant app + Games diff --git a/src/com/android/settings/applications/AppHeaderController.java b/src/com/android/settings/applications/AppHeaderController.java index 01d5fc74d73..fa3d1d3819a 100644 --- a/src/com/android/settings/applications/AppHeaderController.java +++ b/src/com/android/settings/applications/AppHeaderController.java @@ -37,7 +37,6 @@ import android.widget.TextView; import com.android.settings.AppHeader; import com.android.settings.R; import com.android.settings.Utils; -import com.android.settings.applications.instantapps.InstantAppDetails; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.applications.ApplicationsState; @@ -81,7 +80,7 @@ public class AppHeaderController { @ActionType private int mRightAction; - private InstantAppDetails mInstantAppDetails; + private boolean mIsInstantApp; public AppHeaderController(Context context, Fragment fragment, View appHeader) { mContext = context; @@ -154,8 +153,8 @@ public class AppHeaderController { return this; } - public AppHeaderController setInstantAppDetails(InstantAppDetails instantAppDetails) { - mInstantAppDetails = instantAppDetails; + public AppHeaderController setIsInstantApp(boolean isInstantApp) { + this.mIsInstantApp = isInstantApp; return this; } @@ -220,26 +219,9 @@ public class AppHeaderController { bindAppHeaderButtons(); } - if (mInstantAppDetails != null) { - setText(R.id.instant_app_developer_title, mInstantAppDetails.developerTitle); - View maturity = mAppHeader.findViewById(R.id.instant_app_maturity); - - if (maturity != null) { - String maturityText = mInstantAppDetails.maturityRatingString; - Drawable maturityIcon = mInstantAppDetails.maturityRatingIcon; - if (!TextUtils.isEmpty(maturityText) || maturityIcon != null) { - maturity.setVisibility(View.VISIBLE); - } - setText(R.id.instant_app_maturity_text, maturityText); - if (maturityIcon != null) { - ImageView maturityIconView = (ImageView) mAppHeader.findViewById( - R.id.instant_app_maturity_icon); - if (maturityIconView != null) { - maturityIconView.setImageDrawable(maturityIcon); - } - } - } - setText(R.id.instant_app_monetization, mInstantAppDetails.monetizationNotice); + if (mIsInstantApp) { + setText(R.id.install_type, + mAppHeader.getResources().getString(R.string.install_type_instant)); } return mAppHeader; diff --git a/src/com/android/settings/applications/AppInfoWithHeader.java b/src/com/android/settings/applications/AppInfoWithHeader.java index 910b618b04f..4f3e8faeb98 100644 --- a/src/com/android/settings/applications/AppInfoWithHeader.java +++ b/src/com/android/settings/applications/AppInfoWithHeader.java @@ -23,6 +23,7 @@ import android.util.Log; import com.android.settings.AppHeader; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.applications.AppUtils; import static com.android.settings.applications.AppHeaderController.ActionType; @@ -52,6 +53,7 @@ public abstract class AppInfoWithHeader extends AppInfoBase { .setIcon(mPackageInfo.applicationInfo.loadIcon(mPm)) .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) .setSummary(mPackageInfo) + .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) .setPackageName(mPackageName) .setUid(mPackageInfo.applicationInfo.uid) .setButtonActions(ActionType.ACTION_APP_INFO, ActionType.ACTION_NONE) diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 0be4d9e8614..581b0aff486 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -563,6 +563,7 @@ public class InstalledAppDetails extends AppInfoBase .setLabel(mAppEntry) .setIcon(mAppEntry) .setSummary(getString(getInstallationStatus(mAppEntry.info))) + .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) .done(false /* rebindActions */); mVersionPreference.setSummary(getString(R.string.version_text, pkgInfo.versionName)); } else { diff --git a/src/com/android/settings/applications/instantapps/InstantAppDetails.java b/src/com/android/settings/applications/instantapps/InstantAppDetails.java deleted file mode 100644 index 8b54c207f1f..00000000000 --- a/src/com/android/settings/applications/instantapps/InstantAppDetails.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * 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 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.applications.instantapps; - -import android.graphics.drawable.Drawable; -import java.net.URL; - -/** - * Encapsulates state about instant apps that is provided by an app store implementation. - */ -public class InstantAppDetails { - - // Most of these members are self-explanatory; the one that may not be is - // monetizationNotice, which is a string alerting users that the app contains ads and/or uses - // in-app purchases (this may eventually become two separate members). - public final Drawable maturityRatingIcon; - public final String maturityRatingString; - public final String monetizationNotice; - public final String developerTitle; - public final URL privacyPolicy; - public final URL developerWebsite; - public final String developerEmail; - public final String developerMailingAddress; - - public static class Builder { - private Drawable mMaturityRatingIcon; - private String mMaturityRatingString; - private String mMonetizationNotice; - private String mDeveloperTitle; - private URL mPrivacyPolicy; - private URL mDeveloperWebsite; - private String mDeveloperEmail; - private String mDeveloperMailingAddress; - - public Builder maturityRatingIcon(Drawable maturityRatingIcon) { - this.mMaturityRatingIcon = maturityRatingIcon; - return this; - } - - public Builder maturityRatingString(String maturityRatingString) { - mMaturityRatingString = maturityRatingString; - return this; - } - - public Builder monetizationNotice(String monetizationNotice) { - mMonetizationNotice = monetizationNotice; - return this; - } - - public Builder developerTitle(String developerTitle) { - mDeveloperTitle = developerTitle; - return this; - } - - public Builder privacyPolicy(URL privacyPolicy) { - mPrivacyPolicy = privacyPolicy; - return this; - } - - public Builder developerWebsite(URL developerWebsite) { - mDeveloperWebsite = developerWebsite; - return this; - } - - public Builder developerEmail(String developerEmail) { - mDeveloperEmail = developerEmail; - return this; - } - - public Builder developerMailingAddress(String developerMailingAddress) { - mDeveloperMailingAddress = developerMailingAddress; - return this; - } - - public InstantAppDetails build() { - return new InstantAppDetails(mMaturityRatingIcon, mMaturityRatingString, - mMonetizationNotice, mDeveloperTitle, mPrivacyPolicy, mDeveloperWebsite, - mDeveloperEmail, mDeveloperMailingAddress); - } - } - - public static Builder builder() { return new Builder(); } - - private InstantAppDetails(Drawable maturityRatingIcon, String maturityRatingString, - String monetizationNotice, String developerTitle, URL privacyPolicy, - URL developerWebsite, String developerEmail, String developerMailingAddress) { - this.maturityRatingIcon = maturityRatingIcon; - this.maturityRatingString = maturityRatingString; - this.monetizationNotice = monetizationNotice; - this.developerTitle = developerTitle; - this.privacyPolicy = privacyPolicy; - this.developerWebsite = developerWebsite; - this.developerEmail = developerEmail; - this.developerMailingAddress = developerMailingAddress; - } -} diff --git a/tests/robotests/src/com/android/settings/applications/AppHeaderControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppHeaderControllerTest.java index 458af5b9a8d..33b9f901cf8 100644 --- a/tests/robotests/src/com/android/settings/applications/AppHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppHeaderControllerTest.java @@ -36,8 +36,6 @@ import android.widget.TextView; import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; -import com.android.settings.applications.InstantDataBuilder.Param; -import com.android.settings.applications.instantapps.InstantAppDetails; import com.android.settingslib.applications.ApplicationsState; import org.junit.Before; @@ -251,102 +249,27 @@ public class AppHeaderControllerTest { .isEqualTo(View.GONE); } - // Ensure that no instant app related information shows up when the AppHeaderController's - // InstantAppDetails are null. + // Ensure that the instant app label does not show up when we haven't told the controller the + // app is instant. @Test - public void instantApps_nullInstantAppDetails() { + public void instantApps_normalAppsDontGetLabel() { final View appHeader = mLayoutInflater.inflate(R.layout.app_details, null /* root */); mController = new AppHeaderController(mContext, mFragment, appHeader); - mController.setInstantAppDetails(null); mController.done(); - assertThat(appHeader.findViewById(R.id.instant_app_developer_title).getVisibility()) - .isEqualTo(View.GONE); - assertThat(appHeader.findViewById(R.id.instant_app_maturity).getVisibility()) - .isEqualTo(View.GONE); - assertThat(appHeader.findViewById(R.id.instant_app_monetization).getVisibility()) + assertThat(appHeader.findViewById(R.id.install_type).getVisibility()) .isEqualTo(View.GONE); } - // Ensure that no instant app related information shows up when the AppHeaderController has - // a non-null InstantAppDetails, but each member of it is null. + // Test that the "instant apps" label is present in the header when we have an instant app. @Test - public void instantApps_detailsMembersNull() { + public void instantApps_expectedHeaderItem() { final View appHeader = mLayoutInflater.inflate(R.layout.app_details, null /* root */); mController = new AppHeaderController(mContext, mFragment, appHeader); - - InstantAppDetails details = InstantDataBuilder.build(mContext, EnumSet.noneOf(Param.class)); - mController.setInstantAppDetails(details); + mController.setIsInstantApp(true); mController.done(); - assertThat(appHeader.findViewById(R.id.instant_app_developer_title).getVisibility()) - .isEqualTo(View.GONE); - assertThat(appHeader.findViewById(R.id.instant_app_maturity).getVisibility()) - .isEqualTo(View.GONE); - assertThat(appHeader.findViewById(R.id.instant_app_monetization).getVisibility()) - .isEqualTo(View.GONE); - } - - // Helper to assert a TextView for a given id is visible and has a certain string value. - private void assertVisibleContent(View header, @IdRes int id, String expectedValue) { - TextView view = (TextView)header.findViewById(id); - assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(view.getText()).isEqualTo(expectedValue); - } - - // Helper to assert an ImageView for a given id is visible and has a certain Drawable value. - private void assertVisibleContent(View header, @IdRes int id, Drawable expectedValue) { - ImageView view = (ImageView)header.findViewById(id); - assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(view.getDrawable()).isEqualTo(expectedValue); - } - - // Test that expected items are present in the header when we have a complete InstantAppDetails. - @Test - public void instantApps_expectedHeaderItems() { - final View header = mLayoutInflater.inflate(R.layout.app_details, null /* root */); - mController = new AppHeaderController(mContext, mFragment, header); - - InstantAppDetails details = InstantDataBuilder.build(mContext); - mController.setInstantAppDetails(details); - mController.done(); - - assertVisibleContent(header, R.id.instant_app_developer_title, details.developerTitle); - assertVisibleContent(header, R.id.instant_app_maturity_icon, - details.maturityRatingIcon); - assertVisibleContent(header, R.id.instant_app_maturity_text, - details.maturityRatingString); - assertVisibleContent(header, R.id.instant_app_monetization, - details.monetizationNotice); - } - - // Test having each member of InstantAppDetails be null. - @Test - public void instantApps_expectedHeaderItemsWithSingleNullMembers() { - final EnumSet allParams = EnumSet.allOf(Param.class); - for (Param paramToRemove : allParams) { - EnumSet params = allParams.clone(); - params.remove(paramToRemove); - final View header = mLayoutInflater.inflate(R.layout.app_details, null /* root */); - mController = new AppHeaderController(mContext, mFragment, header); - InstantAppDetails details = InstantDataBuilder.build(mContext, params); - mController.setInstantAppDetails(details); - mController.done(); - - if (params.contains(Param.DEVELOPER_TITLE)) { - assertVisibleContent(header, R.id.instant_app_developer_title, - details.developerTitle); - } - if (params.contains(Param.MATURITY_RATING_ICON)) { - assertVisibleContent(header, R.id.instant_app_maturity_icon, - details.maturityRatingIcon); - } - if (params.contains(Param.MATURITY_RATING_STRING)) { - assertVisibleContent(header, R.id.instant_app_maturity_text, - details.maturityRatingString); - } - if (params.contains(Param.MONETIZATION_NOTICE)) { - assertVisibleContent(header, R.id.instant_app_monetization, - details.monetizationNotice); - } - } + TextView label = (TextView)appHeader.findViewById(R.id.install_type); + assertThat(label.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(label.getText()).isEqualTo( + appHeader.getResources().getString(R.string.install_type_instant)); } } diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java index 4cc39fb40c2..56d4a83f325 100644 --- a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java @@ -28,6 +28,8 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import org.junit.Before; import org.junit.Test; @@ -37,6 +39,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; +import org.robolectric.util.ReflectionHelpers; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; @@ -88,7 +91,8 @@ public class AppInfoWithHeaderTest { mScreen = mock(PreferenceScreen.class); mPackageInfo = new PackageInfo(); mPackageInfo.applicationInfo = new ApplicationInfo(); - + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (info -> false)); when(mManager.getContext()) .thenReturn(ShadowApplication.getInstance().getApplicationContext()); } diff --git a/tests/robotests/src/com/android/settings/applications/InstantDataBuilder.java b/tests/robotests/src/com/android/settings/applications/InstantDataBuilder.java deleted file mode 100644 index 81ebe06e11e..00000000000 --- a/tests/robotests/src/com/android/settings/applications/InstantDataBuilder.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); 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 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package com.android.settings.applications; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; - -import com.android.settings.R; -import com.android.settings.applications.instantapps.InstantAppDetails; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.EnumSet; - -/** - * Utility class for generating fake InstantAppDetails data to use in tests. - */ -public class InstantDataBuilder { - public enum Param { - MATURITY_RATING_ICON, - MATURITY_RATING_STRING, - MONETIZATION_NOTICE, - DEVELOPER_TITLE, - PRIVACY_POLICY, - DEVELOPER_WEBSITE, - DEVELOPER_EMAIL, - DEVELOPER_MAILING_ADDRESS - } - - /** - * Creates an InstantAppDetails with any desired combination of null/non-null members. - * - * @param context An optional context, required only if MATURITY_RATING_ICON is a member of - * params - * @param params Specifies which elements of the returned InstantAppDetails should be non-null - * @return InstantAppDetails - */ - public static InstantAppDetails build(@Nullable Context context, EnumSet params) { - Drawable ratingIcon = null; - String rating = null; - String monetizationNotice = null; - String developerTitle = null; - URL privacyPolicy = null; - URL developerWebsite = null; - String developerEmail = null; - String developerMailingAddress = null; - - if (params.contains(Param.MATURITY_RATING_ICON)) { - ratingIcon = context.getDrawable(R.drawable.ic_android); - } - if (params.contains(Param.MATURITY_RATING_STRING)) { - rating = "everyone"; - } - if (params.contains(Param.MONETIZATION_NOTICE)) { - monetizationNotice = "Uses in-app purchases"; - } - if (params.contains(Param.DEVELOPER_TITLE)) { - developerTitle = "Instant Apps Inc."; - } - if (params.contains(Param.DEVELOPER_EMAIL)) { - developerEmail = "developer@instant-apps.com"; - } - if (params.contains(Param.DEVELOPER_MAILING_ADDRESS)) { - developerMailingAddress = "1 Main Street, Somewhere, CA, 94043"; - } - - if (params.contains(Param.PRIVACY_POLICY)) { - try { - privacyPolicy = new URL("https://test.com/privacy"); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - if (params.contains(Param.DEVELOPER_WEBSITE)) { - try { - developerWebsite = new URL("https://test.com"); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - - return InstantAppDetails.builder() - .maturityRatingIcon(ratingIcon) - .maturityRatingString(rating) - .monetizationNotice(monetizationNotice) - .developerTitle(developerTitle) - .privacyPolicy(privacyPolicy) - .developerWebsite(developerWebsite) - .developerEmail(developerEmail) - .developerMailingAddress(developerMailingAddress) - .build(); - } - - /** - * Convenience method to create an InstantAppDetails with all non-null members. - * - * @param context a required Context for loading a test maturity rating icon - * @return InstantAppDetails - */ - public static InstantAppDetails build(Context context) { - return build(context, EnumSet.allOf(Param.class)); - } -}