Merge "Final UX for Wi-Fi assistant platform settings." into lmp-mr1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a800b4b..5e10171 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -692,17 +692,6 @@
android:value="true" />
</activity>
- <activity android:name="SettingsSafetyLegalActivity"
- android:label="@string/settings_safetylegal_activity_title"
- android:theme="@*android:style/Theme.Material.Light.Dialog.Alert">
- <intent-filter>
- <action android:name="android.settings.SAFETY" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
- android:value="true" />
- </activity>
-
<activity android:name="Settings$ManageApplicationsActivity"
android:label="@string/applications_settings"
android:taskAffinity="">
diff --git a/res/drawable/ic_audio_alarm_24dp.xml b/res/drawable/ic_audio_alarm_24dp.xml
deleted file mode 100644
index ddfaa01..0000000
--- a/res/drawable/ic_audio_alarm_24dp.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_alarm from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@*android:drawable/ic_audio_alarm"
- android:inset="4dp"
- />
diff --git a/res/drawable/ic_audio_vol_24dp.xml b/res/drawable/ic_audio_vol_24dp.xml
deleted file mode 100644
index ecb3a40..0000000
--- a/res/drawable/ic_audio_vol_24dp.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_vol from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@*android:drawable/ic_audio_vol"
- android:inset="4dp"
- />
diff --git a/res/drawable/ring_notif.xml b/res/drawable/ring_notif.xml
deleted file mode 100644
index cc9da31..0000000
--- a/res/drawable/ring_notif.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_ring_notif from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@*android:drawable/ic_audio_ring_notif"
- android:inset="4dp"
- />
-
diff --git a/res/drawable/ring_notif_mute.xml b/res/drawable/ring_notif_mute.xml
deleted file mode 100644
index fe9417d..0000000
--- a/res/drawable/ring_notif_mute.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_ring_notif_mute from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@*android:drawable/ic_audio_ring_notif_mute"
- android:inset="4dp"
- />
diff --git a/res/drawable/ring_notif_vibrate.xml b/res/drawable/ring_notif_vibrate.xml
deleted file mode 100644
index 37113d7..0000000
--- a/res/drawable/ring_notif_vibrate.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_ring_notif_vibrate from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@*android:drawable/ic_audio_ring_notif_vibrate"
- android:inset="4dp"
- />
diff --git a/res/layout/preference_volume_slider.xml b/res/layout/preference_volume_slider.xml
new file mode 100644
index 0000000..878a710
--- /dev/null
+++ b/res/layout/preference_volume_slider.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip">
+
+ <TextView android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:textColor="?android:attr/textColorPrimary"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_gravity="center_vertical|start"
+ android:layout_width="24dp"
+ android:layout_height="24dp" />
+
+ <SeekBar android:id="@*android:id/seekbar"
+ android:layout_marginStart="24dp"
+ android:layout_gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView android:id="@+id/suppression_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|start"
+ android:textAlignment="viewStart"
+ android:layout_marginStart="24dp"
+ android:paddingStart="14dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="@android:style/TextAppearance.Material.Body1"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/res/xml/device_info_settings.xml b/res/xml/device_info_settings.xml
index 2c47c40..45eda60 100644
--- a/res/xml/device_info_settings.xml
+++ b/res/xml/device_info_settings.xml
@@ -81,12 +81,6 @@
</PreferenceScreen>
<PreferenceScreen
- android:key="safetylegal"
- android:title="@string/settings_safetylegal_title">
- <intent android:action="android.settings.SAFETY" />
- </PreferenceScreen>
-
- <PreferenceScreen
android:key="regulatory_info"
android:title="@string/regulatory_information">
<intent android:action="android.settings.SHOW_REGULATORY_INFO" />
diff --git a/res/xml/notification_settings.xml b/res/xml/notification_settings.xml
index 3f9b40c..e5dda19 100644
--- a/res/xml/notification_settings.xml
+++ b/res/xml/notification_settings.xml
@@ -27,25 +27,25 @@
<!-- Media volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="media_volume"
- android:icon="@drawable/ic_audio_vol_24dp"
+ android:icon="@*android:drawable/ic_audio_vol"
android:title="@string/media_volume_option_title" />
<!-- Alarm volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="alarm_volume"
- android:icon="@drawable/ic_audio_alarm_24dp"
+ android:icon="@*android:drawable/ic_audio_alarm"
android:title="@string/alarm_volume_option_title" />
<!-- Ring volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="ring_volume"
- android:icon="@drawable/ring_notif"
+ android:icon="@*android:drawable/ic_audio_ring_notif"
android:title="@string/ring_volume_option_title" />
<!-- Notification volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="notification_volume"
- android:icon="@drawable/ring_notif"
+ android:icon="@*android:drawable/ic_audio_ring_notif"
android:title="@string/notification_volume_option_title" />
<!-- Also vibrate for calls -->
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index 2e917f5..40bed90 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -22,11 +22,13 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import android.security.Credentials;
import android.security.KeyChain.KeyChainConnection;
@@ -126,8 +128,7 @@
if (ACTION_RESET.equals(action)) {
new ResetDialog();
} else {
- if (ACTION_INSTALL.equals(action)
- && "com.android.certinstaller".equals(getCallingPackage())) {
+ if (ACTION_INSTALL.equals(action) && checkCallerIsCertInstallerOrSelfInProfile()) {
mInstallBundle = intent.getExtras();
}
// ACTION_UNLOCK also handled here in addition to ACTION_INSTALL
@@ -215,54 +216,74 @@
* Install credentials if available, otherwise do nothing.
*/
private void installIfAvailable() {
- if (mInstallBundle != null && !mInstallBundle.isEmpty()) {
- Bundle bundle = mInstallBundle;
- mInstallBundle = null;
-
- final int uid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
-
- if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) {
- String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
- byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
-
- int flags = KeyStore.FLAG_ENCRYPTED;
- if (uid == Process.WIFI_UID && isHardwareBackedKey(value)) {
- // Hardware backed keystore is secure enough to allow for WIFI stack
- // to enable access to secure networks without user intervention
- Log.d(TAG, "Saving private key with FLAG_NONE for WIFI_UID");
- flags = KeyStore.FLAG_NONE;
- }
-
- if (!mKeyStore.importKey(key, value, uid, flags)) {
- Log.e(TAG, "Failed to install " + key + " as user " + uid);
- return;
- }
- }
-
- int flags = (uid == Process.WIFI_UID) ? KeyStore.FLAG_NONE : KeyStore.FLAG_ENCRYPTED;
-
- if (bundle.containsKey(Credentials.EXTRA_USER_CERTIFICATE_NAME)) {
- String certName = bundle.getString(Credentials.EXTRA_USER_CERTIFICATE_NAME);
- byte[] certData = bundle.getByteArray(Credentials.EXTRA_USER_CERTIFICATE_DATA);
-
- if (!mKeyStore.put(certName, certData, uid, flags)) {
- Log.e(TAG, "Failed to install " + certName + " as user " + uid);
- return;
- }
- }
-
- if (bundle.containsKey(Credentials.EXTRA_CA_CERTIFICATES_NAME)) {
- String caListName = bundle.getString(Credentials.EXTRA_CA_CERTIFICATES_NAME);
- byte[] caListData = bundle.getByteArray(Credentials.EXTRA_CA_CERTIFICATES_DATA);
-
- if (!mKeyStore.put(caListName, caListData, uid, flags)) {
- Log.e(TAG, "Failed to install " + caListName + " as user " + uid);
- return;
- }
- }
-
- setResult(RESULT_OK);
+ if (mInstallBundle == null || mInstallBundle.isEmpty()) {
+ return;
}
+
+ Bundle bundle = mInstallBundle;
+ mInstallBundle = null;
+
+ final int uid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
+
+ if (!UserHandle.isSameUser(uid, Process.myUid())) {
+ int dstUserId = UserHandle.getUserId(uid);
+ int myUserId = UserHandle.myUserId();
+
+ // Restrict install target to the wifi uid.
+ if (uid != Process.WIFI_UID) {
+ Log.e(TAG, "Failed to install credentials as uid " + uid + ": cross-user installs"
+ + " may only target wifi uids");
+ return;
+ }
+
+ Intent installIntent = new Intent(ACTION_INSTALL)
+ .setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
+ .putExtras(bundle);
+ startActivityAsUser(installIntent, new UserHandle(dstUserId));
+ return;
+ }
+
+ if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) {
+ String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
+ byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
+
+ int flags = KeyStore.FLAG_ENCRYPTED;
+ if (uid == Process.WIFI_UID && isHardwareBackedKey(value)) {
+ // Hardware backed keystore is secure enough to allow for WIFI stack
+ // to enable access to secure networks without user intervention
+ Log.d(TAG, "Saving private key with FLAG_NONE for WIFI_UID");
+ flags = KeyStore.FLAG_NONE;
+ }
+
+ if (!mKeyStore.importKey(key, value, uid, flags)) {
+ Log.e(TAG, "Failed to install " + key + " as uid " + uid);
+ return;
+ }
+ }
+
+ int flags = (uid == Process.WIFI_UID) ? KeyStore.FLAG_NONE : KeyStore.FLAG_ENCRYPTED;
+
+ if (bundle.containsKey(Credentials.EXTRA_USER_CERTIFICATE_NAME)) {
+ String certName = bundle.getString(Credentials.EXTRA_USER_CERTIFICATE_NAME);
+ byte[] certData = bundle.getByteArray(Credentials.EXTRA_USER_CERTIFICATE_DATA);
+
+ if (!mKeyStore.put(certName, certData, uid, flags)) {
+ Log.e(TAG, "Failed to install " + certName + " as uid " + uid);
+ return;
+ }
+ }
+
+ if (bundle.containsKey(Credentials.EXTRA_CA_CERTIFICATES_NAME)) {
+ String caListName = bundle.getString(Credentials.EXTRA_CA_CERTIFICATES_NAME);
+ byte[] caListData = bundle.getByteArray(Credentials.EXTRA_CA_CERTIFICATES_DATA);
+
+ if (!mKeyStore.put(caListName, caListData, uid, flags)) {
+ Log.e(TAG, "Failed to install " + caListName + " as uid " + uid);
+ return;
+ }
+ }
+
+ setResult(RESULT_OK);
}
/**
@@ -371,6 +392,42 @@
}
/**
+ * Check that the caller is either certinstaller or Settings running in a profile of this user.
+ */
+ private boolean checkCallerIsCertInstallerOrSelfInProfile() {
+ if (TextUtils.equals("com.android.certinstaller", getCallingPackage())) {
+ // CertInstaller is allowed to install credentials
+ return true;
+ }
+
+ final int launchedFromUserId;
+ try {
+ int launchedFromUid = android.app.ActivityManagerNative.getDefault()
+ .getLaunchedFromUid(getActivityToken());
+ if (launchedFromUid == -1) {
+ Log.e(TAG, ACTION_INSTALL + " must be started with startActivityForResult");
+ return false;
+ }
+ if (!UserHandle.isSameApp(launchedFromUid, Process.myUid())) {
+ // Not the same app
+ return false;
+ }
+ launchedFromUserId = UserHandle.getUserId(launchedFromUid);
+ } catch (RemoteException re) {
+ // Error talking to ActivityManager, just give up
+ return false;
+ }
+
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ UserInfo parentInfo = userManager.getProfileParent(launchedFromUserId);
+ if (parentInfo == null || parentInfo.id != UserHandle.myUserId()) {
+ // Caller is not running in a profile of this user
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Confirm existing key guard, returning password via onActivityResult.
*/
private boolean confirmKeyGuard() {
diff --git a/src/com/android/settings/SettingsLicenseActivity.java b/src/com/android/settings/SettingsLicenseActivity.java
index 3d66b77..52b48bb 100644
--- a/src/com/android/settings/SettingsLicenseActivity.java
+++ b/src/com/android/settings/SettingsLicenseActivity.java
@@ -16,205 +16,64 @@
package com.android.settings;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
import android.widget.Toast;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.res.Configuration;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.zip.GZIPInputStream;
+import java.io.File;
/**
* The "dialog" that shows from "License" in the Settings app.
*/
public class SettingsLicenseActivity extends Activity {
-
private static final String TAG = "SettingsLicenseActivity";
- private static final boolean LOGV = false || false;
private static final String DEFAULT_LICENSE_PATH = "/system/etc/NOTICE.html.gz";
private static final String PROPERTY_LICENSE_PATH = "ro.config.license_path";
- private Handler mHandler;
- private WebView mWebView;
- private ProgressDialog mSpinnerDlg;
- private AlertDialog mTextDlg;
-
- private class LicenseFileLoader implements Runnable {
-
- private static final String INNER_TAG = "SettingsLicenseActivity.LicenseFileLoader";
- public static final int STATUS_OK = 0;
- public static final int STATUS_NOT_FOUND = 1;
- public static final int STATUS_READ_ERROR = 2;
- public static final int STATUS_EMPTY_FILE = 3;
-
- private String mFileName;
- private Handler mHandler;
-
- public LicenseFileLoader(String fileName, Handler handler) {
- mFileName = fileName;
- mHandler = handler;
- }
-
- public void run() {
-
- int status = STATUS_OK;
-
- InputStreamReader inputReader = null;
- StringBuilder data = new StringBuilder(2048);
- try {
- char[] tmp = new char[2048];
- int numRead;
- if (mFileName.endsWith(".gz")) {
- inputReader = new InputStreamReader(
- new GZIPInputStream(new FileInputStream(mFileName)));
- } else {
- inputReader = new FileReader(mFileName);
- }
-
- while ((numRead = inputReader.read(tmp)) >= 0) {
- data.append(tmp, 0, numRead);
- }
- } catch (FileNotFoundException e) {
- Log.e(INNER_TAG, "License HTML file not found at " + mFileName, e);
- status = STATUS_NOT_FOUND;
- } catch (IOException e) {
- Log.e(INNER_TAG, "Error reading license HTML file at " + mFileName, e);
- status = STATUS_READ_ERROR;
- } finally {
- try {
- if (inputReader != null) {
- inputReader.close();
- }
- } catch (IOException e) {
- }
- }
-
- if ((status == STATUS_OK) && TextUtils.isEmpty(data)) {
- Log.e(INNER_TAG, "License HTML is empty (from " + mFileName + ")");
- status = STATUS_EMPTY_FILE;
- }
-
- // Tell the UI thread that we are finished.
- Message msg = mHandler.obtainMessage(status, null);
- if (status == STATUS_OK) {
- msg.obj = data.toString();
- }
- mHandler.sendMessage(msg);
- }
- }
-
- public SettingsLicenseActivity() {
- super();
- mHandler = null;
- mWebView = null;
- mSpinnerDlg = null;
- mTextDlg = null;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- String fileName = SystemProperties.get(PROPERTY_LICENSE_PATH, DEFAULT_LICENSE_PATH);
- if (TextUtils.isEmpty(fileName)) {
- Log.e(TAG, "The system property for the license file is empty.");
+ final String path = SystemProperties.get(PROPERTY_LICENSE_PATH, DEFAULT_LICENSE_PATH);
+ if (TextUtils.isEmpty(path)) {
+ Log.e(TAG, "The system property for the license file is empty");
showErrorAndFinish();
return;
}
- // The activity does not have any view itself,
- // so set it invisible to avoid displaying the title text in the background.
- setVisible(false);
-
- mWebView = new WebView(this);
-
- mHandler = new Handler() {
-
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
-
- if (msg.what == LicenseFileLoader.STATUS_OK) {
- String text = (String) msg.obj;
- showPageOfText(text);
- } else {
- showErrorAndFinish();
- }
- }
- };
-
- CharSequence title = getText(R.string.settings_license_activity_title);
- CharSequence msg = getText(R.string.settings_license_activity_loading);
-
- ProgressDialog pd = ProgressDialog.show(this, title, msg, true, false);
- pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- mSpinnerDlg = pd;
-
- // Start separate thread to do the actual loading.
- Thread thread = new Thread(new LicenseFileLoader(fileName, mHandler));
- thread.start();
- }
-
- @Override
- protected void onDestroy() {
- if (mTextDlg != null && mTextDlg.isShowing()) {
- mTextDlg.dismiss();
+ final File file = new File(path);
+ if (!file.exists() || file.length() == 0) {
+ Log.e(TAG, "License file " + path + " does not exist");
+ showErrorAndFinish();
+ return;
}
- if (mSpinnerDlg != null && mSpinnerDlg.isShowing()) {
- mSpinnerDlg.dismiss();
+
+ // Kick off external viewer due to WebView security restrictions; we
+ // carefully point it at HTMLViewer, since it offers to decompress
+ // before viewing.
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.fromFile(file), "text/html");
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.settings_license_activity_title));
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setPackage("com.android.htmlviewer");
+
+ try {
+ startActivity(intent);
+ finish();
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to find viewer", e);
+ showErrorAndFinish();
}
- super.onDestroy();
- }
-
- private void showPageOfText(String text) {
- // Create an AlertDialog to display the WebView in.
- AlertDialog.Builder builder = new AlertDialog.Builder(SettingsLicenseActivity.this);
- builder.setCancelable(true)
- .setView(mWebView)
- .setTitle(R.string.settings_license_activity_title);
-
- mTextDlg = builder.create();
- mTextDlg.setOnDismissListener(new OnDismissListener() {
-
- public void onDismiss(DialogInterface dlgi) {
- SettingsLicenseActivity.this.finish();
- }
- });
-
- // Begin the loading. This will be done in a separate thread in WebView.
- mWebView.loadDataWithBaseURL(null, text, "text/html", "utf-8", null);
- mWebView.setWebViewClient(new WebViewClient() {
- @Override
- public void onPageFinished(WebView view, String url) {
- mSpinnerDlg.dismiss();
- if (SettingsLicenseActivity.this.isResumed()) {
- mTextDlg.show();
- }
- }
- });
-
- mWebView = null;
}
private void showErrorAndFinish() {
- mSpinnerDlg.dismiss();
- mSpinnerDlg = null;
Toast.makeText(this, R.string.settings_license_activity_unavailable, Toast.LENGTH_LONG)
.show();
finish();
diff --git a/src/com/android/settings/SettingsSafetyLegalActivity.java b/src/com/android/settings/SettingsSafetyLegalActivity.java
deleted file mode 100644
index 368ee1d..0000000
--- a/src/com/android/settings/SettingsSafetyLegalActivity.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2009 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.app.AlertDialog;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.view.KeyEvent;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-import android.content.DialogInterface;
-
-/**
- * The "dialog" that shows from "Safety information" in the Settings app.
- */
-public class SettingsSafetyLegalActivity extends AlertActivity
- implements DialogInterface.OnCancelListener, DialogInterface.OnClickListener {
- private static final String PROPERTY_LSAFETYLEGAL_URL = "ro.url.safetylegal";
-
- private WebView mWebView;
-
- private AlertDialog mErrorDialog = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- String userSafetylegalUrl = SystemProperties.get(PROPERTY_LSAFETYLEGAL_URL);
-
- final Configuration configuration = getResources().getConfiguration();
- final String language = configuration.locale.getLanguage();
- final String country = configuration.locale.getCountry();
-
- String loc = String.format("locale=%s-%s", language, country);
-
- userSafetylegalUrl = String.format("%s&%s", userSafetylegalUrl, loc);
-
- mWebView = new WebView(this);
-
- // Begin accessing
- mWebView.getSettings().setJavaScriptEnabled(true);
- if (savedInstanceState == null) {
- mWebView.loadUrl(userSafetylegalUrl);
- } else {
- mWebView.restoreState(savedInstanceState);
- }
- mWebView.setWebViewClient(new WebViewClient() {
- @Override
- public void onPageFinished(WebView view, String url) {
- // Change from 'Loading...' to the real title
- mAlert.setTitle(getString(R.string.settings_safetylegal_activity_title));
- }
-
- @Override
- public void onReceivedError(WebView view, int errorCode,
- String description, String failingUrl) {
- showErrorAndFinish(failingUrl);
- }
- });
-
- final AlertController.AlertParams p = mAlertParams;
- p.mTitle = getString(R.string.settings_safetylegal_activity_loading);
- p.mView = mWebView;
- p.mForceInverseBackground = true;
- setupAlert();
- }
-
- private void showErrorAndFinish(String url) {
- if (mErrorDialog == null) {
- mErrorDialog = new AlertDialog.Builder(this)
- .setTitle(R.string.settings_safetylegal_activity_title)
- .setPositiveButton(android.R.string.ok, this)
- .setOnCancelListener(this)
- .setCancelable(true)
- .create();
- } else {
- if (mErrorDialog.isShowing()) {
- mErrorDialog.dismiss();
- }
- }
- mErrorDialog.setMessage(getResources()
- .getString(R.string.settings_safetylegal_activity_unreachable, url));
- mErrorDialog.show();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
-
- if (mErrorDialog != null) {
- mErrorDialog.dismiss();
- mErrorDialog = null;
- }
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
- && event.getAction() == KeyEvent.ACTION_DOWN) {
- if (mWebView.canGoBack()) {
- mWebView.goBack();
- return true;
- }
- }
- return super.dispatchKeyEvent(event);
- }
-
- public void onClick(DialogInterface dialog, int whichButton) {
- finish();
- }
-
- public void onCancel(DialogInterface dialog) {
- finish();
- }
-
- @Override
- public void onSaveInstanceState(Bundle icicle) {
- mWebView.saveState(icicle);
- super.onSaveInstanceState(icicle);
- }
-}
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index c611772..230bbb2 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -16,11 +16,7 @@
package com.android.settings;
-import com.android.settings.wifi.WifiApEnabler;
-import com.android.settings.wifi.WifiApDialog;
-
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
@@ -31,7 +27,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
@@ -44,16 +39,13 @@
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
-import android.text.TextUtils;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.webkit.WebView;
import android.widget.TextView;
-import java.io.InputStream;
+import com.android.settings.wifi.WifiApDialog;
+import com.android.settings.wifi.WifiApEnabler;
+
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.Locale;
/*
* Displays preferences for Tethering.
@@ -69,7 +61,6 @@
private static final int DIALOG_AP_SETTINGS = 1;
- private WebView mView;
private SwitchPreference mUsbTether;
private WifiApEnabler mWifiApEnabler;
@@ -182,8 +173,6 @@
mProvisionApp = getResources().getStringArray(
com.android.internal.R.array.config_mobile_hotspot_provision_app);
-
- mView = new WebView(activity);
}
@Override
diff --git a/src/com/android/settings/notification/NotificationSettings.java b/src/com/android/settings/notification/NotificationSettings.java
index 6899440..bd08d8f 100644
--- a/src/com/android/settings/notification/NotificationSettings.java
+++ b/src/com/android/settings/notification/NotificationSettings.java
@@ -16,9 +16,15 @@
package com.android.settings.notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@@ -42,7 +48,6 @@
import android.provider.Settings;
import android.util.Log;
-import android.widget.SeekBar;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
@@ -53,6 +58,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
public class NotificationSettings extends SettingsPreferenceFragment implements Indexable {
private static final String TAG = "NotificationSettings";
@@ -75,6 +81,7 @@
private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
private final H mHandler = new H();
private final SettingsObserver mSettingsObserver = new SettingsObserver();
+ private final Receiver mReceiver = new Receiver();
private Context mContext;
private PackageManager mPM;
@@ -90,6 +97,8 @@
private Preference mNotificationAccess;
private boolean mSecure;
private int mLockscreenSelectedValue;
+ private ComponentName mSuppressor;
+ private int mRingOrNotificationProgress;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -128,6 +137,7 @@
mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
refreshNotificationListeners();
+ updateEffectsSuppressor();
}
@Override
@@ -136,6 +146,8 @@
refreshNotificationListeners();
lookupRingtoneNames();
mSettingsObserver.register(true);
+ mReceiver.register(true);
+ updateEffectsSuppressor();
}
@Override
@@ -143,6 +155,7 @@
super.onPause();
mVolumeCallback.stopSample();
mSettingsObserver.register(false);
+ mReceiver.register(false);
}
// === Volumes ===
@@ -154,12 +167,46 @@
return volumePref;
}
- private void updateRingOrNotificationIcon(int progress) {
- mRingOrNotificationPreference.showIcon(progress > 0
- ? R.drawable.ring_notif
- : (mVibrator == null
- ? R.drawable.ring_notif_mute
- : R.drawable.ring_notif_vibrate));
+ private void updateRingOrNotificationIcon() {
+ mRingOrNotificationPreference.showIcon(mSuppressor != null
+ ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
+ : mRingOrNotificationProgress > 0
+ ? com.android.internal.R.drawable.ic_audio_ring_notif
+ : (mVibrator == null
+ ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
+ : com.android.internal.R.drawable.ic_audio_ring_notif_vibrate));
+ }
+
+ private void updateEffectsSuppressor() {
+ final ComponentName suppressor = NotificationManager.from(mContext).getEffectsSuppressor();
+ if (Objects.equals(suppressor, mSuppressor)) return;
+ mSuppressor = suppressor;
+ if (mRingOrNotificationPreference != null) {
+ final String text = suppressor != null ?
+ mContext.getString(com.android.internal.R.string.muted_by,
+ getSuppressorCaption(suppressor)) : null;
+ mRingOrNotificationPreference.setSuppressionText(text);
+ }
+ updateRingOrNotificationIcon();
+ }
+
+ private String getSuppressorCaption(ComponentName suppressor) {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
+ if (info != null) {
+ final CharSequence seq = info.loadLabel(pm);
+ if (seq != null) {
+ final String str = seq.toString().trim();
+ if (str.length() > 0) {
+ return str;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ Log.w(TAG, "Error loading suppressor caption", e);
+ }
+ return suppressor.getPackageName();
}
private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
@@ -464,6 +511,7 @@
private static final int UPDATE_NOTIFICATION_RINGTONE = 2;
private static final int STOP_SAMPLE = 3;
private static final int UPDATE_RINGER_ICON = 4;
+ private static final int UPDATE_EFFECTS_SUPPRESSOR = 5;
private H() {
super(Looper.getMainLooper());
@@ -482,12 +530,36 @@
mVolumeCallback.stopSample();
break;
case UPDATE_RINGER_ICON:
- updateRingOrNotificationIcon(msg.arg1);
+ mRingOrNotificationProgress = msg.arg1;
+ updateRingOrNotificationIcon();
+ break;
+ case UPDATE_EFFECTS_SUPPRESSOR:
+ updateEffectsSuppressor();
break;
}
}
}
+ private class Receiver extends BroadcastReceiver {
+ private boolean mRegistered;
+
+ public void register(boolean register) {
+ if (mRegistered == register) return;
+ if (register) {
+ mContext.registerReceiver(this,
+ new IntentFilter(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED));
+ } else {
+ mContext.unregisterReceiver(this);
+ }
+ mRegistered = register;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.sendEmptyMessage(H.UPDATE_EFFECTS_SUPPRESSOR);
+ }
+ }
+
// === Indexing ===
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java
index f391f86..0fdcc19 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreference.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java
@@ -23,14 +23,18 @@
import android.preference.PreferenceManager;
import android.preference.SeekBarPreference;
import android.preference.SeekBarVolumizer;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
+import android.widget.TextView;
import com.android.settings.R;
+import java.util.Objects;
+
/** A slider preference that directly controls an audio stream volume (no dialog) **/
public class VolumeSeekBarPreference extends SeekBarPreference
implements PreferenceManager.OnActivityStopListener {
@@ -41,10 +45,13 @@
private SeekBarVolumizer mVolumizer;
private Callback mCallback;
private ImageView mIconView;
+ private TextView mSuppressionTextView;
+ private String mSuppressionText;
public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ setLayoutResource(R.layout.preference_volume_slider);
}
public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -108,13 +115,14 @@
mVolumizer.start();
mVolumizer.setSeekBar(mSeekBar);
mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+ mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());
+ updateSuppressionText();
}
// during initialization, this preference is the SeekBar listener
@Override
- public void onProgressChanged(SeekBar seekBar, int progress,
- boolean fromTouch) {
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
super.onProgressChanged(seekBar, progress, fromTouch);
mCallback.onStreamValueChanged(mStream, progress);
}
@@ -133,6 +141,21 @@
+ "/" + R.raw.media_volume);
}
+ public void setSuppressionText(String text) {
+ if (Objects.equals(text, mSuppressionText)) return;
+ mSuppressionText = text;
+ updateSuppressionText();
+ }
+
+ private void updateSuppressionText() {
+ if (mSuppressionTextView != null && mSeekBar != null) {
+ mSuppressionTextView.setText(mSuppressionText);
+ final boolean showSuppression = !TextUtils.isEmpty(mSuppressionText);
+ mSuppressionTextView.setVisibility(showSuppression ? View.VISIBLE : View.INVISIBLE);
+ mSeekBar.setVisibility(showSuppression ? View.INVISIBLE : View.VISIBLE);
+ }
+ }
+
public interface Callback {
void onSampleStarting(SeekBarVolumizer sbv);
void onStreamValueChanged(int stream, int progress);