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 01547ef9b31..4df5526ec1b 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 1d64fcae071..45b8960d613 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 3ee33aa0d86..bd55fd05e8a 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; @@ -271,102 +269,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)); - } -}