Added transparency-metadata display.
This feature enables 'App Details' to support MBAs with metadata file and display in an offline UI. This change includes a new fragment and user view that sources data from within any MBAs apk file that contains application-metadata.xml. Test: make -j64 RunSettingsRoboTests , AVD manual test Change-Id: If7822100a90f5fb8d33ce5d85958391fc33ecbee Bug: 244215932
This commit is contained in:
@@ -4605,6 +4605,17 @@
|
||||
android:exported="false">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.android.settings.applications.mobilebundledapps.MobileBundledAppDetailsActivity"
|
||||
android:label="@string/mobile_bundled_apps_details_title"
|
||||
android:exported="true"
|
||||
android:icon="@drawable/ic_homepage_connected_device">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.settings.TRANSPARENCY_METADATA" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- This is the longest AndroidManifest.xml ever. -->
|
||||
</application>
|
||||
</manifest>
|
||||
|
107
res/layout/mobile_bundled_apps_details_fragment.xml
Normal file
107
res/layout/mobile_bundled_apps_details_fragment.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/app_details_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contains_ads_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mobile_bundled_apps_details_contains_ad_title"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/contains_ads"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/developer_info_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mobile_bundled_apps_details_developer_info_title"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<LinearLayout
|
||||
android:id="@+id/developer_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contact_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mobile_bundled_apps_details_contact_title"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/contact_url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
<TextView
|
||||
android:id="@+id/contact_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mobile_bundled_apps_details_description_title"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/privacy_policy_url_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mobile_bundled_apps_details_privacy_policy_title"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/privacy_policy_url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mobile_bundled_apps_details_category_title"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/category"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
50
res/layout/mobile_bundled_apps_developer_fragment_row.xml
Normal file
50
res/layout/mobile_bundled_apps_developer_fragment_row.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
<TextView
|
||||
android:id="@+id/mba_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"/>
|
||||
<TextView
|
||||
android:id="@+id/developer_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
<TextView
|
||||
android:id="@+id/developer_relationship"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
<TextView
|
||||
android:id="@+id/developer_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
<TextView
|
||||
android:id="@+id/developer_country"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
@@ -11907,4 +11907,26 @@
|
||||
<!-- [CHAR LIMIT=NONE] Title for Accessibility Software Cursor setting for keyboard shift. -->
|
||||
<string name="software_cursor_trigger_keyboard_shift_enabled_title" translatable="false">Shift gesture detection region above keyboard</string>
|
||||
|
||||
<!-- Mobile Bundled Apps Transparency Metadata-->
|
||||
|
||||
<!-- [CHAR_LIMIT=NONE] Label for mobile bundled apps screen -->
|
||||
<string name="mobile_bundled_apps">Mobile bundled apps</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Title for mobile bundled apps screen -->
|
||||
<string name="mobile_bundled_apps_title">Mobile bundled apps</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Title for mobile bundled apps transparency information details screen -->
|
||||
<string name="mobile_bundled_apps_details_title">Mobile bundled apps transparency info</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
|
||||
<string name="mobile_bundled_apps_details_contains_ad_title">Contains ads</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
|
||||
<string name="mobile_bundled_apps_details_developer_info_title">Developer(s) Information</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
|
||||
<string name="mobile_bundled_apps_details_contact_title">Contact Information</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
|
||||
<string name="mobile_bundled_apps_details_description_title">Description</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
|
||||
<string name="mobile_bundled_apps_details_privacy_policy_title">Privacy Policy</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
|
||||
<string name="mobile_bundled_apps_details_category_title">Category</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Summary for App Details in App Info page if app is mobile bundled app -->
|
||||
<string name="app_install_details_mba_summary">Mobile Bundled App</string>
|
||||
</resources>
|
||||
|
26
res/xml/mobile_bundled_apps_details_preference.xml
Normal file
26
res/xml/mobile_bundled_apps_details_preference.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Copyright (C) 2022 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="">
|
||||
<com.android.settingslib.widget.LayoutPreference
|
||||
android:key="metadata"
|
||||
android:selectable="false"
|
||||
android:layout="@layout/mobile_bundled_apps_details_fragment" />
|
||||
</PreferenceScreen>
|
@@ -56,6 +56,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.applications.manageapplications.ManageApplications;
|
||||
import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils;
|
||||
import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetailsPreferenceController;
|
||||
import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetailPreferenceController;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
@@ -162,6 +163,8 @@ public class AppInfoDashboardFragment extends DashboardFragment
|
||||
use(AppInstallerInfoPreferenceController.class);
|
||||
installer.setPackageName(packageName);
|
||||
installer.setParentFragment(this);
|
||||
installer.setMbaWithMetadataStatus(ApplicationMetadataUtils.getDefaultInstance(),
|
||||
packageName);
|
||||
use(AppInstallerPreferenceCategoryController.class).setChildren(Arrays.asList(installer));
|
||||
use(AppNotificationPreferenceController.class).setParentFragment(this);
|
||||
|
||||
|
@@ -16,42 +16,50 @@
|
||||
|
||||
package com.android.settings.applications.appinfo;
|
||||
|
||||
import static com.android.settings.applications.mobilebundledapps.MobileBundledAppDetailsActivity.ACTION_TRANSPARENCY_METADATA;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.UserManager;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.applications.AppStoreUtil;
|
||||
import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils;
|
||||
import com.android.settingslib.applications.AppUtils;
|
||||
|
||||
public class AppInstallerInfoPreferenceController extends AppInfoPreferenceControllerBase {
|
||||
|
||||
private static final String KEY_ENABLE_PROMPT = "enable_prompt";
|
||||
private String mPackageName;
|
||||
private String mInstallerPackage;
|
||||
private CharSequence mInstallerLabel;
|
||||
private Boolean mAppIsMbaWithMetadata;
|
||||
private Boolean mEnableMbaUiFlag = false;
|
||||
|
||||
public AppInstallerInfoPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
updateFromDeviceConfigFlags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (UserManager.get(mContext).isManagedProfile()) {
|
||||
if (UserManager.get(mContext).isManagedProfile()
|
||||
|| AppUtils.isMainlineModule(mContext.getPackageManager(), mPackageName)) {
|
||||
return DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
if (AppUtils.isMainlineModule(mContext.getPackageManager(), mPackageName)) {
|
||||
return DISABLED_FOR_USER;
|
||||
if (mInstallerLabel != null || (mAppIsMbaWithMetadata && mEnableMbaUiFlag)) {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
return mInstallerLabel != null ? AVAILABLE : DISABLED_FOR_USER;
|
||||
return DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
public void updateState(final Preference preference) {
|
||||
final int detailsStringId = AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
|
||||
? R.string.instant_app_details_summary
|
||||
: R.string.app_install_details_summary;
|
||||
@@ -60,14 +68,52 @@ public class AppInstallerInfoPreferenceController extends AppInfoPreferenceContr
|
||||
Intent intent = AppStoreUtil.getAppStoreLink(mContext, mInstallerPackage, mPackageName);
|
||||
if (intent != null) {
|
||||
preference.setIntent(intent);
|
||||
} else if (mAppIsMbaWithMetadata && mEnableMbaUiFlag) {
|
||||
preference.setIntent(generateMetadataXmlViewerIntent());
|
||||
preference.setSummary(mContext.getString(R.string.app_install_details_mba_summary));
|
||||
} else {
|
||||
preference.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
/**
|
||||
* Sets the packageName in context for the controller.
|
||||
*/
|
||||
public void setPackageName(final String packageName) {
|
||||
mPackageName = packageName;
|
||||
mInstallerPackage = AppStoreUtil.getInstallerPackageName(mContext, mPackageName);
|
||||
mInstallerLabel = Utils.getApplicationLabel(mContext, mInstallerPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups and determines if the current package in context is an mobile-bundled-app with
|
||||
* an application metadata file embedded within.
|
||||
*/
|
||||
public void setMbaWithMetadataStatus(final ApplicationMetadataUtils appMetadataUtils,
|
||||
final String packageName) {
|
||||
mAppIsMbaWithMetadata = appMetadataUtils.packageContainsXmlFile(
|
||||
mContext.getPackageManager(), packageName);
|
||||
}
|
||||
|
||||
private Intent generateMetadataXmlViewerIntent() {
|
||||
final Intent metadataXmlIntent = new Intent(ACTION_TRANSPARENCY_METADATA)
|
||||
.setPackage(mContext.getPackageName());
|
||||
metadataXmlIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
|
||||
return metadataXmlIntent;
|
||||
}
|
||||
|
||||
private void updateFromDeviceConfigFlags() {
|
||||
String enablePromptFlag = DeviceConfig.getProperty(
|
||||
DeviceConfig.NAMESPACE_TRANSPARENCY_METADATA,
|
||||
KEY_ENABLE_PROMPT);
|
||||
//No-op for empty field and relies on default value of false
|
||||
if (!TextUtils.isEmpty(enablePromptFlag)) {
|
||||
setEnableMbaFlag(Boolean.parseBoolean(enablePromptFlag));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setEnableMbaFlag(final boolean flagValue) {
|
||||
mEnableMbaUiFlag = flagValue;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.mobilebundledapps;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
/**
|
||||
* Used for parsing application-metadata.xml and return relevant fields
|
||||
*/
|
||||
public class ApplicationMetadataUtils {
|
||||
private static final String TAG = ApplicationMetadataUtils.class.getSimpleName();
|
||||
|
||||
private static final ApplicationMetadataUtils DEFAULT_INSTANCE = new ApplicationMetadataUtils();
|
||||
private static final String TRANSPARENCY_XML_DIR = "APP-INF/application-metadata.xml";
|
||||
private static final String DESCRIPTION_TAG = "description";
|
||||
private static final String CONTAINS_ADS_TAG = "contains-ads";
|
||||
private static final String PRIVACY_POLICY_TAG = "privacy-policy";
|
||||
private static final String CONTACT_TAG = "contact";
|
||||
private static final String CATEGORY_TAG = "category";
|
||||
private static final String DEVELOPER_TAG = "developer";
|
||||
private static final String URL_TAG = "url";
|
||||
private static final String EMAIL_TAG = "email";
|
||||
private static final String NAME_TAG = "name";
|
||||
private static final String RELATIONSHIP_TAG = "relationship";
|
||||
private static final String COUNTRY_TAG = "country";
|
||||
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
private Document mXmlDoc;
|
||||
|
||||
@VisibleForTesting
|
||||
ApplicationMetadataUtils() {
|
||||
mPackageManager = null;
|
||||
}
|
||||
|
||||
//Need to create singleton factory as Android is unable to mock static for testing.
|
||||
public static ApplicationMetadataUtils getDefaultInstance() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new instance that also provisions and reads the XML file.
|
||||
*/
|
||||
public static ApplicationMetadataUtils newInstance(final PackageManager packageManager,
|
||||
String packageName) {
|
||||
return new ApplicationMetadataUtils(packageManager, packageName);
|
||||
}
|
||||
private ApplicationMetadataUtils(final PackageManager packageManager,
|
||||
final String packageName) {
|
||||
mPackageManager = packageManager;
|
||||
try (ZipFile apk = new ZipFile(getApkDirectory(packageName, mPackageManager))) {
|
||||
mXmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
.parse(apk.getInputStream(apk.getEntry(TRANSPARENCY_XML_DIR)));
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setXmlDoc(final Document xmlDoc) {
|
||||
mXmlDoc = xmlDoc;
|
||||
}
|
||||
|
||||
private static String getApkDirectory(final String packageName,
|
||||
final PackageManager packageManager)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
return packageManager
|
||||
.getApplicationInfo(packageName,
|
||||
PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA))
|
||||
.sourceDir;
|
||||
}
|
||||
public boolean getContainsAds() {
|
||||
return mXmlDoc != null
|
||||
&& mXmlDoc.getElementsByTagName(CONTAINS_ADS_TAG) != null
|
||||
&& mXmlDoc.getElementsByTagName(CONTAINS_ADS_TAG).getLength() > 0;
|
||||
}
|
||||
|
||||
public String getPrivacyPolicyUrl() {
|
||||
return retrieveElementAttributeValue(PRIVACY_POLICY_TAG, URL_TAG);
|
||||
}
|
||||
|
||||
private String retrieveElementAttributeValue(final String elementTag, final String attribute) {
|
||||
try {
|
||||
return mXmlDoc.getElementsByTagName(elementTag).item(0)
|
||||
.getAttributes().getNamedItem(attribute).getNodeValue();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return retrieveElementValue(DESCRIPTION_TAG);
|
||||
}
|
||||
|
||||
private String retrieveElementValue(final String elementTag) {
|
||||
try {
|
||||
return mXmlDoc.getElementsByTagName(elementTag).item(0).getTextContent();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getCategoryName() {
|
||||
return retrieveElementAttributeValue(CATEGORY_TAG, NAME_TAG);
|
||||
}
|
||||
|
||||
public String getContactUrl() {
|
||||
return retrieveElementAttributeValue(CONTACT_TAG, URL_TAG);
|
||||
}
|
||||
|
||||
public String getContactEmail() {
|
||||
return retrieveElementAttributeValue(CONTACT_TAG, EMAIL_TAG);
|
||||
}
|
||||
|
||||
public String getPlayStoreUrl() {
|
||||
return retrieveElementValue(DESCRIPTION_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of relevant major parties involved with this MBA
|
||||
*/
|
||||
public List<MbaDeveloper> getDevelopers() {
|
||||
final List<MbaDeveloper> developersDetails = new ArrayList();
|
||||
try {
|
||||
final NodeList developers = mXmlDoc.getElementsByTagName(DEVELOPER_TAG);
|
||||
if (developers == null) return developersDetails;
|
||||
for (int i = 0; i < developers.getLength(); ++i) {
|
||||
final NamedNodeMap developerAttributes = developers.item(i).getAttributes();
|
||||
developersDetails.add(new MbaDeveloper(
|
||||
developerAttributes.getNamedItem(NAME_TAG).getNodeValue(),
|
||||
developerAttributes.getNamedItem(RELATIONSHIP_TAG).getNodeValue(),
|
||||
developerAttributes.getNamedItem(EMAIL_TAG).getNodeValue(),
|
||||
developerAttributes.getNamedItem(COUNTRY_TAG).getNodeValue()
|
||||
));
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.d(TAG, e.getMessage());
|
||||
}
|
||||
return developersDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the a package can be parsed and extrapolate metadata from.
|
||||
*/
|
||||
public boolean packageContainsXmlFile(final PackageManager packageManager,
|
||||
final String packageName) {
|
||||
try (ZipFile apk = new ZipFile(getApkDirectory(packageName, packageManager))) {
|
||||
return apk.getEntry(TRANSPARENCY_XML_DIR) != null;
|
||||
} catch (final Exception e) {
|
||||
Log.d(TAG, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to return developer details
|
||||
*/
|
||||
public static class MbaDeveloper {
|
||||
public final String name;
|
||||
public final String relationship;
|
||||
public final String email;
|
||||
public final String country;
|
||||
|
||||
public MbaDeveloper(final String name,
|
||||
final String relationship,
|
||||
final String email,
|
||||
final String country) {
|
||||
this.name = name;
|
||||
this.relationship = relationship;
|
||||
this.email = email;
|
||||
this.country = country;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.mobilebundledapps;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.android.settings.SettingsActivity;
|
||||
/**
|
||||
* An activity that is used to parse and display mobile-bundled apps application metadata xml file.
|
||||
*/
|
||||
public class MobileBundledAppDetailsActivity extends SettingsActivity {
|
||||
public static final String ACTION_TRANSPARENCY_METADATA =
|
||||
"android.settings.TRANSPARENCY_METADATA";
|
||||
|
||||
public MobileBundledAppDetailsActivity() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
final Intent modIntent = new Intent(super.getIntent());
|
||||
modIntent.setData(Uri.parse("package:"
|
||||
+ super.getIntent().getExtra(Intent.EXTRA_PACKAGE_NAME).toString()));
|
||||
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, MobileBundledAppsDetailsFragment.class.getName());
|
||||
return modIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(final String fragmentName) {
|
||||
return MobileBundledAppsDetailsFragment.class.getName().equals(fragmentName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.mobilebundledapps;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppInfoWithHeader;
|
||||
import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils.MbaDeveloper;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A fragment for retrieving the transparency metadata and PSL in the in-APK XML file and displaying
|
||||
* them.
|
||||
*/
|
||||
public class MobileBundledAppsDetailsFragment extends AppInfoWithHeader {
|
||||
private static final String METADATA_PREF_KEY = "metadata";
|
||||
|
||||
protected PackageManager mPackageManager;
|
||||
private Context mContext;
|
||||
private LayoutPreference mMetadataPreferenceView;
|
||||
private ApplicationsState mApplicationState;
|
||||
private boolean mCreated = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mContext = getActivity();
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
addPreferencesFromResource(R.xml.mobile_bundled_apps_details_preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
if (mCreated) {
|
||||
return;
|
||||
}
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
final ApplicationMetadataUtils appUtil = ApplicationMetadataUtils.newInstance(
|
||||
mPackageManager,
|
||||
mPackageName);
|
||||
if (mAppEntry == null) {
|
||||
mApplicationState =
|
||||
ApplicationsState.getInstance((Application) (mContext.getApplicationContext()));
|
||||
mAppEntry = mApplicationState.getEntry(mPackageName, mContext.getUserId());
|
||||
}
|
||||
mMetadataPreferenceView = findPreference(METADATA_PREF_KEY);
|
||||
createView(appUtil);
|
||||
mCreated = true;
|
||||
}
|
||||
|
||||
private void createView(final ApplicationMetadataUtils appUtil) {
|
||||
final LinearLayout devListLayout =
|
||||
mMetadataPreferenceView.findViewById(R.id.developer_list);
|
||||
populateDeveloperList(appUtil.getDevelopers(), devListLayout);
|
||||
|
||||
((TextView) mMetadataPreferenceView.findViewById(R.id.contains_ads))
|
||||
.setText(Boolean.toString(appUtil.getContainsAds()));
|
||||
|
||||
((TextView) mMetadataPreferenceView.findViewById(R.id.contact_url))
|
||||
.setText(appUtil.getContactUrl());
|
||||
((TextView) mMetadataPreferenceView.findViewById(R.id.contact_email))
|
||||
.setText(appUtil.getContactEmail());
|
||||
|
||||
((TextView) mMetadataPreferenceView.findViewById(R.id.privacy_policy_url))
|
||||
.setText(appUtil.getPrivacyPolicyUrl());
|
||||
|
||||
((TextView) mMetadataPreferenceView.findViewById(R.id.description))
|
||||
.setText(appUtil.getDescription());
|
||||
|
||||
((TextView) mMetadataPreferenceView.findViewById(R.id.category))
|
||||
.setText(appUtil.getCategoryName());
|
||||
}
|
||||
|
||||
private void populateDeveloperList(List<MbaDeveloper> developersDetails, ViewGroup parent) {
|
||||
for (MbaDeveloper dev : developersDetails) {
|
||||
View itemView = LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.mobile_bundled_apps_developer_fragment_row, parent, false);
|
||||
|
||||
((TextView) itemView.findViewById(R.id.developer_name)).setText(dev.name);
|
||||
((TextView) itemView.findViewById(R.id.developer_relationship))
|
||||
.setText(dev.relationship);
|
||||
((TextView) itemView.findViewById(R.id.developer_email)).setText(dev.email);
|
||||
((TextView) itemView.findViewById(R.id.developer_country)).setText(dev.country);
|
||||
|
||||
parent.addView(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AlertDialog createDialog(int id, int errorCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refreshUi() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.TRANSPARENCY_METADATA;
|
||||
}
|
||||
|
||||
}
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.applications.appinfo;
|
||||
|
||||
import static com.android.settings.applications.mobilebundledapps.MobileBundledAppDetailsActivity.ACTION_TRANSPARENCY_METADATA;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -41,11 +43,14 @@ import android.os.UserManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
@@ -53,7 +58,8 @@ import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AppInstallerInfoPreferenceControllerTest {
|
||||
|
||||
private static final String TEST_PACKAGE_NAME = "Package1";
|
||||
private static final String TEST_CONTEXT_KEY = "test_key";
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
@@ -67,11 +73,17 @@ public class AppInstallerInfoPreferenceControllerTest {
|
||||
@Mock
|
||||
private Preference mPreference;
|
||||
|
||||
@Mock
|
||||
private ApplicationMetadataUtils mApplicationMetadataUtils;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<Intent> mIntentArgumentCaptor;
|
||||
|
||||
private Context mContext;
|
||||
private AppInstallerInfoPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() throws PackageManager.NameNotFoundException {
|
||||
public void setup() throws PackageManager.NameNotFoundException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||
@@ -81,9 +93,13 @@ public class AppInstallerInfoPreferenceControllerTest {
|
||||
when(mInstallSourceInfo.getInstallingPackageName()).thenReturn(installerPackage);
|
||||
when(mPackageManager.getApplicationInfo(eq(installerPackage), anyInt()))
|
||||
.thenReturn(mAppInfo);
|
||||
mController = new AppInstallerInfoPreferenceController(mContext, "test_key");
|
||||
mController.setPackageName("Package1");
|
||||
mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
|
||||
mController.setParentFragment(mFragment);
|
||||
mController.setPackageName(TEST_PACKAGE_NAME);
|
||||
when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
|
||||
.thenReturn(false);
|
||||
mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
|
||||
mController.setEnableMbaFlag(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,8 +111,42 @@ public class AppInstallerInfoPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noAppLabel_shouldReturnDisabled() {
|
||||
public void getAvailabilityStatus_noAppLabel_andNotMbaWithMetadata_shouldReturnDisabled()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
when(mUserManager.isManagedProfile()).thenReturn(false);
|
||||
mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.DISABLED_FOR_USER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noAppLabel_andHaveMbaFile_shouldReturnAvailable()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
|
||||
mController.setPackageName(TEST_PACKAGE_NAME);
|
||||
mController.setParentFragment(mFragment);
|
||||
when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
|
||||
.thenReturn(true);
|
||||
mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
|
||||
mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.DISABLED_FOR_USER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noAppLabel_andMbaFeatureFlagDisabled_shouldReturnDisabled()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
mController.setEnableMbaFlag(false);
|
||||
when(mUserManager.isManagedProfile()).thenReturn(false);
|
||||
mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
|
||||
mController.setPackageName(TEST_PACKAGE_NAME);
|
||||
mController.setParentFragment(mFragment);
|
||||
when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
|
||||
.thenReturn(true);
|
||||
mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
|
||||
mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.DISABLED_FOR_USER);
|
||||
@@ -105,13 +155,12 @@ public class AppInstallerInfoPreferenceControllerTest {
|
||||
@Test
|
||||
public void getAvailabilityStatus_hasAppLabel_shouldReturnAvailable()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
final String packageName = "Package1";
|
||||
when(mUserManager.isManagedProfile()).thenReturn(false);
|
||||
when(mAppInfo.loadLabel(mPackageManager)).thenReturn("Label1");
|
||||
mController = new AppInstallerInfoPreferenceController(mContext, "test_key");
|
||||
mController.setPackageName(packageName);
|
||||
mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
|
||||
mController.setPackageName(TEST_PACKAGE_NAME);
|
||||
mController.setParentFragment(mFragment);
|
||||
mockMainlineModule(packageName, false /* isMainlineModule */);
|
||||
mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.AVAILABLE);
|
||||
@@ -129,7 +178,7 @@ public class AppInstallerInfoPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_noAppStoreLink_shouldDisablePreference() {
|
||||
public void updateState_noAppStoreLink_andNotMbaWithMetadata_shouldDisablePreference() {
|
||||
final PackageInfo packageInfo = mock(PackageInfo.class);
|
||||
packageInfo.applicationInfo = mAppInfo;
|
||||
when(mFragment.getPackageInfo()).thenReturn(packageInfo);
|
||||
@@ -139,6 +188,39 @@ public class AppInstallerInfoPreferenceControllerTest {
|
||||
|
||||
verify(mPreference).setEnabled(false);
|
||||
}
|
||||
@Test
|
||||
public void updateState_noAppStoreLink_andMbaFeatureFlagDisabled_shouldDisablePreference() {
|
||||
mController.setEnableMbaFlag(false);
|
||||
when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
|
||||
.thenReturn(true);
|
||||
mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
|
||||
final PackageInfo packageInfo = mock(PackageInfo.class);
|
||||
packageInfo.applicationInfo = mAppInfo;
|
||||
when(mFragment.getPackageInfo()).thenReturn(packageInfo);
|
||||
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(null);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setEnabled(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_noAppStoreLink_andMbaWithMetadata_shouldSetPreferenceIntent() {
|
||||
when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
|
||||
.thenReturn(true);
|
||||
mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
|
||||
final PackageInfo packageInfo = mock(PackageInfo.class);
|
||||
packageInfo.applicationInfo = mAppInfo;
|
||||
when(mFragment.getPackageInfo()).thenReturn(packageInfo);
|
||||
when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(null);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference, never()).setEnabled(false);
|
||||
verify(mPreference).setIntent(mIntentArgumentCaptor.capture());
|
||||
assertThat(mIntentArgumentCaptor.getValue().getAction())
|
||||
.isEqualTo(ACTION_TRANSPARENCY_METADATA);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_hasAppStoreLink_shouldSetPreferenceIntent() {
|
||||
@@ -154,7 +236,9 @@ public class AppInstallerInfoPreferenceControllerTest {
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference, never()).setEnabled(false);
|
||||
verify(mPreference).setIntent(any(Intent.class));
|
||||
verify(mPreference).setIntent(mIntentArgumentCaptor.capture());
|
||||
assertThat(mIntentArgumentCaptor.getValue().getAction())
|
||||
.isEqualTo(Intent.ACTION_SHOW_APP_INFO);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.mobilebundledapps;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.google.common.io.CharSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ApplicationMetadataUtilsTest {
|
||||
private static final String TEST_PACKAGE_NAME = "test";
|
||||
private static final String TEST_SOURCE_DIR = "sourcedir";
|
||||
|
||||
private static final String TEST_XML_SCHEMA = "<transparency-info>\n"
|
||||
+ " <template/>\n"
|
||||
+ " <contains-ads/>\n"
|
||||
+ " <developers>\n"
|
||||
+ " <developer name=\"Example ODM\" relationship=\"ODM\" email=\"odm@example.com\""
|
||||
+ " \n"
|
||||
+ " website=\"http://odm.example.com\" country=\"US\"/>\n"
|
||||
+ " <developer name=\"Example carrier\" relationship=\"CARRIER\" "
|
||||
+ "email=\"carrier@example.com\" \n"
|
||||
+ " country=\"US\"/>\n"
|
||||
+ " </developers>\n"
|
||||
+ " <contact url=\"http://example.com/contact-us\" email=\"contact@example.com\"/>\n"
|
||||
+ " <privacy-policy url=\"https://www.example.com/privacy-policy.html\"/>\n"
|
||||
+ " <description>This application provides the user with news "
|
||||
+ "headlines</description>\n"
|
||||
+ " <category name=\"News and magazines\"/>\n"
|
||||
+ "</transparency-info>";
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private Document mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
.parse(CharSource.wrap(TEST_XML_SCHEMA).asByteSource(StandardCharsets.UTF_8)
|
||||
.openStream());
|
||||
|
||||
|
||||
public ApplicationMetadataUtilsTest()
|
||||
throws IOException, ParserConfigurationException, SAXException {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
throws PackageManager.NameNotFoundException, IOException, ParserConfigurationException,
|
||||
SAXException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
final ApplicationInfo appInfo = new ApplicationInfo();
|
||||
appInfo.sourceDir = TEST_SOURCE_DIR;
|
||||
when(mPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME),
|
||||
any(PackageManager.ApplicationInfoFlags.class))).thenReturn(appInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDefaultInstance_alwaysReturnSameInstance() {
|
||||
final ApplicationMetadataUtils firstInstance =
|
||||
ApplicationMetadataUtils.getDefaultInstance();
|
||||
|
||||
assertThat(firstInstance).isEqualTo(ApplicationMetadataUtils.getDefaultInstance());
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void createInstance_bubblesUpException() throws PackageManager.NameNotFoundException {
|
||||
final String testErrorMsg = "test";
|
||||
when(mPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME),
|
||||
any(PackageManager.ApplicationInfoFlags.class)))
|
||||
.thenThrow(new Exception(testErrorMsg));
|
||||
|
||||
ApplicationMetadataUtils.newInstance(mPackageManager, TEST_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldGetters_toReturnNull_whenEmptyOrError() {
|
||||
final ApplicationMetadataUtils appUtils = new ApplicationMetadataUtils();
|
||||
assertThat(appUtils.getContainsAds()).isEqualTo(false);
|
||||
assertThat(appUtils.getCategoryName()).isNull();
|
||||
assertThat(appUtils.getPrivacyPolicyUrl()).isNull();
|
||||
assertThat(appUtils.getDescription()).isNull();
|
||||
assertThat(appUtils.getDevelopers()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldGetters_toReturnCorrectValues_whenExists() {
|
||||
final ApplicationMetadataUtils appUtils = new ApplicationMetadataUtils();
|
||||
appUtils.setXmlDoc(mDocument);
|
||||
assertThat(appUtils.getContainsAds()).isEqualTo(true);
|
||||
assertThat(appUtils.getCategoryName()).isEqualTo("News and magazines");
|
||||
assertThat(appUtils.getPrivacyPolicyUrl())
|
||||
.isEqualTo("https://www.example.com/privacy-policy.html");
|
||||
assertThat(appUtils.getDescription())
|
||||
.isEqualTo("This application provides the user with news headlines");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDevelopers_returnsCorrectValues() {
|
||||
final ApplicationMetadataUtils appUtils = new ApplicationMetadataUtils();
|
||||
appUtils.setXmlDoc(mDocument);
|
||||
final List<ApplicationMetadataUtils.MbaDeveloper> developers = appUtils.getDevelopers();
|
||||
|
||||
assertThat(developers.size()).isEqualTo(2);
|
||||
assertThat(developers.get(0).country).isEqualTo("US");
|
||||
assertThat(developers.get(0).email).isEqualTo("odm@example.com");
|
||||
assertThat(developers.get(0).name).isEqualTo("Example ODM");
|
||||
assertThat(developers.get(1).relationship).isEqualTo("CARRIER");
|
||||
assertThat(developers.get(1).country).isEqualTo("US");
|
||||
assertThat(developers.get(1).email).isEqualTo("carrier@example.com");
|
||||
assertThat(developers.get(1).name).isEqualTo("Example carrier");
|
||||
assertThat(developers.get(1).relationship).isEqualTo("CARRIER");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user