Snap for 4496165 from 2f7fd8c592 to pi-release

Change-Id: I7d053a387f04c5ab536b44bb8337a6bba5d8e5a7
This commit is contained in:
android-build-team Robot
2017-12-11 12:20:54 +00:00
72 changed files with 2209 additions and 1393 deletions

View File

@@ -20,13 +20,16 @@ LOCAL_USE_AAPT2 := true
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_ANDROID_LIBRARIES := \
android-slices-builders \
android-slices-core \
android-slices-view \
android-support-v4 \
android-support-v13 \
android-support-v7-appcompat \
android-support-v7-cardview \
android-support-v7-preference \
android-support-v7-recyclerview \
android-support-v14-preference
android-support-v14-preference \
LOCAL_JAVA_LIBRARIES := \
bouncycastle \
@@ -34,8 +37,10 @@ LOCAL_JAVA_LIBRARIES := \
ims-common
LOCAL_STATIC_JAVA_LIBRARIES := \
apptoolkit-arch-core-runtime \
apptoolkit-lifecycle-extensions \
jsr305 \
settings-logtags
settings-logtags \
LOCAL_PROGUARD_FLAG_FILES := proguard.flags

View File

@@ -27,6 +27,9 @@
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int, int);
}
# Keep annotated classes or class members.
-keep @android.support.annotation.Keep class *

View File

@@ -14,6 +14,6 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@*android:dimen/secondary_content_alpha_material_dark"
<item android:alpha="?android:attr/secondaryContentAlpha"
android:color="?android:attr/colorError"/>
</selector>

View File

@@ -105,6 +105,33 @@
android:title="@string/sms_application_title"
android:summary="@string/summary_placeholder" />
<!-- Advanced apps settings -->
<PreferenceCategory
android:key="advanced_app_info"
android:title="@string/advanced_apps">
<Preference
android:key="system_alert_window"
android:title="@string/draw_overlay"
android:summary="@string/summary_placeholder" />
<Preference
android:key="write_settings_apps"
android:title="@string/write_settings"
android:summary="@string/summary_placeholder" />
<Preference
android:key="picture_in_picture"
android:title="@string/picture_in_picture_app_detail_title"
android:summary="@string/summary_placeholder" />
<Preference
android:key="install_other_apps"
android:title="@string/install_other_apps"
android:summary="@string/summary_placeholder" />
</PreferenceCategory>
<Preference
android:key="app_version"
android:selectable="false"

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="draw_overlay_permission_detail_settings"
android:title="@string/draw_overlay">
<SwitchPreference
android:key="app_ops_settings_switch"
android:title="@string/permit_draw_overlay"/>
<Preference
android:key="app_ops_settings_preference"
android:title="@string/app_overlay_permission_preference"/>
<Preference
android:summary="@string/allow_overlay_description"
android:selectable="false"/>
</PreferenceScreen>

View File

@@ -15,7 +15,6 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/install_other_apps">
<com.android.settingslib.RestrictedSwitchPreference

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="picture_in_picture_permission_detail_settings"
android:title="@string/picture_in_picture_app_detail_title">
<SwitchPreference
android:key="app_ops_settings_switch"
android:title="@string/picture_in_picture_app_detail_switch"/>
<Preference
android:summary="@string/picture_in_picture_app_detail_summary"
android:selectable="false"/>
</PreferenceScreen>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="write_system_settings_permission_detail_settings"
android:title="@string/write_settings">
<SwitchPreference
android:key="app_ops_settings_switch"
android:title="@string/permit_write_settings"/>
<Preference
android:key="app_ops_settings_preference"
android:title="@string/write_settings_preference"/>
<Preference
android:summary="@string/write_settings_description"
android:selectable="false"/>
</PreferenceScreen>

View File

@@ -84,7 +84,7 @@ public class DeviceInfoSettings extends DashboardFragment implements Indexable {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Bundle arguments = getArguments();
if (FeatureFlagUtils.isEnabled(getContext(), DEVICE_INFO_V2) || true) {
if (FeatureFlagUtils.isEnabled(getContext(), DEVICE_INFO_V2)) {
// Do not override initial expand children count if we come from
// search (EXTRA_FRAGMENT_ARG_KEY is set) - we need to display every if entry point
// is search.
@@ -119,7 +119,7 @@ public class DeviceInfoSettings extends DashboardFragment implements Indexable {
@Override
protected int getPreferenceScreenResId() {
return FeatureFlagUtils.isEnabled(getContext(), DEVICE_INFO_V2) || true
return FeatureFlagUtils.isEnabled(getContext(), DEVICE_INFO_V2)
? R.xml.device_info_settings_v2 : R.xml.device_info_settings;
}
@@ -156,7 +156,7 @@ public class DeviceInfoSettings extends DashboardFragment implements Indexable {
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Activity activity, Fragment fragment, Lifecycle lifecycle) {
if (FeatureFlagUtils.isEnabled(context, DEVICE_INFO_V2) || true) {
if (FeatureFlagUtils.isEnabled(context, DEVICE_INFO_V2)) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
// Device name
@@ -220,7 +220,7 @@ public class DeviceInfoSettings extends DashboardFragment implements Indexable {
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = FeatureFlagUtils.isEnabled(context, DEVICE_INFO_V2) || true
sir.xmlResId = FeatureFlagUtils.isEnabled(context, DEVICE_INFO_V2)
? R.xml.device_info_settings_v2 : R.xml.device_info_settings;
return Arrays.asList(sir);
}

View File

@@ -1,292 +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;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
/**
* The utility class that generate a license html file from xml files.
* All the HTML snippets and logic are copied from build/make/tools/generate-notice-files.py.
*
* TODO: Remove duplicate codes once backward support ends.
*/
class LicenseHtmlGeneratorFromXml {
private static final String TAG = "LicenseHtmlGeneratorFromXml";
private static final String TAG_ROOT = "licenses";
private static final String TAG_FILE_NAME = "file-name";
private static final String TAG_FILE_CONTENT = "file-content";
private static final String ATTR_CONTENT_ID = "contentId";
private static final String HTML_HEAD_STRING =
"<html><head>\n" +
"<style type=\"text/css\">\n" +
"body { padding: 0; font-family: sans-serif; }\n" +
".same-license { background-color: #eeeeee;\n" +
" border-top: 20px solid white;\n" +
" padding: 10px; }\n" +
".label { font-weight: bold; }\n" +
".file-list { margin-left: 1em; color: blue; }\n" +
"</style>\n" +
"</head>" +
"<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n" +
"<div class=\"toc\">\n" +
"<ul>";
private static final String HTML_MIDDLE_STRING =
"</ul>\n" +
"</div><!-- table of contents -->\n" +
"<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">";
private static final String HTML_REAR_STRING =
"</table></body></html>";
private final List<File> mXmlFiles;
/*
* A map from a file name to a content id (MD5 sum of file content) for its license.
* For example, "/system/priv-app/TeleService/TeleService.apk" maps to
* "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
* of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
*/
private final Map<String, String> mFileNameToContentIdMap = new HashMap();
/*
* A map from a content id (MD5 sum of file content) to a license file content.
* For example, "9645f39e9db895a4aa6e02cb57294595" maps to the content string of
* packages/services/Telephony/MODULE_LICENSE_APACHE2. Here "9645f39e9db895a4aa6e02cb57294595"
* is a MD5 sum of the file content.
*/
private final Map<String, String> mContentIdToFileContentMap = new HashMap();
static class ContentIdAndFileNames {
final String mContentId;
final List<String> mFileNameList = new ArrayList();
ContentIdAndFileNames(String contentId) {
mContentId = contentId;
}
}
private LicenseHtmlGeneratorFromXml(List<File> xmlFiles) {
mXmlFiles = xmlFiles;
}
public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
return genertor.generateHtml(outputFile);
}
private boolean generateHtml(File outputFile) {
for (File xmlFile : mXmlFiles) {
parse(xmlFile);
}
if (mFileNameToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
return false;
}
PrintWriter writer = null;
try {
writer = new PrintWriter(outputFile);
generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
writer.flush();
writer.close();
return true;
} catch (FileNotFoundException | SecurityException e) {
Log.e(TAG, "Failed to generate " + outputFile, e);
if (writer != null) {
writer.close();
}
return false;
}
}
private void parse(File xmlFile) {
if (xmlFile == null || !xmlFile.exists() || xmlFile.length() == 0) {
return;
}
InputStreamReader in = null;
try {
if (xmlFile.getName().endsWith(".gz")) {
in = new InputStreamReader(new GZIPInputStream(new FileInputStream(xmlFile)));
} else {
in = new FileReader(xmlFile);
}
parse(in, mFileNameToContentIdMap, mContentIdToFileContentMap);
in.close();
} catch (XmlPullParserException | IOException e) {
Log.e(TAG, "Failed to parse " + xmlFile, e);
if (in != null) {
try {
in.close();
} catch (IOException ie) {
Log.w(TAG, "Failed to close " + xmlFile);
}
}
}
}
/*
* Parses an input stream and fills a map from a file name to a content id for its license
* and a map from a content id to a license file content.
*
* Following xml format is expected from the input stream.
*
* <licenses>
* <file-name contentId="content_id_of_license1">file1</file-name>
* <file-name contentId="content_id_of_license2">file2</file-name>
* ...
* <file-content contentId="content_id_of_license1">license1 file contents</file-content>
* <file-content contentId="content_id_of_license2">license2 file contents</file-content>
* ...
* </licenses>
*/
@VisibleForTesting
static void parse(InputStreamReader in, Map<String, String> outFileNameToContentIdMap,
Map<String, String> outContentIdToFileContentMap)
throws XmlPullParserException, IOException {
Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in);
parser.nextTag();
parser.require(XmlPullParser.START_TAG, "", TAG_ROOT);
int state = parser.getEventType();
while (state != XmlPullParser.END_DOCUMENT) {
if (state == XmlPullParser.START_TAG) {
if (TAG_FILE_NAME.equals(parser.getName())) {
String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
if (!TextUtils.isEmpty(contentId)) {
String fileName = readText(parser).trim();
if (!TextUtils.isEmpty(fileName)) {
fileNameToContentIdMap.put(fileName, contentId);
}
}
} else if (TAG_FILE_CONTENT.equals(parser.getName())) {
String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
if (!TextUtils.isEmpty(contentId) &&
!outContentIdToFileContentMap.containsKey(contentId) &&
!contentIdToFileContentMap.containsKey(contentId)) {
String fileContent = readText(parser);
if (!TextUtils.isEmpty(fileContent)) {
contentIdToFileContentMap.put(contentId, fileContent);
}
}
}
}
state = parser.next();
}
outFileNameToContentIdMap.putAll(fileNameToContentIdMap);
outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
}
private static String readText(XmlPullParser parser)
throws IOException, XmlPullParserException {
StringBuffer result = new StringBuffer();
int state = parser.next();
while (state == XmlPullParser.TEXT) {
result.append(parser.getText());
state = parser.next();
}
return result.toString();
}
@VisibleForTesting
static void generateHtml(Map<String, String> fileNameToContentIdMap,
Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
List<String> fileNameList = new ArrayList();
fileNameList.addAll(fileNameToContentIdMap.keySet());
Collections.sort(fileNameList);
writer.println(HTML_HEAD_STRING);
int count = 0;
Map<String, Integer> contentIdToOrderMap = new HashMap();
List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
// Prints all the file list with a link to its license file content.
for (String fileName : fileNameList) {
String contentId = fileNameToContentIdMap.get(fileName);
// Assigns an id to a newly referred license file content.
if (!contentIdToOrderMap.containsKey(contentId)) {
contentIdToOrderMap.put(contentId, count);
// An index in contentIdAndFileNamesList is the order of each element.
contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
count++;
}
int id = contentIdToOrderMap.get(contentId);
contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
}
writer.println(HTML_MIDDLE_STRING);
count = 0;
// Prints all contents of the license files in order of id.
for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", count);
writer.println("<div class=\"label\">Notices for file(s):</div>");
writer.println("<div class=\"file-list\">");
for (String fileName : contentIdAndFileNames.mFileNameList) {
writer.format("%s <br/>\n", fileName);
}
writer.println("</div><!-- file-list -->");
writer.println("<pre class=\"license-text\">");
writer.println(contentIdToFileContentMap.get(
contentIdAndFileNames.mContentId));
writer.println("</pre><!-- license-text -->");
writer.println("</td></tr><!-- same-license -->");
count++;
}
writer.println(HTML_REAR_STRING);
}
}

View File

@@ -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;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.android.settings.utils.AsyncLoader;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* LicenseHtmlLoader is a loader which loads a license html file from default license xml files.
*/
class LicenseHtmlLoader extends AsyncLoader<File> {
private static final String TAG = "LicenseHtmlLoader";
private static final String[] DEFAULT_LICENSE_XML_PATHS = {
"/system/etc/NOTICE.xml.gz",
"/vendor/etc/NOTICE.xml.gz",
"/odm/etc/NOTICE.xml.gz",
"/oem/etc/NOTICE.xml.gz"};
private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
private Context mContext;
public LicenseHtmlLoader(Context context) {
super(context);
mContext = context;
}
@Override
public File loadInBackground() {
return generateHtmlFromDefaultXmlFiles();
}
@Override
protected void onDiscardResult(File f) {
}
private File generateHtmlFromDefaultXmlFiles() {
final List<File> xmlFiles = getVaildXmlFiles();
if (xmlFiles.isEmpty()) {
Log.e(TAG, "No notice file exists.");
return null;
}
File cachedHtmlFile = getCachedHtmlFile();
if(!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile) ||
generateHtmlFile(xmlFiles, cachedHtmlFile)) {
return cachedHtmlFile;
}
return null;
}
@VisibleForTesting
List<File> getVaildXmlFiles() {
final List<File> xmlFiles = new ArrayList();
for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
File file = new File(xmlPath);
if (file.exists() && file.length() != 0) {
xmlFiles.add(file);
}
}
return xmlFiles;
}
@VisibleForTesting
File getCachedHtmlFile() {
return new File(mContext.getCacheDir(), NOTICE_HTML_FILE_NAME);
}
@VisibleForTesting
boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
boolean outdated = true;
if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
outdated = false;
for (File file : xmlFiles) {
if (cachedHtmlFile.lastModified() < file.lastModified()) {
outdated = true;
break;
}
}
}
return outdated;
}
@VisibleForTesting
boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
}
}

View File

@@ -20,12 +20,10 @@ import android.app.Activity;
import android.app.LoaderManager;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.net.Uri;
import android.os.Bundle;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.FileProvider;
@@ -34,10 +32,9 @@ import android.util.Log;
import android.widget.Toast;
import com.android.settings.users.RestrictedProfileSettings;
import com.android.settingslib.license.LicenseHtmlLoader;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* The "dialog" that shows from "License" in the Settings app.
@@ -111,9 +108,9 @@ public class SettingsLicenseActivity extends Activity implements
return;
}
showHtmlFromUri(Uri.fromFile(file));
}
}
private void showHtmlFromUri(Uri uri) {
private void showHtmlFromUri(Uri uri) {
// Kick off external viewer due to WebView security restrictions; we
// carefully point it at HTMLViewer, since it offers to decompress
// before viewing.

View File

@@ -18,7 +18,6 @@ package com.android.settings.applications;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.Manifest.permission;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
@@ -47,7 +46,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
@@ -58,7 +56,6 @@ import android.view.MenuItem;
import android.view.View;
import android.webkit.IWebViewUpdateService;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.DeviceAdminAdd;
import com.android.settings.R;
@@ -78,11 +75,10 @@ import com.android.settings.applications.appinfo.DefaultEmergencyShortcutPrefere
import com.android.settings.applications.appinfo.DefaultHomeShortcutPreferenceController;
import com.android.settings.applications.appinfo.DefaultPhoneShortcutPreferenceController;
import com.android.settings.applications.appinfo.DefaultSmsShortcutPreferenceController;
import com.android.settings.applications.defaultapps.DefaultBrowserPreferenceController;
import com.android.settings.applications.defaultapps.DefaultEmergencyPreferenceController;
import com.android.settings.applications.defaultapps.DefaultHomePreferenceController;
import com.android.settings.applications.defaultapps.DefaultPhonePreferenceController;
import com.android.settings.applications.defaultapps.DefaultSmsPreferenceController;
import com.android.settings.applications.appinfo.DrawOverlayDetailPreferenceController;
import com.android.settings.applications.appinfo.ExternalSourceDetailPreferenceController;
import com.android.settings.applications.appinfo.PictureInPictureDetailPreferenceController;
import com.android.settings.applications.appinfo.WriteSystemSettingsPreferenceController;
import com.android.settings.applications.instantapps.InstantAppButtonsController;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -90,6 +86,7 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.ActionButtonPreference;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settings.wrapper.DevicePolicyManagerWrapper;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.AppUtils;
@@ -97,7 +94,6 @@ import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -149,6 +145,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
public static final String ARG_PACKAGE_UID = "uid";
protected static final boolean localLOGV = false;
private static final String KEY_ADVANCED_APP_INFO_CATEGORY = "advanced_app_info";
private EnforcedAdmin mAppsControlDisallowedAdmin;
private boolean mAppsControlDisallowedBySystem;
@@ -358,11 +355,6 @@ public class AppInfoDashboardFragment extends DashboardFragment
if (!refreshUi()) {
setIntentAndFinish(true, true);
}
if (mFinishing) {
return;
}
updateDynamicPrefs();
}
@Override
@@ -404,6 +396,17 @@ public class AppInfoDashboardFragment extends DashboardFragment
controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName));
controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName));
final List<AbstractPreferenceController> advancedAppInfoControllers = new ArrayList<>();
advancedAppInfoControllers.add(new DrawOverlayDetailPreferenceController(context, this));
advancedAppInfoControllers.add(new WriteSystemSettingsPreferenceController(context, this));
advancedAppInfoControllers.add(
new PictureInPictureDetailPreferenceController(context, this, packageName));
advancedAppInfoControllers.add(
new ExternalSourceDetailPreferenceController(context, this, packageName));
controllers.addAll(advancedAppInfoControllers);
controllers.add(new PreferenceCategoryController(
context, KEY_ADVANCED_APP_INFO_CATEGORY, advancedAppInfoControllers));
return controllers;
}
@@ -415,6 +418,9 @@ public class AppInfoDashboardFragment extends DashboardFragment
}
public PackageInfo getPackageInfo() {
if (mAppEntry == null) {
retrieveAppEntry();
}
return mPackageInfo;
}
@@ -603,7 +609,8 @@ public class AppInfoDashboardFragment extends DashboardFragment
return false;
}
protected boolean refreshUi() {
@VisibleForTesting
boolean refreshUi() {
retrieveAppEntry();
if (mAppEntry == null) {
return false; // onCreate must have failed, make sure to exit
@@ -782,10 +789,6 @@ public class AppInfoDashboardFragment extends DashboardFragment
}
}
private void startAppInfoFragment(Class<?> fragment, int title) {
startAppInfoFragment(fragment, title, this, mAppEntry);
}
public static void startAppInfoFragment(Class<?> fragment, int title,
SettingsPreferenceFragment caller, AppEntry appEntry) {
// start new fragment to display extended information
@@ -871,100 +874,10 @@ public class AppInfoDashboardFragment extends DashboardFragment
if (UserManager.get(getContext()).isManagedProfile()) {
return;
}
final PreferenceScreen screen = getPreferenceScreen();
final Context context = getContext();
// Get the package info with the activities
PackageInfo packageInfoWithActivities = null;
try {
packageInfoWithActivities = mPm.getPackageInfoAsUser(mPackageName,
PackageManager.GET_ACTIVITIES, UserHandle.myUserId());
} catch (NameNotFoundException e) {
Log.e(TAG, "Exception while retrieving the package info of " + mPackageName, e);
}
boolean hasDrawOverOtherApps = hasPermission(permission.SYSTEM_ALERT_WINDOW);
boolean hasWriteSettings = hasPermission(permission.WRITE_SETTINGS);
boolean hasPictureInPictureActivities = (packageInfoWithActivities != null) &&
PictureInPictureSettings.checkPackageHasPictureInPictureActivities(
packageInfoWithActivities.packageName,
packageInfoWithActivities.activities);
boolean isPotentialAppSource = isPotentialAppSource();
if (hasDrawOverOtherApps || hasWriteSettings || hasPictureInPictureActivities ||
isPotentialAppSource) {
PreferenceCategory category = new PreferenceCategory(getPrefContext());
category.setTitle(R.string.advanced_apps);
screen.addPreference(category);
if (hasDrawOverOtherApps) {
Preference pref = new Preference(getPrefContext());
pref.setTitle(R.string.draw_overlay);
pref.setKey("system_alert_window");
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startAppInfoFragment(DrawOverlayDetails.class, R.string.draw_overlay);
return true;
}
});
category.addPreference(pref);
}
if (hasWriteSettings) {
Preference pref = new Preference(getPrefContext());
pref.setTitle(R.string.write_settings);
pref.setKey("write_settings_apps");
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startAppInfoFragment(WriteSettingsDetails.class, R.string.write_settings);
return true;
}
});
category.addPreference(pref);
}
if (hasPictureInPictureActivities) {
Preference pref = new Preference(getPrefContext());
pref.setTitle(R.string.picture_in_picture_app_detail_title);
pref.setKey("picture_in_picture");
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
AppInfoBase.startAppInfoFragment(PictureInPictureDetails.class,
R.string.picture_in_picture_app_detail_title, mPackageName,
mPackageInfo.applicationInfo.uid, AppInfoDashboardFragment.this,
-1, getMetricsCategory());
return true;
}
});
category.addPreference(pref);
}
if (isPotentialAppSource) {
Preference pref = new Preference(getPrefContext());
pref.setTitle(R.string.install_other_apps);
pref.setKey("install_other_apps");
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startAppInfoFragment(ExternalSourcesDetails.class,
R.string.install_other_apps);
return true;
}
});
category.addPreference(pref);
}
}
addAppInstallerInfoPref(screen);
addAppInstallerInfoPref(getPreferenceScreen());
maybeAddInstantAppButtons();
}
private boolean isPotentialAppSource() {
AppStateInstallAppsBridge.InstallAppsState appState =
new AppStateInstallAppsBridge(getContext(), null, null)
.createInstallAppsStateFor(mPackageName, mPackageInfo.applicationInfo.uid);
return appState.isPotentialAppSource();
}
private void addAppInstallerInfoPref(PreferenceScreen screen) {
String installerPackageName =
AppStoreUtil.getInstallerPackageName(getContext(), mPackageName);
@@ -1008,39 +921,6 @@ public class AppInfoDashboardFragment extends DashboardFragment
}
}
private boolean hasPermission(String permission) {
if (mPackageInfo == null || mPackageInfo.requestedPermissions == null) {
return false;
}
for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) {
if (mPackageInfo.requestedPermissions[i].equals(permission)) {
return true;
}
}
return false;
}
private void updateDynamicPrefs() {
final Context context = getContext();
Preference pref = findPreference("system_alert_window");
if (pref != null) {
pref.setSummary(DrawOverlayDetails.getSummary(getContext(), mAppEntry));
}
pref = findPreference("picture_in_picture");
if (pref != null) {
pref.setSummary(PictureInPictureDetails.getPreferenceSummary(getContext(),
mPackageInfo.applicationInfo.uid, mPackageName));
}
pref = findPreference("write_settings_apps");
if (pref != null) {
pref.setSummary(WriteSettingsDetails.getSummary(getContext(), mAppEntry));
}
pref = findPreference("install_other_apps");
if (pref != null) {
pref.setSummary(ExternalSourcesDetails.getPreferenceSummary(getContext(), mAppEntry));
}
}
private void onPackageRemoved() {
getActivity().finishActivity(SUB_INFO_FRAGMENT);
getActivity().finishAndRemoveTask();
@@ -1112,7 +992,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
return mPackageName;
}
final Bundle args = getArguments();
String mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
if (mPackageName == null) {
Intent intent = (args == null) ?
getActivity().getIntent() : (Intent) args.getParcelable("intent");
@@ -1229,7 +1109,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.DIALOG_APP_INFO_ACTION;
return MetricsEvent.DIALOG_APP_INFO_ACTION;
}
@Override

View File

@@ -90,7 +90,7 @@ public class AppStateInstallAppsBridge extends AppStateBaseBridge {
return mAppOpsManager.checkOpNoThrow(appOpCode, uid, packageName);
}
InstallAppsState createInstallAppsStateFor(String packageName, int uid) {
public InstallAppsState createInstallAppsStateFor(String packageName, int uid) {
final InstallAppsState appState = new InstallAppsState();
appState.permissionRequested = hasRequestedAppOpPermission(
Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName);

View File

@@ -24,9 +24,9 @@ import android.os.UserHandle;
import android.util.Log;
import com.android.internal.util.Preconditions;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
import com.android.settingslib.utils.AsyncLoader;
import java.io.IOException;

View File

@@ -75,6 +75,11 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.PictureInPictureDetails;
import com.android.settings.applications.appinfo.PictureInPictureSettings;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.applications.defaultapps.DefaultBrowserPreferenceController;
import com.android.settings.applications.defaultapps.DefaultEmergencyPreferenceController;
import com.android.settings.applications.defaultapps.DefaultHomePreferenceController;

View File

@@ -0,0 +1,70 @@
/*
* 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.appinfo;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppInfoDashboardFragment;
public class DrawOverlayDetailPreferenceController extends AppInfoPreferenceControllerBase {
private static final String KEY = "system_alert_window";
public DrawOverlayDetailPreferenceController(Context context, AppInfoDashboardFragment parent) {
super(context, parent, KEY);
}
@Override
public int getAvailabilityStatus() {
if (UserManager.get(mContext).isManagedProfile()) {
return DISABLED_FOR_USER;
}
final PackageInfo packageInfo = mParent.getPackageInfo();
if (packageInfo == null || packageInfo.requestedPermissions == null) {
return DISABLED_FOR_USER;
}
for (int i = 0; i < packageInfo.requestedPermissions.length; i++) {
if (packageInfo.requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW)) {
return AVAILABLE;
}
}
return DISABLED_FOR_USER;
}
@Override
public void updateState(Preference preference) {
preference.setSummary(getSummary());
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return DrawOverlayDetails.class;
}
@VisibleForTesting
CharSequence getSummary() {
return DrawOverlayDetails.getSummary(mContext, mParent.getAppEntry());
}
}

View File

@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
@@ -29,12 +31,13 @@ import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.applications.AppInfoWithHeader;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateOverlayBridge;
import com.android.settings.applications.AppStateOverlayBridge.OverlayState;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -44,7 +47,6 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
private static final String LOG_TAG = "DrawOverlayDetails";
private static final int [] APP_OPS_OP_CODE = {
@@ -57,7 +59,6 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc
private AppOpsManager mAppOpsManager;
private SwitchPreference mSwitchPref;
private Preference mOverlayPrefs;
private Preference mOverlayDesc;
private Intent mSettingsIntent;
private OverlayState mOverlayState;
@@ -70,16 +71,9 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
// find preferences
addPreferencesFromResource(R.xml.app_ops_permissions_details);
addPreferencesFromResource(R.xml.draw_overlay_permissions_details);
mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
mOverlayPrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
mOverlayDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
// set title/summary for all of them
getPreferenceScreen().setTitle(R.string.draw_overlay);
mSwitchPref.setTitle(R.string.permit_draw_overlay);
mOverlayPrefs.setTitle(R.string.app_overlay_permission_preference);
mOverlayDesc.setSummary(R.string.allow_overlay_description);
// install event listeners
mSwitchPref.setOnPreferenceChangeListener(this);
@@ -116,7 +110,8 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc
try {
getActivity().startActivityAsUser(mSettingsIntent, new UserHandle(mUserId));
} catch (ActivityNotFoundException e) {
Log.w(LOG_TAG, "Unable to launch app draw overlay settings " + mSettingsIntent, e);
Log.w(LOG_TAG, "Unable to launch app draw overlay settings " + mSettingsIntent,
e);
}
}
return true;
@@ -161,7 +156,14 @@ public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenc
// you cannot ask a user to grant you a permission you did not have!
mSwitchPref.setEnabled(mOverlayState.permissionDeclared && mOverlayState.controlEnabled);
mOverlayPrefs.setEnabled(isAllowed);
getPreferenceScreen().removePreference(mOverlayPrefs);
ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
PackageManager.GET_META_DATA, mUserId);
if (resolveInfo == null) {
if (findPreference(KEY_APP_OPS_SETTINGS_PREFS) != null) {
getPreferenceScreen().removePreference(mOverlayPrefs);
}
}
return true;
}

View File

@@ -0,0 +1,71 @@
/*
* 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.appinfo;
import android.content.Context;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settings.applications.AppStateInstallAppsBridge;
public class ExternalSourceDetailPreferenceController extends AppInfoPreferenceControllerBase {
private static final String KEY = "install_other_apps";
private final String mPackageName;
public ExternalSourceDetailPreferenceController(Context context,
AppInfoDashboardFragment parent, String packageName) {
super(context, parent, KEY);
mPackageName = packageName;
}
@Override
public int getAvailabilityStatus() {
if (UserManager.get(mContext).isManagedProfile()) {
return DISABLED_FOR_USER;
}
return isPotentialAppSource() ? AVAILABLE : DISABLED_FOR_USER;
}
@Override
public void updateState(Preference preference) {
preference.setSummary(getPreferenceSummary());
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return ExternalSourcesDetails.class;
}
@VisibleForTesting
CharSequence getPreferenceSummary() {
return ExternalSourcesDetails.getPreferenceSummary(mContext, mParent.getAppEntry());
}
@VisibleForTesting
boolean isPotentialAppSource() {
AppStateInstallAppsBridge.InstallAppsState appState =
new AppStateInstallAppsBridge(mContext, null, null).createInstallAppsStateFor(
mPackageName, mParent.getPackageInfo().applicationInfo.uid);
return appState.isPotentialAppSource();
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
@@ -30,6 +30,8 @@ import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.applications.AppInfoWithHeader;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.applications.ApplicationsState.AppEntry;

View File

@@ -0,0 +1,86 @@
/*
* 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.appinfo;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.util.Log;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppInfoDashboardFragment;
public class PictureInPictureDetailPreferenceController extends AppInfoPreferenceControllerBase {
private static final String KEY = "picture_in_picture";
private static final String TAG = "PicInPicDetailControl";
private final PackageManager mPackageManager;
private final String mPackageName;
public PictureInPictureDetailPreferenceController(Context context,
AppInfoDashboardFragment parent, String packageName) {
super(context, parent, KEY);
mPackageManager = context.getPackageManager();
mPackageName = packageName;
}
@Override
public int getAvailabilityStatus() {
if (UserManager.get(mContext).isManagedProfile()) {
return DISABLED_FOR_USER;
}
return hasPictureInPictureActivites() ? AVAILABLE : DISABLED_FOR_USER;
}
@Override
public void updateState(Preference preference) {
preference.setSummary(getPreferenceSummary());
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return PictureInPictureDetails.class;
}
@VisibleForTesting
boolean hasPictureInPictureActivites() {
// Get the package info with the activities
PackageInfo packageInfoWithActivities = null;
try {
packageInfoWithActivities = mPackageManager.getPackageInfoAsUser(mPackageName,
PackageManager.GET_ACTIVITIES, UserHandle.myUserId());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Exception while retrieving the package info of " + mPackageName, e);
}
return packageInfoWithActivities != null
&& PictureInPictureSettings.checkPackageHasPictureInPictureActivities(
packageInfoWithActivities.packageName,
packageInfoWithActivities.activities);
}
@VisibleForTesting
int getPreferenceSummary() {
return PictureInPictureDetails.getPreferenceSummary(mContext,
mParent.getPackageInfo().applicationInfo.uid, mPackageName);
}
}

View File

@@ -13,14 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
@@ -28,6 +26,7 @@ import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.applications.AppInfoWithHeader;
import com.android.settings.overlay.FeatureFactory;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -38,42 +37,31 @@ public class PictureInPictureDetails extends AppInfoWithHeader
implements OnPreferenceChangeListener {
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
private static final String LOG_TAG = "PictureInPictureDetails";
private SwitchPreference mSwitchPref;
private Preference mOverlayDesc;
private Intent mSettingsIntent;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// find preferences
addPreferencesFromResource(R.xml.app_ops_permissions_details);
addPreferencesFromResource(R.xml.picture_in_picture_permissions_details);
mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
mOverlayDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
getPreferenceScreen().removePreference(findPreference(KEY_APP_OPS_SETTINGS_PREFS));
// set title/summary for all of them
getPreferenceScreen().setTitle(R.string.picture_in_picture_app_detail_title);
mSwitchPref.setTitle(R.string.picture_in_picture_app_detail_switch);
mOverlayDesc.setSummary(R.string.picture_in_picture_app_detail_summary);
// install event listeners
mSwitchPref.setOnPreferenceChangeListener(this);
mSettingsIntent = new Intent(Intent.ACTION_MAIN)
.setAction(Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mSwitchPref) {
logSpecialPermissionChange((Boolean) newValue, mPackageName);
setEnterPipStateForPackage(getActivity(), mPackageInfo.applicationInfo.uid, mPackageName,
(Boolean) newValue);
setEnterPipStateForPackage(getActivity(), mPackageInfo.applicationInfo.uid,
mPackageName, (Boolean) newValue);
return true;
}
return false;
@@ -121,7 +109,7 @@ public class PictureInPictureDetails extends AppInfoWithHeader
* @return the summary for the current state of whether the app associated with the given
* {@param packageName} is allowed to enter picture-in-picture.
*/
static int getPreferenceSummary(Context context, int uid, String packageName) {
public static int getPreferenceSummary(Context context, int uid, String packageName) {
final boolean enabled = PictureInPictureDetails.getEnterPipStateForPackage(context, uid,
packageName);
return enabled ? R.string.app_permission_summary_allowed

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import static android.content.pm.PackageManager.GET_ACTIVITIES;
@@ -37,6 +37,7 @@ import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.notification.EmptyTextSettings;
import com.android.settings.widget.AppPreference;
import com.android.settings.wrapper.ActivityInfoWrapper;
@@ -95,7 +96,7 @@ public class PictureInPictureSettings extends EmptyTextSettings {
* @return true if the package has any activities that declare that they support
* picture-in-picture.
*/
static boolean checkPackageHasPictureInPictureActivities(String packageName,
public static boolean checkPackageHasPictureInPictureActivities(String packageName,
ActivityInfo[] activities) {
ActivityInfoWrapper[] wrappedActivities = null;
if (activities != null) {

View File

@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
@@ -31,7 +33,9 @@ import android.util.Log;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.applications.AppInfoWithHeader;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateWriteSettingsBridge;
import com.android.settings.applications.AppStateWriteSettingsBridge.WriteSettingsState;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -42,7 +46,6 @@ public class WriteSettingsDetails extends AppInfoWithHeader implements OnPrefere
private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
private static final String LOG_TAG = "WriteSettingsDetails";
private static final int [] APP_OPS_OP_CODE = {
@@ -55,7 +58,6 @@ public class WriteSettingsDetails extends AppInfoWithHeader implements OnPrefere
private AppOpsManager mAppOpsManager;
private SwitchPreference mSwitchPref;
private Preference mWriteSettingsPrefs;
private Preference mWriteSettingsDesc;
private Intent mSettingsIntent;
private WriteSettingsState mWriteSettingsState;
@@ -67,15 +69,9 @@ public class WriteSettingsDetails extends AppInfoWithHeader implements OnPrefere
mAppBridge = new AppStateWriteSettingsBridge(context, mState, null);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
addPreferencesFromResource(R.xml.app_ops_permissions_details);
addPreferencesFromResource(R.xml.write_system_settings_permissions_details);
mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
mWriteSettingsPrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
mWriteSettingsDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
getPreferenceScreen().setTitle(R.string.write_settings);
mSwitchPref.setTitle(R.string.permit_write_settings);
mWriteSettingsPrefs.setTitle(R.string.write_settings_preference);
mWriteSettingsDesc.setSummary(R.string.write_settings_description);
mSwitchPref.setOnPreferenceChangeListener(this);
mWriteSettingsPrefs.setOnPreferenceClickListener(this);
@@ -147,8 +143,13 @@ public class WriteSettingsDetails extends AppInfoWithHeader implements OnPrefere
// you can't ask a user for a permission you didn't even declare!
mSwitchPref.setEnabled(mWriteSettingsState.permissionDeclared);
mWriteSettingsPrefs.setEnabled(canWrite);
if (getPreferenceScreen().findPreference(KEY_APP_OPS_SETTINGS_PREFS) != null) {
getPreferenceScreen().removePreference(mWriteSettingsPrefs);
ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
PackageManager.GET_META_DATA, mUserId);
if (resolveInfo == null) {
if (getPreferenceScreen().findPreference(KEY_APP_OPS_SETTINGS_PREFS) != null) {
getPreferenceScreen().removePreference(mWriteSettingsPrefs);
}
}
return true;
}

View File

@@ -0,0 +1,71 @@
/*
* 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.appinfo;
import static android.Manifest.permission.WRITE_SETTINGS;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppInfoDashboardFragment;
public class WriteSystemSettingsPreferenceController extends AppInfoPreferenceControllerBase {
private static final String KEY = "write_settings_apps";
public WriteSystemSettingsPreferenceController(Context context,
AppInfoDashboardFragment parent) {
super(context, parent, KEY);
}
@Override
public int getAvailabilityStatus() {
if (UserManager.get(mContext).isManagedProfile()) {
return DISABLED_FOR_USER;
}
final PackageInfo packageInfo = mParent.getPackageInfo();
if (packageInfo == null || packageInfo.requestedPermissions == null) {
return DISABLED_FOR_USER;
}
for (int i = 0; i < packageInfo.requestedPermissions.length; i++) {
if (packageInfo.requestedPermissions[i].equals(WRITE_SETTINGS)) {
return AVAILABLE;
}
}
return DISABLED_FOR_USER;
}
@Override
public void updateState(Preference preference) {
preference.setSummary(getSummary());
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return WriteSettingsDetails.class;
}
@VisibleForTesting
CharSequence getSummary() {
return WriteSettingsDetails.getSummary(mContext, mParent.getAppEntry());
}
}

View File

@@ -89,14 +89,14 @@ import com.android.settings.applications.AppStateUsageBridge.UsageState;
import com.android.settings.applications.AppStateWriteSettingsBridge;
import com.android.settings.applications.AppStorageSettings;
import com.android.settings.applications.DefaultAppSettings;
import com.android.settings.applications.DrawOverlayDetails;
import com.android.settings.applications.ExternalSourcesDetails;
import com.android.settings.applications.InstalledAppCounter;
import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.applications.NotificationApps;
import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.WriteSettingsDetails;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.dashboard.SummaryLoader;

View File

@@ -26,4 +26,5 @@ public class FeatureFlags {
public static final String APP_INFO_V2 = "settings_app_info_v2";
public static final String CONNECTED_DEVICE_V2 = "settings_connected_device_v2";
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
}

View File

@@ -40,19 +40,19 @@ import com.android.settings.accounts.ManagedProfileSettings;
import com.android.settings.accounts.UserAndAccountDashboardFragment;
import com.android.settings.applications.AppAndNotificationDashboardFragment;
import com.android.settings.applications.DefaultAppSettings;
import com.android.settings.applications.DrawOverlayDetails;
import com.android.settings.applications.ExternalSourcesDetails;
import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.applications.ManageDomainUrls;
import com.android.settings.applications.NotificationApps;
import com.android.settings.applications.PictureInPictureDetails;
import com.android.settings.applications.PictureInPictureSettings;
import com.android.settings.applications.ProcessStatsSummary;
import com.android.settings.applications.ProcessStatsUi;
import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.VrListenerSettings;
import com.android.settings.applications.WriteSettingsDetails;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.PictureInPictureDetails;
import com.android.settings.applications.appinfo.PictureInPictureSettings;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.applications.assist.ManageAssist;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;

View File

@@ -92,7 +92,7 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
}
private static boolean isV2Enabled(Context context) {
return FeatureFlagUtils.isEnabled(context, SUGGESTIONS_V2) || true;
return FeatureFlagUtils.isEnabled(context, SUGGESTIONS_V2);
}
@Override

View File

@@ -20,7 +20,7 @@ import android.content.Context;
import android.service.settings.suggestions.Suggestion;
import android.util.Log;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
import java.util.List;

View File

@@ -21,7 +21,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.v7.preference.Preference;
import android.util.ArraySet;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
public class AppPrefLoader extends AsyncLoader<ArraySet<Preference>> {
private ArraySet<String> mPackages;

View File

@@ -17,7 +17,6 @@
package com.android.settings.development.featureflags;
import android.content.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.FeatureFlagUtils;
@@ -68,14 +67,8 @@ public class FeatureFlagsPreferenceController extends AbstractPreferenceControll
}
mScreen.removeAll();
final Context prefContext = mScreen.getContext();
for (String prefixedFeature : featureMap.keySet()) {
if (prefixedFeature.startsWith(FeatureFlagUtils.FFLAG_PREFIX)
&& !prefixedFeature.startsWith(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX)) {
final String feature = prefixedFeature.substring(
FeatureFlagUtils.FFLAG_PREFIX.length());
final Preference pref = new FeatureFlagPreference(prefContext, feature);
mScreen.addPreference(pref);
}
for (String feature : featureMap.keySet()) {
mScreen.addPreference(new FeatureFlagPreference(prefContext, feature));
}
}
}

View File

@@ -51,7 +51,7 @@ public class DeviceModelPreferenceController extends AbstractPreferenceControlle
super.displayPreference(screen);
final Preference pref = screen.findPreference(KEY_DEVICE_MODEL);
if (pref != null) {
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.DEVICE_INFO_V2) || true) {
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.DEVICE_INFO_V2)) {
pref.setSummary(mContext.getResources().getString(R.string.model_summary,
getDeviceModel()));
} else {

View File

@@ -59,7 +59,7 @@ public class HardwareInfoDialogFragment extends InstrumentedDialogFragment {
DeviceModelPreferenceController.getDeviceModel());
// Serial number
if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.DEVICE_INFO_V2) || true) {
if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.DEVICE_INFO_V2)) {
setText(content, R.id.serial_number_label, R.id.serial_number_value, getSerialNumber());
} else {
content.findViewById(R.id.serial_number_label).setVisibility(View.GONE);

View File

@@ -30,9 +30,9 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import com.android.settings.utils.AsyncLoader;
import com.android.settings.wrapper.UserManagerWrapper;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.utils.AsyncLoader;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.io.IOException;

View File

@@ -25,7 +25,7 @@ import android.util.SparseArray;
import com.android.internal.util.Preconditions;
import com.android.settings.Utils;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
/**
* Fetches a user icon as a loader using a given icon loading lambda.

View File

@@ -21,9 +21,9 @@ import android.content.Context;
import android.os.storage.VolumeInfo;
import android.support.annotation.VisibleForTesting;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import com.android.settingslib.utils.AsyncLoader;
import java.io.IOException;

View File

@@ -0,0 +1,484 @@
/*
* 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.fuelgauge;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.PowerProfile;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnPause;
import java.util.ArrayList;
import java.util.List;
/**
* Controller that update the battery header view
*/
public class BatteryAppListPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy {
private static final boolean USE_FAKE_DATA = true;
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final int STATS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
private final String mPreferenceKey;
@VisibleForTesting
PreferenceGroup mAppListGroup;
private BatteryStatsHelper mBatteryStatsHelper;
private ArrayMap<String, Preference> mPreferenceCache;
@VisibleForTesting
BatteryUtils mBatteryUtils;
private UserManager mUserManager;
private SettingsActivity mActivity;
private PreferenceFragment mFragment;
private Context mPrefContext;
SparseArray<List<Anomaly>> mAnomalySparseArray;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BatteryEntry.MSG_UPDATE_NAME_ICON:
BatteryEntry entry = (BatteryEntry) msg.obj;
PowerGaugePreference pgp =
(PowerGaugePreference) mAppListGroup.findPreference(
Integer.toString(entry.sipper.uidObj.getUid()));
if (pgp != null) {
final int userId = UserHandle.getUserId(entry.sipper.getUid());
final UserHandle userHandle = new UserHandle(userId);
pgp.setIcon(mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle));
pgp.setTitle(entry.name);
if (entry.sipper.drainType == DrainType.APP) {
pgp.setContentDescription(entry.name);
}
}
break;
case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
Activity activity = mActivity;
if (activity != null) {
activity.reportFullyDrawn();
}
break;
}
super.handleMessage(msg);
}
};
public BatteryAppListPreferenceController(Context context, String preferenceKey,
Lifecycle lifecycle, SettingsActivity activity, PreferenceFragment fragment) {
super(context);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mPreferenceKey = preferenceKey;
mBatteryUtils = BatteryUtils.getInstance(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mActivity = activity;
mFragment = fragment;
}
@Override
public void onPause() {
BatteryEntry.stopRequestQueue();
mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
}
@Override
public void onDestroy() {
if (mActivity.isChangingConfigurations()) {
BatteryEntry.clearUidCache();
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPrefContext = screen.getContext();
mAppListGroup = (PreferenceGroup) screen.findPreference(mPreferenceKey);
}
@Override
public boolean isAvailable() {
return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST);
}
@Override
public String getPreferenceKey() {
return mPreferenceKey;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (preference instanceof PowerGaugePreference) {
PowerGaugePreference pgp = (PowerGaugePreference) preference;
BatteryEntry entry = pgp.getInfo();
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity,
mFragment, mBatteryStatsHelper, STATS_TYPE, entry, pgp.getPercent(),
mAnomalySparseArray != null ? mAnomalySparseArray.get(entry.sipper.getUid())
: null);
return true;
}
return false;
}
public void refreshAnomalyIcon(final SparseArray<List<Anomaly>> anomalySparseArray) {
if (!isAvailable()) {
return;
}
mAnomalySparseArray = anomalySparseArray;
for (int i = 0, size = anomalySparseArray.size(); i < size; i++) {
final String key = extractKeyFromUid(anomalySparseArray.keyAt(i));
final PowerGaugePreference pref = (PowerGaugePreference) mAppListGroup.findPreference(
key);
if (pref != null) {
pref.shouldShowAnomalyIcon(true);
}
}
}
public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps,
CharSequence timeSequence) {
if (!isAvailable()) {
return;
}
mBatteryStatsHelper = statsHelper;
final int resId = showAllApps ? R.string.power_usage_list_summary_device
: R.string.power_usage_list_summary;
mAppListGroup.setTitle(TextUtils.expandTemplate(mContext.getText(resId), timeSequence));
final PowerProfile powerProfile = statsHelper.getPowerProfile();
final BatteryStats stats = statsHelper.getStats();
final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
boolean addedSome = false;
final int dischargeAmount = USE_FAKE_DATA ? 5000
: stats != null ? stats.getDischargeAmount(STATS_TYPE) : 0;
cacheRemoveAllPrefs(mAppListGroup);
mAppListGroup.setOrderingAsAdded(false);
if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
final List<BatterySipper> usageList = getCoalescedUsageList(
USE_FAKE_DATA ? getFakeStats() : statsHelper.getUsageList());
double hiddenPowerMah = showAllApps ? 0 :
mBatteryUtils.removeHiddenBatterySippers(usageList);
mBatteryUtils.sortUsageList(usageList);
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
double totalPower = USE_FAKE_DATA ? 4000 : statsHelper.getTotalPower();
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
if (shouldHideSipper(sipper)) {
continue;
}
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
final BatteryEntry entry = new BatteryEntry(mActivity, mHandler, mUserManager,
sipper);
final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(),
userHandle);
final CharSequence contentDescription = mUserManager.getBadgedLabelForUser(
entry.getLabel(),
userHandle);
final String key = extractKeyFromSipper(sipper);
PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
if (pref == null) {
pref = new PowerGaugePreference(mPrefContext, badgedIcon,
contentDescription, entry);
pref.setKey(key);
}
sipper.percent = percentOfTotal;
pref.setTitle(entry.getLabel());
pref.setOrder(i + 1);
pref.setPercent(percentOfTotal);
pref.shouldShowAnomalyIcon(false);
if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATS_TYPE);
}
setUsageSummary(pref, sipper);
addedSome = true;
mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() - getCachedCount()
> (MAX_ITEMS_TO_LIST + 1)) {
break;
}
}
}
if (!addedSome) {
addNotAvailableMessage();
}
removeCachedPrefs(mAppListGroup);
BatteryEntry.startRequestQueue();
}
/**
* We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
* exists for all users of the same app. We detect this case and merge the power use
* for dex2oat to the device OWNER's use of the app.
*
* @return A sorted list of apps using power.
*/
private List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
final SparseArray<BatterySipper> uidList = new SparseArray<>();
final ArrayList<BatterySipper> results = new ArrayList<>();
final int numSippers = sippers.size();
for (int i = 0; i < numSippers; i++) {
BatterySipper sipper = sippers.get(i);
if (sipper.getUid() > 0) {
int realUid = sipper.getUid();
// Check if this UID is a shared GID. If so, we combine it with the OWNER's
// actual app UID.
if (isSharedGid(sipper.getUid())) {
realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
UserHandle.getAppIdFromSharedAppGid(sipper.getUid()));
}
// Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
if (isSystemUid(realUid)
&& !"mediaserver".equals(sipper.packageWithHighestDrain)) {
// Use the system UID for all UIDs running in their own sandbox that
// are not apps. We exclude mediaserver because we already are expected to
// report that as a separate item.
realUid = Process.SYSTEM_UID;
}
if (realUid != sipper.getUid()) {
// Replace the BatterySipper with a new one with the real UID set.
BatterySipper newSipper = new BatterySipper(sipper.drainType,
new FakeUid(realUid), 0.0);
newSipper.add(sipper);
newSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
newSipper.mPackages = sipper.mPackages;
sipper = newSipper;
}
int index = uidList.indexOfKey(realUid);
if (index < 0) {
// New entry.
uidList.put(realUid, sipper);
} else {
// Combine BatterySippers if we already have one with this UID.
final BatterySipper existingSipper = uidList.valueAt(index);
existingSipper.add(sipper);
if (existingSipper.packageWithHighestDrain == null
&& sipper.packageWithHighestDrain != null) {
existingSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
}
final int existingPackageLen = existingSipper.mPackages != null ?
existingSipper.mPackages.length : 0;
final int newPackageLen = sipper.mPackages != null ?
sipper.mPackages.length : 0;
if (newPackageLen > 0) {
String[] newPackages = new String[existingPackageLen + newPackageLen];
if (existingPackageLen > 0) {
System.arraycopy(existingSipper.mPackages, 0, newPackages, 0,
existingPackageLen);
}
System.arraycopy(sipper.mPackages, 0, newPackages, existingPackageLen,
newPackageLen);
existingSipper.mPackages = newPackages;
}
}
} else {
results.add(sipper);
}
}
final int numUidSippers = uidList.size();
for (int i = 0; i < numUidSippers; i++) {
results.add(uidList.valueAt(i));
}
// The sort order must have changed, so re-sort based on total power use.
mBatteryUtils.sortUsageList(results);
return results;
}
@VisibleForTesting
void setUsageSummary(Preference preference, BatterySipper sipper) {
// Only show summary when usage time is longer than one minute
final long usageTimeMs = sipper.usageTimeMs;
if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
final CharSequence timeSequence = Utils.formatElapsedTime(mContext, usageTimeMs,
false);
preference.setSummary(
(sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
? timeSequence
: TextUtils.expandTemplate(mContext.getText(R.string.battery_used_for),
timeSequence));
}
}
@VisibleForTesting
boolean shouldHideSipper(BatterySipper sipper) {
// Don't show over-counted and unaccounted in any condition
return sipper.drainType == BatterySipper.DrainType.OVERCOUNTED
|| sipper.drainType == BatterySipper.DrainType.UNACCOUNTED;
}
@VisibleForTesting
String extractKeyFromSipper(BatterySipper sipper) {
if (sipper.uidObj != null) {
return extractKeyFromUid(sipper.getUid());
} else if (sipper.drainType == DrainType.USER) {
return sipper.drainType.toString() + sipper.userId;
} else if (sipper.drainType != DrainType.APP) {
return sipper.drainType.toString();
} else if (sipper.getPackages() != null) {
return TextUtils.concat(sipper.getPackages()).toString();
} else {
Log.w(TAG, "Inappropriate BatterySipper without uid and package names: " + sipper);
return "-1";
}
}
@VisibleForTesting
String extractKeyFromUid(int uid) {
return Integer.toString(uid);
}
private void cacheRemoveAllPrefs(PreferenceGroup group) {
mPreferenceCache = new ArrayMap<>();
final int N = group.getPreferenceCount();
for (int i = 0; i < N; i++) {
Preference p = group.getPreference(i);
if (TextUtils.isEmpty(p.getKey())) {
continue;
}
mPreferenceCache.put(p.getKey(), p);
}
}
private static boolean isSharedGid(int uid) {
return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
}
private static boolean isSystemUid(int uid) {
final int appUid = UserHandle.getAppId(uid);
return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
}
private static List<BatterySipper> getFakeStats() {
ArrayList<BatterySipper> stats = new ArrayList<>();
float use = 5;
for (DrainType type : DrainType.values()) {
if (type == DrainType.APP) {
continue;
}
stats.add(new BatterySipper(type, null, use));
use += 5;
}
for (int i = 0; i < 100; i++) {
stats.add(new BatterySipper(DrainType.APP,
new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
}
stats.add(new BatterySipper(DrainType.APP,
new FakeUid(0), use));
// Simulate dex2oat process.
BatterySipper sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
stats.add(sipper);
return stats;
}
private Preference getCachedPreference(String key) {
return mPreferenceCache != null ? mPreferenceCache.remove(key) : null;
}
private void removeCachedPrefs(PreferenceGroup group) {
for (Preference p : mPreferenceCache.values()) {
group.removePreference(p);
}
mPreferenceCache = null;
}
private int getCachedCount() {
return mPreferenceCache != null ? mPreferenceCache.size() : 0;
}
private void addNotAvailableMessage() {
final String NOT_AVAILABLE = "not_available";
Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
if (notAvailable == null) {
notAvailable = new Preference(mPrefContext);
notAvailable.setKey(NOT_AVAILABLE);
notAvailable.setTitle(R.string.power_usage_not_available);
mAppListGroup.addPreference(notAvailable);
}
}
}

View File

@@ -25,7 +25,7 @@ import android.os.BatteryStats;
import android.os.SystemClock;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
/**
* Loader that can be used by classes to load BatteryInfo in a background thread. This loader will

View File

@@ -23,7 +23,7 @@ import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
/**
* Loader to get new {@link BatteryStatsHelper} in the background

View File

@@ -22,7 +22,7 @@ import android.os.BatteryStats;
import android.os.SystemClock;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
import java.util.ArrayList;
import java.util.List;

View File

@@ -26,7 +26,7 @@ import android.view.Menu;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
/**
* Common base class for things that need to show the battery usage graph.

View File

@@ -21,21 +21,13 @@ import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Loader;
import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
import android.provider.SearchIndexableResource;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
@@ -49,7 +41,6 @@ import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.PowerProfile;
import com.android.settings.R;
import com.android.settings.Settings.HighPowerApplicationsActivity;
import com.android.settings.SettingsActivity;
@@ -71,6 +62,7 @@ import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.Arrays;
@@ -86,12 +78,9 @@ public class PowerUsageSummary extends PowerUsageBase implements
static final String TAG = "PowerUsageSummary";
private static final boolean DEBUG = false;
private static final boolean USE_FAKE_DATA = false;
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header";
private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final String KEY_SCREEN_USAGE = "screen_usage";
private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge";
@@ -136,6 +125,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
PreferenceGroup mAppListGroup;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
@@ -157,7 +147,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
updateAnomalySparseArray(data);
refreshAnomalyIcon();
mBatteryAppListPreferenceController.refreshAnomalyIcon(mAnomalySparseArray);
}
@Override
@@ -235,7 +225,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
initFeatureProvider();
mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER);
mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
mScreenUsagePref = (PowerGaugePreference) findPreference(KEY_SCREEN_USAGE);
mLastFullChargePref = (PowerGaugePreference) findPreference(
KEY_TIME_SINCE_LAST_FULL_CHARGE);
@@ -254,21 +243,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
return MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY_V2;
}
@Override
public void onPause() {
BatteryEntry.stopRequestQueue();
mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
if (getActivity().isChangingConfigurations()) {
BatteryEntry.clearUidCache();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -283,14 +257,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
if (KEY_BATTERY_HEADER.equals(preference.getKey())) {
performBatteryHeaderClick();
return true;
} else if (!(preference instanceof PowerGaugePreference)) {
return super.onPreferenceTreeClick(preference);
}
PowerGaugePreference pgp = (PowerGaugePreference) preference;
BatteryEntry entry = pgp.getInfo();
AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
this, mStatsHelper, mStatsType, entry, pgp.getPercent(),
mAnomalySparseArray.get(entry.sipper.getUid()));
return super.onPreferenceTreeClick(preference);
}
@@ -306,10 +273,15 @@ public class PowerUsageSummary extends PowerUsageBase implements
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
final Lifecycle lifecycle = getLifecycle();
final SettingsActivity activity = (SettingsActivity) getActivity();
final List<AbstractPreferenceController> controllers = new ArrayList<>();
mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController(
context, getActivity(), this /* host */, getLifecycle());
context, activity, this /* host */, getLifecycle());
controllers.add(mBatteryHeaderPreferenceController);
mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
KEY_APP_LIST, lifecycle, activity, this);
controllers.add(mBatteryAppListPreferenceController);
controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS));
controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT));
controllers.add(new BatterySaverController(context, getLifecycle()));
@@ -388,17 +360,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
}
}
private void addNotAvailableMessage() {
final String NOT_AVAILABLE = "not_available";
Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
if (notAvailable == null) {
notAvailable = new Preference(getPrefContext());
notAvailable.setKey(NOT_AVAILABLE);
notAvailable.setTitle(R.string.power_usage_not_available);
mAppListGroup.addPreference(notAvailable);
}
}
private void performBatteryHeaderClick() {
if (mPowerFeatureProvider.isAdvancedUiEnabled()) {
Utils.startWithFragment(getContext(), PowerUsageAdvanced.class.getName(), null,
@@ -415,101 +376,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
}
}
private static boolean isSharedGid(int uid) {
return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
}
private static boolean isSystemUid(int uid) {
final int appUid = UserHandle.getAppId(uid);
return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
}
/**
* We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
* exists for all users of the same app. We detect this case and merge the power use
* for dex2oat to the device OWNER's use of the app.
*
* @return A sorted list of apps using power.
*/
private List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
final SparseArray<BatterySipper> uidList = new SparseArray<>();
final ArrayList<BatterySipper> results = new ArrayList<>();
final int numSippers = sippers.size();
for (int i = 0; i < numSippers; i++) {
BatterySipper sipper = sippers.get(i);
if (sipper.getUid() > 0) {
int realUid = sipper.getUid();
// Check if this UID is a shared GID. If so, we combine it with the OWNER's
// actual app UID.
if (isSharedGid(sipper.getUid())) {
realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
UserHandle.getAppIdFromSharedAppGid(sipper.getUid()));
}
// Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
if (isSystemUid(realUid)
&& !"mediaserver".equals(sipper.packageWithHighestDrain)) {
// Use the system UID for all UIDs running in their own sandbox that
// are not apps. We exclude mediaserver because we already are expected to
// report that as a separate item.
realUid = Process.SYSTEM_UID;
}
if (realUid != sipper.getUid()) {
// Replace the BatterySipper with a new one with the real UID set.
BatterySipper newSipper = new BatterySipper(sipper.drainType,
new FakeUid(realUid), 0.0);
newSipper.add(sipper);
newSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
newSipper.mPackages = sipper.mPackages;
sipper = newSipper;
}
int index = uidList.indexOfKey(realUid);
if (index < 0) {
// New entry.
uidList.put(realUid, sipper);
} else {
// Combine BatterySippers if we already have one with this UID.
final BatterySipper existingSipper = uidList.valueAt(index);
existingSipper.add(sipper);
if (existingSipper.packageWithHighestDrain == null
&& sipper.packageWithHighestDrain != null) {
existingSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
}
final int existingPackageLen = existingSipper.mPackages != null ?
existingSipper.mPackages.length : 0;
final int newPackageLen = sipper.mPackages != null ?
sipper.mPackages.length : 0;
if (newPackageLen > 0) {
String[] newPackages = new String[existingPackageLen + newPackageLen];
if (existingPackageLen > 0) {
System.arraycopy(existingSipper.mPackages, 0, newPackages, 0,
existingPackageLen);
}
System.arraycopy(sipper.mPackages, 0, newPackages, existingPackageLen,
newPackageLen);
existingSipper.mPackages = newPackages;
}
}
} else {
results.add(sipper);
}
}
final int numUidSippers = uidList.size();
for (int i = 0; i < numUidSippers; i++) {
results.add(uidList.valueAt(i));
}
// The sort order must have changed, so re-sort based on total power use.
mBatteryUtils.sortUsageList(results);
return results;
}
protected void refreshUi() {
final Context context = getContext();
if (context == null) {
@@ -527,102 +393,8 @@ public class PowerUsageSummary extends PowerUsageBase implements
final CharSequence timeSequence = Utils.formatRelativeTime(context, lastFullChargeTime,
false);
final int resId = mShowAllApps ? R.string.power_usage_list_summary_device
: R.string.power_usage_list_summary;
mAppListGroup.setTitle(TextUtils.expandTemplate(getText(resId), timeSequence));
refreshAppListGroup();
}
private void refreshAppListGroup() {
final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
final BatteryStats stats = mStatsHelper.getStats();
final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
boolean addedSome = false;
final int dischargeAmount = USE_FAKE_DATA ? 5000
: stats != null ? stats.getDischargeAmount(mStatsType) : 0;
cacheRemoveAllPrefs(mAppListGroup);
mAppListGroup.setOrderingAsAdded(false);
if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
final List<BatterySipper> usageList = getCoalescedUsageList(
USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());
double hiddenPowerMah = mShowAllApps ? 0 :
mBatteryUtils.removeHiddenBatterySippers(usageList);
mBatteryUtils.sortUsageList(usageList);
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower();
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
if (((int) (percentOfTotal + .5)) < 1) {
continue;
}
if (shouldHideSipper(sipper)) {
continue;
}
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(),
userHandle);
final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(),
userHandle);
final String key = extractKeyFromSipper(sipper);
PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
if (pref == null) {
pref = new PowerGaugePreference(getPrefContext(), badgedIcon,
contentDescription, entry);
pref.setKey(key);
}
sipper.percent = percentOfTotal;
pref.setTitle(entry.getLabel());
pref.setOrder(i + 1);
pref.setPercent(percentOfTotal);
pref.shouldShowAnomalyIcon(false);
if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
}
setUsageSummary(pref, sipper);
addedSome = true;
mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() - getCachedCount()
> (MAX_ITEMS_TO_LIST + 1)) {
break;
}
}
}
if (!addedSome) {
addNotAvailableMessage();
}
removeCachedPrefs(mAppListGroup);
BatteryEntry.startRequestQueue();
}
@VisibleForTesting
boolean shouldHideSipper(BatterySipper sipper) {
// Don't show over-counted and unaccounted in any condition
return sipper.drainType == BatterySipper.DrainType.OVERCOUNTED
|| sipper.drainType == BatterySipper.DrainType.UNACCOUNTED;
}
@VisibleForTesting
void refreshAnomalyIcon() {
for (int i = 0, size = mAnomalySparseArray.size(); i < size; i++) {
final String key = extractKeyFromUid(mAnomalySparseArray.keyAt(i));
final PowerGaugePreference pref = (PowerGaugePreference) mAppListGroup.findPreference(
key);
if (pref != null) {
pref.shouldShowAnomalyIcon(true);
}
}
mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps,
timeSequence);
}
@VisibleForTesting
@@ -632,6 +404,11 @@ public class PowerUsageSummary extends PowerUsageBase implements
}
}
@VisibleForTesting
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
mBatteryLayoutPref = layoutPreference;
}
@VisibleForTesting
AnomalyDetectionPolicy getAnomalyDetectionPolicy() {
return new AnomalyDetectionPolicy(getContext());
@@ -674,54 +451,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
mBatteryInfoDebugLoaderCallbacks);
}
@VisibleForTesting
double calculatePercentage(double powerUsage, double dischargeAmount) {
final double totalPower = mStatsHelper.getTotalPower();
return totalPower == 0 ? 0 :
((powerUsage / totalPower) * dischargeAmount);
}
@VisibleForTesting
void setUsageSummary(Preference preference, BatterySipper sipper) {
// Only show summary when usage time is longer than one minute
final long usageTimeMs = sipper.usageTimeMs;
if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), usageTimeMs,
false);
preference.setSummary(
(sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
? timeSequence
: TextUtils.expandTemplate(getText(R.string.battery_used_for),
timeSequence));
}
}
@VisibleForTesting
String extractKeyFromSipper(BatterySipper sipper) {
if (sipper.uidObj != null) {
return extractKeyFromUid(sipper.getUid());
} else if (sipper.drainType == DrainType.USER) {
return sipper.drainType.toString() + sipper.userId;
} else if (sipper.drainType != DrainType.APP) {
return sipper.drainType.toString();
} else if (sipper.getPackages() != null) {
return TextUtils.concat(sipper.getPackages()).toString();
} else {
Log.w(TAG, "Inappropriate BatterySipper without uid and package names: " + sipper);
return "-1";
}
}
@VisibleForTesting
String extractKeyFromUid(int uid) {
return Integer.toString(uid);
}
@VisibleForTesting
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
mBatteryLayoutPref = layoutPreference;
}
@VisibleForTesting
void initFeatureProvider() {
final Context context = getContext();
@@ -755,72 +484,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
}
}
private static List<BatterySipper> getFakeStats() {
ArrayList<BatterySipper> stats = new ArrayList<>();
float use = 5;
for (DrainType type : DrainType.values()) {
if (type == DrainType.APP) {
continue;
}
stats.add(new BatterySipper(type, null, use));
use += 5;
}
for (int i = 0; i < 100; i++) {
stats.add(new BatterySipper(DrainType.APP,
new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
}
stats.add(new BatterySipper(DrainType.APP,
new FakeUid(0), use));
// Simulate dex2oat process.
BatterySipper sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
stats.add(sipper);
return stats;
}
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BatteryEntry.MSG_UPDATE_NAME_ICON:
BatteryEntry entry = (BatteryEntry) msg.obj;
PowerGaugePreference pgp =
(PowerGaugePreference) findPreference(
Integer.toString(entry.sipper.uidObj.getUid()));
if (pgp != null) {
final int userId = UserHandle.getUserId(entry.sipper.getUid());
final UserHandle userHandle = new UserHandle(userId);
pgp.setIcon(mUm.getBadgedIconForUser(entry.getIcon(), userHandle));
pgp.setTitle(entry.name);
if (entry.sipper.drainType == DrainType.APP) {
pgp.setContentDescription(entry.name);
}
}
break;
case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
Activity activity = getActivity();
if (activity != null) {
activity.reportFullyDrawn();
}
break;
}
super.handleMessage(msg);
}
};
@Override
public void onAnomalyHandled(Anomaly anomaly) {
mAnomalySummaryPreferenceController.hideHighUsagePreference();

View File

@@ -26,7 +26,7 @@ import android.util.Log;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.util.ArrayUtils;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
import java.io.FileDescriptor;
import java.io.PrintWriter;

View File

@@ -13,6 +13,7 @@
*/
package com.android.settings.location;
import android.app.ActivityManager;
import android.Manifest.permission;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -32,6 +33,8 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import static com.android.settingslib.Utils.updateLocationMode;
/**
* A class that listens to location settings change and modifies location settings
* settings.
@@ -40,11 +43,6 @@ public class LocationEnabler implements LifecycleObserver, OnResume, OnPause {
private static final String TAG = "LocationEnabler";
@VisibleForTesting
static final String MODE_CHANGING_ACTION =
"com.android.settings.location.MODE_CHANGING";
private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
private static final String NEW_MODE_KEY = "NEW_MODE";
@VisibleForTesting
static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED =
new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
@@ -122,7 +120,7 @@ public class LocationEnabler implements LifecycleObserver, OnResume, OnPause {
return;
}
updateLocationMode(currentMode, mode);
updateLocationMode(mContext, currentMode, mode, ActivityManager.getCurrentUser());
refreshLocationMode();
}
@@ -154,13 +152,4 @@ public class LocationEnabler implements LifecycleObserver, OnResume, OnPause {
private boolean isRestricted() {
return mUserManager.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION);
}
private boolean updateLocationMode(int oldMode, int newMode) {
final Intent intent = new Intent(MODE_CHANGING_ACTION);
intent.putExtra(CURRENT_MODE_KEY, oldMode);
intent.putExtra(NEW_MODE_KEY, newMode);
mContext.sendBroadcast(intent, permission.WRITE_SECURE_SETTINGS);
return Settings.Secure.putInt(
mContext.getContentResolver(), Settings.Secure.LOCATION_MODE, newMode);
}
}

View File

@@ -23,7 +23,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.VisibleForTesting;
import com.android.settings.search.IndexDatabaseHelper.SavedQueriesColumns;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
import java.util.ArrayList;
import java.util.List;

View File

@@ -24,7 +24,7 @@ import android.database.sqlite.SQLiteException;
import android.util.Log;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_SAVED_QUERIES;

View File

@@ -24,7 +24,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.util.Log;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
public class SavedQueryRemover extends AsyncLoader<Void> {

View File

@@ -1,6 +1,6 @@
package com.android.settings.search;
import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.utils.AsyncLoader;
import android.content.Context;

View File

@@ -1,109 +0,0 @@
/*
* Copyright (C) 2016 Google Inc.
* Licensed to 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.utils;
import android.content.AsyncTaskLoader;
import android.content.Context;
/**
* This class fills in some boilerplate for AsyncTaskLoader to actually load things.
*
* Subclasses need to implement {@link AsyncLoader#loadInBackground()} to perform the actual
* background task, and {@link AsyncLoader#onDiscardResult(T)} to clean up previously loaded
* results.
*
* This loader is based on the MailAsyncTaskLoader from the AOSP EmailUnified repo.
*/
public abstract class AsyncLoader<T> extends AsyncTaskLoader<T> {
private T mResult;
public AsyncLoader(final Context context) {
super(context);
}
@Override
protected void onStartLoading() {
if (mResult != null) {
deliverResult(mResult);
}
if (takeContentChanged() || mResult == null) {
forceLoad();
}
}
@Override
protected void onStopLoading() {
cancelLoad();
}
@Override
public void deliverResult(final T data) {
if (isReset()) {
if (data != null) {
onDiscardResult(data);
}
return;
}
final T oldResult = mResult;
mResult = data;
if (isStarted()) {
super.deliverResult(data);
}
if (oldResult != null && oldResult != mResult) {
onDiscardResult(oldResult);
}
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
if (mResult != null) {
onDiscardResult(mResult);
}
mResult = null;
}
@Override
public void onCanceled(final T data) {
super.onCanceled(data);
if (data != null) {
onDiscardResult(data);
}
}
/**
* Called when discarding the load results so subclasses can take care of clean-up or
* recycling tasks. This is not called if the same result (by way of pointer equality) is
* returned again by a subsequent call to loadInBackground, or if result is null.
*
* Note that this may be called concurrently with loadInBackground(), and in some circumstances
* may be called more than once for a given object.
*
* @param result The value returned from {@link AsyncLoader#loadInBackground()} which
* is to be discarded.
*/
protected abstract void onDiscardResult(final T result);
}

View File

@@ -20,7 +20,7 @@ com.android.settings.datausage.AppDataUsage
com.android.settings.datausage.DataPlanUsageSummary
com.android.settings.accessibility.FontSizePreferenceFragmentForSetupWizard
com.android.settings.applications.ManageDomainUrls
com.android.settings.applications.WriteSettingsDetails
com.android.settings.applications.appinfo.WriteSettingsDetails
com.android.settings.applications.ProcessStatsSummary
com.android.settings.users.RestrictedProfileSettings
com.android.settings.accounts.ChooseAccountActivity
@@ -36,7 +36,7 @@ com.android.settings.accessibility.ToggleSelectToSpeakPreferenceFragmentForSetup
com.android.settings.accounts.AccountSyncSettings
com.android.settings.notification.RedactionInterstitial$RedactionInterstitialFragment
com.android.settings.inputmethod.InputMethodAndSubtypeEnabler
com.android.settings.applications.DrawOverlayDetails
com.android.settings.applications.appinfo.DrawOverlayDetails
com.android.settings.backup.ToggleBackupSettingFragment
com.android.settings.users.UserDetailsSettings
com.android.settings.datausage.UnrestrictedDataAccess
@@ -59,9 +59,9 @@ com.android.settings.applications.AppStorageSettings
com.android.settings.notification.NotificationAccessSettings
com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment
com.android.settings.localepicker.LocaleListEditor
com.android.settings.applications.ExternalSourcesDetails
com.android.settings.applications.PictureInPictureSettings
com.android.settings.applications.PictureInPictureDetails
com.android.settings.applications.appinfo.ExternalSourcesDetails
com.android.settings.applications.appinfo.PictureInPictureSettings
com.android.settings.applications.appinfo.PictureInPictureDetails
com.android.settings.ApnSettings
com.android.settings.PrivacySettings
com.android.settings.WifiCallingSettings

View File

@@ -94,7 +94,12 @@ public class DeviceInfoSettingsTest {
}
@Test
@Config(shadows = {
SettingsShadowSystemProperties.class
})
public void getPrefXml_shouldReturnDeviceInfoXml() {
SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + FeatureFlags.DEVICE_INFO_V2,
"true");
assertThat(mSettings.getPreferenceScreenResId()).isEqualTo(R.xml.device_info_settings_v2);
}

View File

@@ -1,125 +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;
import static com.google.common.truth.Truth.assertThat;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import java.util.HashMap;
import java.util.Map;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.xmlpull.v1.XmlPullParserException;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class LicenseHtmlGeneratorFromXmlTest {
private static final String VAILD_XML_STRING =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<licenses>\n" +
"<file-name contentId=\"0\">/file0</file-name>\n" +
"<file-name contentId=\"0\">/file1</file-name>\n" +
"<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n" +
"</licenses>";
private static final String INVAILD_XML_STRING =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<licenses2>\n" +
"<file-name contentId=\"0\">/file0</file-name>\n" +
"<file-name contentId=\"0\">/file1</file-name>\n" +
"<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n" +
"</licenses2>";
private static final String EXPECTED_HTML_STRING =
"<html><head>\n" +
"<style type=\"text/css\">\n" +
"body { padding: 0; font-family: sans-serif; }\n" +
".same-license { background-color: #eeeeee;\n" +
" border-top: 20px solid white;\n" +
" padding: 10px; }\n" +
".label { font-weight: bold; }\n" +
".file-list { margin-left: 1em; color: blue; }\n" +
"</style>\n" +
"</head>" +
"<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n" +
"<div class=\"toc\">\n" +
"<ul>\n" +
"<li><a href=\"#id0\">/file0</a></li>\n" +
"<li><a href=\"#id0\">/file1</a></li>\n" +
"</ul>\n" +
"</div><!-- table of contents -->\n" +
"<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n" +
"<tr id=\"id0\"><td class=\"same-license\">\n" +
"<div class=\"label\">Notices for file(s):</div>\n" +
"<div class=\"file-list\">\n" +
"/file0 <br/>\n" +
"/file1 <br/>\n" +
"</div><!-- file-list -->\n" +
"<pre class=\"license-text\">\n" +
"license content #0\n" +
"</pre><!-- license-text -->\n" +
"</td></tr><!-- same-license -->\n" +
"</table></body></html>\n";
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(VAILD_XML_STRING.getBytes())),
fileNameToContentIdMap, contentIdToFileContentMap);
assertThat(fileNameToContentIdMap.size()).isEqualTo(2);
assertThat(fileNameToContentIdMap.get("/file0")).isEqualTo("0");
assertThat(fileNameToContentIdMap.get("/file1")).isEqualTo("0");
assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
}
@Test(expected = XmlPullParserException.class)
public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(INVAILD_XML_STRING.getBytes())),
fileNameToContentIdMap, contentIdToFileContentMap);
}
@Test
public void testGenerateHtml() {
Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
fileNameToContentIdMap.put("/file0", "0");
fileNameToContentIdMap.put("/file1", "0");
contentIdToFileContentMap.put("0", "license content #0");
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output));
assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
}
}

View File

@@ -1,109 +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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import java.io.File;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class LicenseHtmlLoaderTest {
@Mock
private Context mContext;
LicenseHtmlLoader newLicenseHtmlLoader(ArrayList<File> xmlFiles,
File cachedHtmlFile, boolean isCachedHtmlFileOutdated,
boolean generateHtmlFileSucceeded) {
LicenseHtmlLoader loader = spy(new LicenseHtmlLoader(mContext));
doReturn(xmlFiles).when(loader).getVaildXmlFiles();
doReturn(cachedHtmlFile).when(loader).getCachedHtmlFile();
doReturn(isCachedHtmlFileOutdated).when(loader).isCachedHtmlFileOutdated(any(), any());
doReturn(generateHtmlFileSucceeded).when(loader).generateHtmlFile(any(), any());
return loader;
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testLoadInBackground() {
ArrayList<File> xmlFiles = new ArrayList();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
verify(loader).generateHtmlFile(any(), any());
}
@Test
public void testLoadInBackgroundWithNoVaildXmlFiles() {
ArrayList<File> xmlFiles = new ArrayList();
File cachedHtmlFile = new File("test.html");
LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
assertThat(loader.loadInBackground()).isNull();
verify(loader, never()).generateHtmlFile(any(), any());
}
@Test
public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
ArrayList<File> xmlFiles = new ArrayList();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, false, true);
assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
verify(loader, never()).generateHtmlFile(any(), any());
}
@Test
public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
ArrayList<File> xmlFiles = new ArrayList();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, false);
assertThat(loader.loadInBackground()).isNull();
verify(loader).generateHtmlFile(any(), any());
}
}

View File

@@ -18,21 +18,19 @@ package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.robolectric.Shadows.shadowOf;
import android.app.Application;
import android.os.SystemProperties;
import android.content.Intent;
import android.net.Uri;
import android.os.SystemProperties;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import java.io.File;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,6 +40,8 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import java.io.File;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SettingsLicenseActivityTest {

View File

@@ -0,0 +1,120 @@
/*
* 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.appinfo;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.Manifest.permission.WRITE_SETTINGS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import com.android.settings.TestConfig;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DrawOverlayDetailPreferenceControllerTest {
@Mock
private UserManager mUserManager;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private Preference mPreference;
private Context mContext;
private DrawOverlayDetailPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
mController = spy(new DrawOverlayDetailPreferenceController(mContext, mFragment));
final String key = mController.getPreferenceKey();
when(mPreference.getKey()).thenReturn(key);
}
@Test
public void getAvailabilityStatus_managedProfile_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_noPermissionRequested_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(false);
when(mFragment.getPackageInfo()).thenReturn(new PackageInfo());
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_noSystemAlertWindowPermission_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(false);
final PackageInfo info = new PackageInfo();
info.requestedPermissions = new String[] {WRITE_SETTINGS};
when(mFragment.getPackageInfo()).thenReturn(info);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_hasSystemAlertWindowPermission_shouldReturnAvailable() {
when(mUserManager.isManagedProfile()).thenReturn(false);
final PackageInfo info = new PackageInfo();
info.requestedPermissions = new String[] {SYSTEM_ALERT_WINDOW};
when(mFragment.getPackageInfo()).thenReturn(info);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void getDetailFragmentClass_shouldReturnDrawOverlayDetails() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(DrawOverlayDetails.class);
}
@Test
public void updateState_shouldSetSummary() {
final String summary = "test summary";
doReturn(summary).when(mController).getSummary();
mController.updateState(mPreference);
verify(mPreference).setSummary(summary);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.nullable;

View File

@@ -0,0 +1,105 @@
/*
* 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.appinfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import com.android.settings.TestConfig;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class ExternalSourceDetailPreferenceControllerTest {
@Mock
private UserManager mUserManager;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private Preference mPreference;
private Context mContext;
private ExternalSourceDetailPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
mController = spy(
new ExternalSourceDetailPreferenceController(mContext, mFragment, "Package1"));
final String key = mController.getPreferenceKey();
when(mPreference.getKey()).thenReturn(key);
}
@Test
public void getAvailabilityStatus_managedProfile_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_notPotentialAppSource_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(false);
doReturn(false).when(mController).isPotentialAppSource();
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_isPotentialAppSource_shouldReturnAvailable() {
when(mUserManager.isManagedProfile()).thenReturn(false);
doReturn(true).when(mController).isPotentialAppSource();
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void getDetailFragmentClass_shouldReturnExternalSourcesDetails() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(ExternalSourcesDetails.class);
}
@Test
public void updateState_shouldSetSummary() {
final String summary = "test summary";
doReturn(summary).when(mController).getPreferenceSummary();
mController.updateState(mPreference);
verify(mPreference).setSummary(summary);
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.appinfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class PictureInPictureDetailPreferenceControllerTest {
@Mock
private UserManager mUserManager;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private Preference mPreference;
private Context mContext;
private PictureInPictureDetailPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
mController = spy(
new PictureInPictureDetailPreferenceController(mContext, mFragment, "Package1"));
final String key = mController.getPreferenceKey();
when(mPreference.getKey()).thenReturn(key);
}
@Test
public void getAvailabilityStatus_managedProfile_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_noPictureInPictureActivities_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(false);
doReturn(false).when(mController).hasPictureInPictureActivites();
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_hasPictureInPictureActivities_shouldReturnAvailable() {
when(mUserManager.isManagedProfile()).thenReturn(false);
doReturn(true).when(mController).hasPictureInPictureActivites();
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void getDetailFragmentClass_shouldReturnPictureInPictureDetails() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(PictureInPictureDetails.class);
}
@Test
public void updateState_shouldSetSummary() {
final int summary = R.string.app_permission_summary_allowed;
doReturn(summary).when(mController).getPreferenceSummary();
mController.updateState(mPreference);
verify(mPreference).setSummary(summary);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;

View File

@@ -14,7 +14,7 @@
* limitations under the License
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import static com.google.common.truth.Truth.assertThat;

View File

@@ -14,7 +14,7 @@
* limitations under the License
*/
package com.android.settings.applications;
package com.android.settings.applications.appinfo;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.eq;

View File

@@ -0,0 +1,120 @@
/*
* 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.appinfo;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.Manifest.permission.WRITE_SETTINGS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import com.android.settings.TestConfig;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class WriteSystemSettingsPreferenceControllerTest {
@Mock
private UserManager mUserManager;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private Preference mPreference;
private Context mContext;
private WriteSystemSettingsPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
mController = spy(new WriteSystemSettingsPreferenceController(mContext, mFragment));
final String key = mController.getPreferenceKey();
when(mPreference.getKey()).thenReturn(key);
}
@Test
public void getAvailabilityStatus_managedProfile_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_noPermissionRequested_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(false);
when(mFragment.getPackageInfo()).thenReturn(new PackageInfo());
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_noWriteSettingsPermission_shouldReturnDisabled() {
when(mUserManager.isManagedProfile()).thenReturn(false);
final PackageInfo info = new PackageInfo();
info.requestedPermissions = new String[] {SYSTEM_ALERT_WINDOW};
when(mFragment.getPackageInfo()).thenReturn(info);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER);
}
@Test
public void getAvailabilityStatus_hasWriteSettingsPermission_shouldReturnAvailable() {
when(mUserManager.isManagedProfile()).thenReturn(false);
final PackageInfo info = new PackageInfo();
info.requestedPermissions = new String[] {WRITE_SETTINGS};
when(mFragment.getPackageInfo()).thenReturn(info);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void getDetailFragmentClass_shouldReturnWriteSettingsDetails() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(WriteSettingsDetails.class);
}
@Test
public void updateState_shouldSetSummary() {
final String summary = "test summary";
doReturn(summary).when(mController).getSummary();
mController.updateState(mPreference);
verify(mPreference).setSummary(summary);
}
}

View File

@@ -21,6 +21,7 @@ import static android.arch.lifecycle.Lifecycle.Event.ON_START;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -70,6 +71,6 @@ public class FeatureFlagPreferenceControllerTest {
mLifecycle.handleLifecycleEvent(ON_START);
verify(mScreen).removeAll();
verify(mScreen).addPreference(any(FeatureFlagPreference.class));
verify(mScreen, atLeastOnce()).addPreference(any(FeatureFlagPreference.class));
}
}

View File

@@ -26,12 +26,16 @@ import static org.mockito.Mockito.when;
import android.app.Fragment;
import android.content.Context;
import android.os.SystemProperties;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.FeatureFlagUtils;
import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.core.FeatureFlags;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
import org.junit.Before;
import org.junit.Test;
@@ -72,7 +76,12 @@ public class DeviceModelPreferenceControllerTest {
}
@Test
@Config(shadows = {
SettingsShadowSystemProperties.class
})
public void displayPref_shouldSetSummary() {
SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + FeatureFlags.DEVICE_INFO_V2,
"true");
mController.displayPreference(mPreferenceScreen);
verify(mPreference).setSummary(mContext.getResources().getString(R.string.model_summary,

View File

@@ -0,0 +1,223 @@
/*
* 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.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.FeatureFlagUtils;
import android.util.SparseArray;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.TestConfig;
import com.android.settings.core.FeatureFlags;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
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.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows =
SettingsShadowSystemProperties.class)
public class BatteryAppListPreferenceControllerTest {
private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
private static final String KEY_APP_LIST = "app_list";
private static final int UID = 123;
@Mock
private BatterySipper mNormalBatterySipper;
@Mock
private SettingsActivity mSettingsActivity;
@Mock
private PreferenceGroup mAppListGroup;
@Mock
private PreferenceFragment mFragment;
@Mock
private BatteryUtils mBatteryUtils;
private Context mContext;
private PowerGaugePreference mPreference;
private BatteryAppListPreferenceController mPreferenceController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
FakeFeatureFactory.setupForTest();
mPreference = new PowerGaugePreference(mContext);
when(mNormalBatterySipper.getPackages()).thenReturn(PACKAGE_NAMES);
when(mNormalBatterySipper.getUid()).thenReturn(UID);
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
mPreferenceController = new BatteryAppListPreferenceController(mContext, KEY_APP_LIST, null,
mSettingsActivity, mFragment);
mPreferenceController.mBatteryUtils = mBatteryUtils;
mPreferenceController.mAppListGroup = mAppListGroup;
}
@Test
public void testExtractKeyFromSipper_typeAPPUidObjectNull_returnPackageNames() {
mNormalBatterySipper.uidObj = null;
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
assertThat(key).isEqualTo(TextUtils.concat(mNormalBatterySipper.getPackages()).toString());
}
@Test
public void testExtractKeyFromSipper_typeOther_returnDrainType() {
mNormalBatterySipper.uidObj = null;
mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
assertThat(key).isEqualTo(mNormalBatterySipper.drainType.toString());
}
@Test
public void testExtractKeyFromSipper_typeUser_returnDrainTypeWithUserId() {
mNormalBatterySipper.uidObj = null;
mNormalBatterySipper.drainType = BatterySipper.DrainType.USER;
mNormalBatterySipper.userId = 2;
final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
assertThat(key).isEqualTo("USER2");
}
@Test
public void testExtractKeyFromSipper_typeAPPUidObjectNotNull_returnUid() {
mNormalBatterySipper.uidObj = new BatteryStatsImpl.Uid(new BatteryStatsImpl(), UID);
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
assertThat(key).isEqualTo(Integer.toString(mNormalBatterySipper.getUid()));
}
@Test
public void testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary() {
mNormalBatterySipper.usageTimeMs = 59 * DateUtils.SECOND_IN_MILLIS;
mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
assertThat(mPreference.getSummary()).isNull();
}
@Test
public void testSetUsageSummary_timeMoreThanOneMinute_normalApp_setScreenSummary() {
mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
doReturn(mContext.getText(R.string.battery_used_for)).when(mFragment).getText(
R.string.battery_used_for);
doReturn(mContext).when(mFragment).getContext();
mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
assertThat(mPreference.getSummary().toString()).isEqualTo("Used for 2m");
}
@Test
public void testSetUsageSummary_timeMoreThanOneMinute_hiddenApp_setUsedSummary() {
mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
doReturn(true).when(mBatteryUtils).shouldHideSipper(mNormalBatterySipper);
doReturn(mContext).when(mFragment).getContext();
mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
assertThat(mPreference.getSummary().toString()).isEqualTo("2m");
}
@Test
public void testSetUsageSummary_timeMoreThanOneMinute_notApp_setUsedSummary() {
mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
mNormalBatterySipper.drainType = BatterySipper.DrainType.PHONE;
doReturn(mContext).when(mFragment).getContext();
mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
assertThat(mPreference.getSummary().toString()).isEqualTo("2m");
}
@Test
public void testRefreshAnomalyIcon_containsAnomaly_showAnomalyIcon() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, true);
PowerGaugePreference preference = new PowerGaugePreference(mContext);
final String key = mPreferenceController.extractKeyFromUid(UID);
final SparseArray<List<Anomaly>> anomalySparseArray = new SparseArray<>();
anomalySparseArray.append(UID, null);
preference.setKey(key);
doReturn(preference).when(mAppListGroup).findPreference(key);
mPreferenceController.refreshAnomalyIcon(anomalySparseArray);
assertThat(preference.showAnomalyIcon()).isTrue();
}
@Test
public void testShouldHideSipper_typeOvercounted_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_typeUnaccounted_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isTrue();
}
@Test
public void testShouldHideSipper_typeNormal_returnFalse() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isFalse();
}
@Test
public void testIsAvailable_featureOn_returnTrue() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, true);
assertThat(mPreferenceController.isAvailable()).isTrue();
}
@Test
public void testIsAvailable_featureOff_returnFalse() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, false);
assertThat(mPreferenceController.isAvailable()).isFalse();
}
}

View File

@@ -0,0 +1,422 @@
/*
* Copyright (C) 2016 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.fuelgauge;
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_HIGH_POWER_APPS;
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_TOGGLE_APPS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.PowerManager;
import android.support.v7.preference.PreferenceScreen;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.TestConfig;
import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
/**
* Unit tests for {@link PowerUsageSummary}.
*/
// TODO: Improve this test class so that it starts up the real activity and fragment.
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {
SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class,
})
public class PowerUsageSummaryTest {
private static final String STUB_STRING = "stub_string";
private static final int UID = 123;
private static final int UID_2 = 234;
private static final int POWER_MAH = 100;
private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
private static final long TIME_SINCE_LAST_FULL_CHARGE_US =
TIME_SINCE_LAST_FULL_CHARGE_MS * 1000;
private static final long USAGE_TIME_MS = 65 * 60 * 1000;
private static final double TOTAL_POWER = 200;
public static final String NEW_ML_EST_SUFFIX = "(New ML est)";
public static final String OLD_EST_SUFFIX = "(Old est)";
private static Intent sAdditionalBatteryInfoIntent;
@BeforeClass
public static void beforeClass() {
sAdditionalBatteryInfoIntent = new Intent("com.example.app.ADDITIONAL_BATTERY_INFO");
}
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Menu mMenu;
@Mock
private MenuItem mToggleAppsMenu;
@Mock
private MenuItem mHighPowerMenu;
@Mock
private MenuInflater mMenuInflater;
@Mock
private BatterySipper mNormalBatterySipper;
@Mock
private BatterySipper mScreenBatterySipper;
@Mock
private BatterySipper mCellBatterySipper;
@Mock
private LayoutPreference mBatteryLayoutPref;
@Mock
private TextView mBatteryPercentText;
@Mock
private TextView mSummary1;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsHelper mBatteryHelper;
@Mock
private PowerManager mPowerManager;
@Mock
private SettingsActivity mSettingsActivity;
@Mock
private LoaderManager mLoaderManager;
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private AnomalyDetectionPolicy mAnomalyDetectionPolicy;
@Mock
private BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
private List<BatterySipper> mUsageList;
private Context mRealContext;
private TestFragment mFragment;
private FakeFeatureFactory mFeatureFactory;
private BatteryMeterView mBatteryMeterView;
private PowerGaugePreference mScreenUsagePref;
private PowerGaugePreference mLastFullChargePref;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mRealContext = RuntimeEnvironment.application;
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
mScreenUsagePref = new PowerGaugePreference(mRealContext);
mLastFullChargePref = new PowerGaugePreference(mRealContext);
mFragment = spy(new TestFragment(mContext));
mFragment.initFeatureProvider();
mBatteryMeterView = new BatteryMeterView(mRealContext);
mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0);
doNothing().when(mFragment).restartBatteryStatsLoader();
doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager();
when(mFragment.getActivity()).thenReturn(mSettingsActivity);
when(mToggleAppsMenu.getItemId()).thenReturn(MENU_TOGGLE_APPS);
when(mHighPowerMenu.getItemId()).thenReturn(MENU_HIGH_POWER_APPS);
when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
.thenReturn(sAdditionalBatteryInfoIntent);
when(mBatteryHelper.getTotalPower()).thenReturn(TOTAL_POWER);
when(mBatteryHelper.getStats().computeBatteryRealtime(anyLong(), anyInt())).thenReturn(
TIME_SINCE_LAST_FULL_CHARGE_US);
when(mNormalBatterySipper.getUid()).thenReturn(UID);
mNormalBatterySipper.totalPowerMah = POWER_MAH;
mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
mCellBatterySipper.drainType = BatterySipper.DrainType.CELL;
mCellBatterySipper.totalPowerMah = POWER_MAH;
when(mBatteryLayoutPref.findViewById(R.id.summary1)).thenReturn(mSummary1);
when(mBatteryLayoutPref.findViewById(R.id.battery_percent)).thenReturn(mBatteryPercentText);
when(mBatteryLayoutPref.findViewById(R.id.battery_header_icon))
.thenReturn(mBatteryMeterView);
mFragment.setBatteryLayoutPreference(mBatteryLayoutPref);
mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
mScreenBatterySipper.usageTimeMs = USAGE_TIME_MS;
mUsageList = new ArrayList<>();
mUsageList.add(mNormalBatterySipper);
mUsageList.add(mScreenBatterySipper);
mUsageList.add(mCellBatterySipper);
mFragment.mStatsHelper = mBatteryHelper;
when(mBatteryHelper.getUsageList()).thenReturn(mUsageList);
mFragment.mScreenUsagePref = mScreenUsagePref;
mFragment.mLastFullChargePref = mLastFullChargePref;
mFragment.mBatteryUtils = spy(new BatteryUtils(mRealContext));
}
@Test
public void testOptionsMenu_menuHighPower_metricEventInvoked() {
mFragment.onOptionsItemSelected(mHighPowerMenu);
verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION);
}
@Test
public void testOptionsMenu_menuAppToggle_metricEventInvoked() {
mFragment.onOptionsItemSelected(mToggleAppsMenu);
mFragment.mShowAllApps = false;
verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE, true);
}
@Test
public void testOptionsMenu_toggleAppsEnabled() {
when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled())
.thenReturn(true);
mFragment.mShowAllApps = false;
mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
verify(mMenu).add(Menu.NONE, MENU_TOGGLE_APPS, Menu.NONE, R.string.show_all_apps);
}
@Test
public void testOptionsMenu_clickToggleAppsMenu_dataChanged() {
testToggleAllApps(true);
testToggleAllApps(false);
}
private void testToggleAllApps(final boolean isShowApps) {
mFragment.mShowAllApps = isShowApps;
mFragment.onOptionsItemSelected(mToggleAppsMenu);
assertThat(mFragment.mShowAllApps).isEqualTo(!isShowApps);
}
@Test
public void testFindBatterySipperByType_findTypeScreen() {
BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList,
BatterySipper.DrainType.SCREEN);
assertThat(sipper).isSameAs(mScreenBatterySipper);
}
@Test
public void testFindBatterySipperByType_findTypeApp() {
BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList,
BatterySipper.DrainType.APP);
assertThat(sipper).isSameAs(mNormalBatterySipper);
}
@Test
public void testUpdateScreenPreference_showCorrectSummary() {
doReturn(mScreenBatterySipper).when(mFragment).findBatterySipperByType(any(), any());
doReturn(mRealContext).when(mFragment).getContext();
final CharSequence expectedSummary = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS,
false);
mFragment.updateScreenPreference();
assertThat(mScreenUsagePref.getSubtitle()).isEqualTo(expectedSummary);
}
@Test
public void testUpdateLastFullChargePreference_showCorrectSummary() {
doReturn(mRealContext).when(mFragment).getContext();
mFragment.updateLastFullChargePreference(TIME_SINCE_LAST_FULL_CHARGE_MS);
assertThat(mLastFullChargePref.getSubtitle()).isEqualTo("2 hr. ago");
}
@Test
public void testUpdatePreference_usageListEmpty_shouldNotCrash() {
when(mBatteryHelper.getUsageList()).thenReturn(new ArrayList<BatterySipper>());
doReturn(STUB_STRING).when(mFragment).getString(anyInt(), any());
doReturn(mRealContext).when(mFragment).getContext();
// Should not crash when update
mFragment.updateScreenPreference();
}
@Test
public void testNonIndexableKeys_MatchPreferenceKeys() {
final Context context = RuntimeEnvironment.application;
final List<String> niks = PowerUsageSummary.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(context);
final List<String> keys = XmlTestUtils.getKeysFromPreferenceXml(context,
R.xml.power_usage_summary);
assertThat(keys).containsAllIn(niks);
}
@Test
public void testPreferenceControllers_getPreferenceKeys_existInPreferenceScreen() {
final Context context = RuntimeEnvironment.application;
final PowerUsageSummary fragment = new PowerUsageSummary();
final List<String> preferenceScreenKeys = XmlTestUtils.getKeysFromPreferenceXml(context,
fragment.getPreferenceScreenResId());
final List<String> preferenceKeys = new ArrayList<>();
for (AbstractPreferenceController controller : fragment.getPreferenceControllers(context)) {
preferenceKeys.add(controller.getPreferenceKey());
}
assertThat(preferenceScreenKeys).containsAllIn(preferenceKeys);
}
@Test
public void testUpdateAnomalySparseArray() {
mFragment.mAnomalySparseArray = new SparseArray<>();
final List<Anomaly> anomalies = new ArrayList<>();
final Anomaly anomaly1 = new Anomaly.Builder().setUid(UID).build();
final Anomaly anomaly2 = new Anomaly.Builder().setUid(UID).build();
final Anomaly anomaly3 = new Anomaly.Builder().setUid(UID_2).build();
anomalies.add(anomaly1);
anomalies.add(anomaly2);
anomalies.add(anomaly3);
mFragment.updateAnomalySparseArray(anomalies);
assertThat(mFragment.mAnomalySparseArray.get(UID)).containsExactly(anomaly1, anomaly2);
assertThat(mFragment.mAnomalySparseArray.get(UID_2)).containsExactly(anomaly3);
}
@Test
public void testInitAnomalyDetectionIfPossible_detectionEnabled_init() {
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
doReturn(mAnomalyDetectionPolicy).when(mFragment).getAnomalyDetectionPolicy();
when(mAnomalyDetectionPolicy.isAnomalyDetectionEnabled()).thenReturn(true);
mFragment.restartAnomalyDetectionIfPossible();
verify(mLoaderManager).restartLoader(eq(PowerUsageSummary.ANOMALY_LOADER), eq(Bundle.EMPTY),
any());
}
@Test
public void testShowBothEstimates_summariesAreBothModified() {
doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary2);
doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary1);
mFragment.onLongClick(new View(mRealContext));
TextView summary1 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary1);
TextView summary2 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary2);
Robolectric.flushBackgroundThreadScheduler();
assertThat(summary2.getText().toString().contains(NEW_ML_EST_SUFFIX));
assertThat(summary1.getText().toString().contains(OLD_EST_SUFFIX));
}
@Test
public void testSaveInstanceState_showAllAppsRestored() {
Bundle bundle = new Bundle();
mFragment.mShowAllApps = true;
doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
mFragment.onSaveInstanceState(bundle);
mFragment.restoreSavedInstance(bundle);
assertThat(mFragment.mShowAllApps).isTrue();
}
@Test
public void testDebugMode() {
doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isEstimateDebugEnabled();
mFragment.restartBatteryInfoLoader();
ArgumentCaptor<View.OnLongClickListener> listener = ArgumentCaptor.forClass(
View.OnLongClickListener.class);
verify(mSummary1).setOnLongClickListener(listener.capture());
// Calling the listener should disable it.
listener.getValue().onLongClick(mSummary1);
verify(mSummary1).setOnLongClickListener(null);
// Restarting the loader should reset the listener.
mFragment.restartBatteryInfoLoader();
verify(mSummary1, times(2)).setOnLongClickListener(any(View.OnLongClickListener.class));
}
@Test
public void testRestartBatteryStatsLoader_notClearHeader_quickUpdateNotInvoked() {
mFragment.mBatteryHeaderPreferenceController = mBatteryHeaderPreferenceController;
mFragment.restartBatteryStatsLoader(false /* clearHeader */);
verify(mBatteryHeaderPreferenceController, never()).quickUpdateHeaderPreference();
}
public static class TestFragment extends PowerUsageSummary {
private Context mContext;
public TestFragment(Context context) {
mContext = context;
}
@Override
public Context getContext() {
return mContext;
}
@Override
protected void refreshUi() {
// Leave it empty for toggle apps menu test
}
}
}

View File

@@ -16,9 +16,7 @@
package com.android.settings.location;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -31,18 +29,21 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.location.LocationManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,11 +53,10 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@Config(manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {ShadowSecureSettings.class})
public class LocationEnablerTest {
@Mock
@@ -178,8 +178,11 @@ public class LocationEnablerTest {
mEnabler.setLocationMode(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY);
verify(mContext).sendBroadcast(argThat(actionMatches(mEnabler.MODE_CHANGING_ACTION)),
verify(mContext).sendBroadcastAsUser(
argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
eq(UserHandle.of(ActivityManager.getCurrentUser())),
eq(WRITE_SECURE_SETTINGS));
}
@Test

View File

@@ -16,29 +16,30 @@
package com.android.settings.location;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.provider.Settings;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settings.widget.RadioButtonPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {ShadowSecureSettings.class})
public class LocationModeRadioButtonPreferenceControllerTest {
@Mock

View File

@@ -82,7 +82,7 @@ public class ZenModeAutomaticRulesPreferenceControllerTest {
mock(Lifecycle.class));
ReflectionHelpers.setField(mController, "mBackend", mBackend);
ReflectionHelpers.setField(mController, "DEFAULT_RULE_IDS", mDefaultIds);
ReflectionHelpers.setField(mController, "mDefaultRuleIds", mDefaultIds);
when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
mockPref);
@@ -188,4 +188,4 @@ public class ZenModeAutomaticRulesPreferenceControllerTest {
return ruleMap;
}
}
}

View File

@@ -17,10 +17,10 @@
package com.android.settings.notification;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.content.Intent;
import android.os.UserManager;
import com.android.settings.R;
import com.android.settings.TestConfig;
@@ -34,6 +34,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowToast;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.RuntimeEnvironment;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
@@ -58,14 +60,19 @@ public class ZenModeScheduleRuleSettingsTest {
private Intent mIntent;
@Mock
private UserManager mUserManager;
private NotificationManager mNotificationManager;
private TestFragment mFragment;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowApplication = ShadowApplication.getInstance();
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
mContext = shadowApplication.getApplicationContext();
mFragment = spy(new TestFragment());
mFragment.onAttach(application);
@@ -77,13 +84,13 @@ public class ZenModeScheduleRuleSettingsTest {
when(mActivity.getTheme()).thenReturn(res.newTheme());
when(mActivity.getIntent()).thenReturn(mIntent);
when(mActivity.getResources()).thenReturn(res);
when(mFragment.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mFragment.getContext()).thenReturn(mContext);
}
@Test
public void onCreate_noRuleId_shouldToastAndFinishAndNoCrash() {
final Context ctx = application.getApplicationContext();
final String expected = ctx.getResources().getString(R.string.zen_mode_rule_not_found_text);
final String expected = mContext.getResources().getString(
R.string.zen_mode_rule_not_found_text);
mFragment.onCreate(null);
@@ -93,7 +100,7 @@ public class ZenModeScheduleRuleSettingsTest {
// verify the finish
verify(mActivity).finish();
//shoud not crash
//should not crash
}
public static class TestFragment extends ZenModeScheduleRuleSettings {

View File

@@ -5,14 +5,19 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
telephony-common \
ims-common \
android.test.base \
android.test.mock \
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
espresso-core \
espresso-contrib-nodep \
espresso-intents-nodep \
legacy-android-test \
mockito-target-minus-junit4 \
platform-test-annotations \
truth-prebuilt \