Loading res/layout/app_details.xml +35 −0 Original line number Original line Diff line number Diff line Loading @@ -75,6 +75,41 @@ </LinearLayout> </LinearLayout> <TextView android:id="@+id/instant_app_developer_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:visibility="gone"/> <LinearLayout android:id="@+id/instant_app_maturity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:visibility="gone"> <ImageView android:id="@+id/instant_app_maturity_icon" android:layout_width="40dp" android:layout_height="40dp" android:scaleType="fitXY"/> <TextView android:id="@+id/instant_app_maturity_text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> <TextView android:id="@+id/instant_app_monetization" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:visibility="gone"/> <LinearLayout <LinearLayout android:id="@+id/app_detail_links" android:id="@+id/app_detail_links" android:layout_width="match_parent" android:layout_width="match_parent" Loading src/com/android/settings/applications/AppHeaderController.java +31 −0 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.widget.TextView; import com.android.settings.AppHeader; import com.android.settings.AppHeader; import com.android.settings.R; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.Utils; import com.android.settings.applications.instantapps.InstantAppDetails; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState; import java.lang.annotation.Retention; import java.lang.annotation.Retention; Loading Loading @@ -78,6 +79,8 @@ public class AppHeaderController { @ActionType @ActionType private int mRightAction; private int mRightAction; private InstantAppDetails mInstantAppDetails; public AppHeaderController(Context context, Fragment fragment, View appHeader) { public AppHeaderController(Context context, Fragment fragment, View appHeader) { mContext = context; mContext = context; mFragment = fragment; mFragment = fragment; Loading Loading @@ -147,6 +150,11 @@ public class AppHeaderController { return this; return this; } } public AppHeaderController setInstantAppDetails(InstantAppDetails instantAppDetails) { mInstantAppDetails = instantAppDetails; return this; } /** /** * Binds app header view and data from {@code PackageInfo} and {@code AppEntry}. * Binds app header view and data from {@code PackageInfo} and {@code AppEntry}. */ */ Loading Loading @@ -207,6 +215,29 @@ public class AppHeaderController { if (rebindActions) { if (rebindActions) { bindAppHeaderButtons(); 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); } return mAppHeader; return mAppHeader; } } Loading src/com/android/settings/applications/instantapps/InstantAppDetails.java 0 → 100644 +110 −0 Original line number Original line Diff line number Diff line /* * 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; } } tests/robotests/src/com/android/settings/applications/AppHeaderControllerTest.java +106 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.settings.applications; package com.android.settings.applications; import android.annotation.IdRes; import android.app.Activity; import android.app.Activity; import android.app.Fragment; import android.app.Fragment; import android.content.Context; import android.content.Context; Loading @@ -24,15 +25,19 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.os.UserHandle; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference; import android.view.LayoutInflater; import android.view.LayoutInflater; import android.view.View; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.TextView; import com.android.settings.R; import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; 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 com.android.settingslib.applications.ApplicationsState; import org.junit.Before; import org.junit.Before; Loading @@ -51,6 +56,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import java.util.EnumSet; @RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class AppHeaderControllerTest { public class AppHeaderControllerTest { Loading Loading @@ -243,4 +250,103 @@ public class AppHeaderControllerTest { assertThat(appLinks.findViewById(R.id.right_button).getVisibility()) assertThat(appLinks.findViewById(R.id.right_button).getVisibility()) .isEqualTo(View.GONE); .isEqualTo(View.GONE); } } // Ensure that no instant app related information shows up when the AppHeaderController's // InstantAppDetails are null. @Test public void instantApps_nullInstantAppDetails() { 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()) .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 public void instantApps_detailsMembersNull() { 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.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<Param> allParams = EnumSet.allOf(Param.class); for (Param paramToRemove : allParams) { EnumSet<Param> 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); } } } } } tests/robotests/src/com/android/settings/applications/InstantDataBuilder.java 0 → 100644 +118 −0 Original line number Original line Diff line number Diff line /** * 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<Param> 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)); } } Loading
res/layout/app_details.xml +35 −0 Original line number Original line Diff line number Diff line Loading @@ -75,6 +75,41 @@ </LinearLayout> </LinearLayout> <TextView android:id="@+id/instant_app_developer_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:visibility="gone"/> <LinearLayout android:id="@+id/instant_app_maturity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:visibility="gone"> <ImageView android:id="@+id/instant_app_maturity_icon" android:layout_width="40dp" android:layout_height="40dp" android:scaleType="fitXY"/> <TextView android:id="@+id/instant_app_maturity_text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> <TextView android:id="@+id/instant_app_monetization" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:visibility="gone"/> <LinearLayout <LinearLayout android:id="@+id/app_detail_links" android:id="@+id/app_detail_links" android:layout_width="match_parent" android:layout_width="match_parent" Loading
src/com/android/settings/applications/AppHeaderController.java +31 −0 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.widget.TextView; import com.android.settings.AppHeader; import com.android.settings.AppHeader; import com.android.settings.R; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.Utils; import com.android.settings.applications.instantapps.InstantAppDetails; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState; import java.lang.annotation.Retention; import java.lang.annotation.Retention; Loading Loading @@ -78,6 +79,8 @@ public class AppHeaderController { @ActionType @ActionType private int mRightAction; private int mRightAction; private InstantAppDetails mInstantAppDetails; public AppHeaderController(Context context, Fragment fragment, View appHeader) { public AppHeaderController(Context context, Fragment fragment, View appHeader) { mContext = context; mContext = context; mFragment = fragment; mFragment = fragment; Loading Loading @@ -147,6 +150,11 @@ public class AppHeaderController { return this; return this; } } public AppHeaderController setInstantAppDetails(InstantAppDetails instantAppDetails) { mInstantAppDetails = instantAppDetails; return this; } /** /** * Binds app header view and data from {@code PackageInfo} and {@code AppEntry}. * Binds app header view and data from {@code PackageInfo} and {@code AppEntry}. */ */ Loading Loading @@ -207,6 +215,29 @@ public class AppHeaderController { if (rebindActions) { if (rebindActions) { bindAppHeaderButtons(); 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); } return mAppHeader; return mAppHeader; } } Loading
src/com/android/settings/applications/instantapps/InstantAppDetails.java 0 → 100644 +110 −0 Original line number Original line Diff line number Diff line /* * 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; } }
tests/robotests/src/com/android/settings/applications/AppHeaderControllerTest.java +106 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.settings.applications; package com.android.settings.applications; import android.annotation.IdRes; import android.app.Activity; import android.app.Activity; import android.app.Fragment; import android.app.Fragment; import android.content.Context; import android.content.Context; Loading @@ -24,15 +25,19 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.os.UserHandle; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference; import android.view.LayoutInflater; import android.view.LayoutInflater; import android.view.View; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.TextView; import com.android.settings.R; import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; 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 com.android.settingslib.applications.ApplicationsState; import org.junit.Before; import org.junit.Before; Loading @@ -51,6 +56,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import java.util.EnumSet; @RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class AppHeaderControllerTest { public class AppHeaderControllerTest { Loading Loading @@ -243,4 +250,103 @@ public class AppHeaderControllerTest { assertThat(appLinks.findViewById(R.id.right_button).getVisibility()) assertThat(appLinks.findViewById(R.id.right_button).getVisibility()) .isEqualTo(View.GONE); .isEqualTo(View.GONE); } } // Ensure that no instant app related information shows up when the AppHeaderController's // InstantAppDetails are null. @Test public void instantApps_nullInstantAppDetails() { 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()) .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 public void instantApps_detailsMembersNull() { 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.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<Param> allParams = EnumSet.allOf(Param.class); for (Param paramToRemove : allParams) { EnumSet<Param> 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); } } } } }
tests/robotests/src/com/android/settings/applications/InstantDataBuilder.java 0 → 100644 +118 −0 Original line number Original line Diff line number Diff line /** * 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<Param> 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)); } }