Merge "Import translations. DO NOT MERGE" into lmp-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 07b3633..21525bc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -813,14 +813,12 @@
*/
public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
-
/**
- * {@link #extras} key: Bitmap representing the profile badge to be shown with the
- * notification.
+ * {@link #extras} key: the user that built the notification.
*
* @hide
*/
- public static final String EXTRA_PROFILE_BADGE = "android.profileBadge";
+ public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
/**
* Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
@@ -1870,7 +1868,11 @@
private final NotificationColorUtil mColorUtil;
private ArrayList<String> mPeople;
private int mColor = COLOR_DEFAULT;
- private Bitmap mProfileBadge;
+
+ /**
+ * The user that built the notification originally.
+ */
+ private int mOriginatingUserId;
/**
* Contains extras related to rebuilding during the build phase.
@@ -2579,8 +2581,10 @@
}
private Bitmap getProfileBadge() {
+ // Note: This assumes that the current user can read the profile badge of the
+ // originating user.
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- Drawable badge = userManager.getBadgeForUser(android.os.Process.myUserHandle());
+ Drawable badge = userManager.getBadgeForUser(new UserHandle(mOriginatingUserId));
if (badge == null) {
return null;
}
@@ -2594,6 +2598,7 @@
}
private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
+ Bitmap profileIcon = getProfileBadge();
RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(), resId);
boolean showLine3 = false;
boolean showLine2 = false;
@@ -2601,8 +2606,8 @@
if (mPriority < PRIORITY_LOW) {
// TODO: Low priority presentation
}
- if (mProfileBadge != null) {
- contentView.setImageViewBitmap(R.id.profile_icon, mProfileBadge);
+ if (profileIcon != null) {
+ contentView.setImageViewBitmap(R.id.profile_icon, profileIcon);
contentView.setViewVisibility(R.id.profile_icon, View.VISIBLE);
} else {
contentView.setViewVisibility(R.id.profile_icon, View.GONE);
@@ -2927,6 +2932,7 @@
*/
public void populateExtras(Bundle extras) {
// Store original information used in the construction of this object
+ extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
extras.putString(EXTRA_REBUILD_CONTEXT_PACKAGE, mContext.getPackageName());
extras.putCharSequence(EXTRA_TITLE, mContentTitle);
extras.putCharSequence(EXTRA_TEXT, mContentText);
@@ -2944,9 +2950,6 @@
if (!mPeople.isEmpty()) {
extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
}
- if (mProfileBadge != null) {
- extras.putParcelable(EXTRA_PROFILE_BADGE, mProfileBadge);
- }
// NOTE: If you're adding new extras also update restoreFromNotification().
}
@@ -3157,6 +3160,7 @@
// Extras.
Bundle extras = n.extras;
+ mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID);
mContentTitle = extras.getCharSequence(EXTRA_TITLE);
mContentText = extras.getCharSequence(EXTRA_TEXT);
mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
@@ -3174,9 +3178,6 @@
mPeople.clear();
Collections.addAll(mPeople, extras.getStringArray(EXTRA_PEOPLE));
}
- if (extras.containsKey(EXTRA_PROFILE_BADGE)) {
- mProfileBadge = extras.getParcelable(EXTRA_PROFILE_BADGE);
- }
}
/**
@@ -3192,7 +3193,7 @@
* object.
*/
public Notification build() {
- mProfileBadge = getProfileBadge();
+ mOriginatingUserId = mContext.getUserId();
Notification n = buildUnstyled();
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 969c0db..7901379 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -35,6 +35,8 @@
void setHardKeyboardStatus(boolean available, boolean enabled);
void setWindowState(int window, int state);
void buzzBeepBlinked();
+ void notificationLightOff();
+ void notificationLightPulse(int argb, int millisOn, int millisOff);
void showRecentApps(boolean triggeredFromAltTab);
void hideRecentApps(boolean triggeredFromAltTab);
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 52a018f..581995a 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -35,7 +35,7 @@
<string name="generating_print_job" msgid="3119608742651698916">"प्रिंट कार्य जनरेट हो रहा है"</string>
<string name="save_as_pdf" msgid="5718454119847596853">"PDF के रूप में सहेजें"</string>
<string name="all_printers" msgid="5018829726861876202">"सभी प्रिंटर..."</string>
- <string name="print_dialog" msgid="32628687461331979">"प्रिंट संवाद"</string>
+ <string name="print_dialog" msgid="32628687461331979">"प्रिंट डॉयलॉग"</string>
<string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
<string name="search" msgid="5421724265322228497">"खोजें"</string>
<string name="all_printers_label" msgid="3178848870161526399">"सभी प्रिंटर"</string>
diff --git a/packages/SystemUI/res/drawable/ic_qs_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_vpn.xml
new file mode 100644
index 0000000..e9141ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_vpn.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="12.0dp"
+ android:height="12.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+
+ <path
+ android:fillColor="#4DFFFFFF"
+ android:pathData="M22.000000,4.000000L22.000000,3.500000C22.000000,2.100000 20.900000,1.000000 19.500000,1.000000C18.100000,1.000000 17.000000,2.100000 17.000000,3.500000L17.000000,4.000000c-0.600000,0.000000 -1.000000,0.400000 -1.000000,1.000000l0.000000,4.000000c0.000000,0.600000 0.400000,1.000000 1.000000,1.000000l5.000000,0.000000c0.600000,0.000000 1.000000,-0.400000 1.000000,-1.000000L23.000000,5.000000C23.000000,4.400000 22.600000,4.000000 22.000000,4.000000zM21.200001,4.000000l-3.400000,0.000000L17.800001,3.500000c0.000000,-0.900000 0.800000,-1.700000 1.700000,-1.700000c0.900000,0.000000 1.700000,0.800000 1.700000,1.700000L21.200003,4.000000zM18.900000,12.000000c0.000000,0.300000 0.100000,0.700000 0.100000,1.000000c0.000000,2.100000 -0.800000,4.000000 -2.100000,5.400000c-0.300000,-0.800000 -1.000000,-1.400000 -1.900000,-1.400000l-1.000000,0.000000l0.000000,-3.000000c0.000000,-0.600000 -0.400000,-1.000000 -1.000000,-1.000000L7.000000,13.000000l0.000000,-2.000000l2.000000,0.000000c0.600000,0.000000 1.000000,-0.400000 1.000000,-1.000000L10.000000,8.000000l2.000000,0.000000c1.100000,0.000000 2.000000,-0.900000 2.000000,-2.000000L14.000000,3.500000C13.100000,3.200000 12.000000,3.000000 11.000000,3.000000C5.500000,3.000000 1.000000,7.500000 1.000000,13.000000c0.000000,5.500000 4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000c0.000000,-0.300000 0.000000,-0.700000 -0.100000,-1.000000L18.900000,12.000000zM10.000000,20.900000c-3.900000,-0.500000 -7.000000,-3.900000 -7.000000,-7.900000c0.000000,-0.600000 0.100000,-1.200000 0.200000,-1.800000L8.000000,16.000000l0.000000,1.000000c0.000000,1.100000 0.900000,2.000000 2.000000,2.000000L10.000000,20.900000z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
new file mode 100644
index 0000000..53baf74
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:paddingBottom="@dimen/qs_tile_padding_top"
+ android:paddingTop="@dimen/qs_tile_padding_top" >
+
+ <TextView
+ android:id="@+id/footer_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:textSize="@dimen/qs_tile_text_size" />
+
+ <ImageView
+ android:id="@+id/footer_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="8dp"
+ android:layout_toStartOf="@id/footer_text"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_qs_vpn"
+ android:visibility="invisible" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 230f4af..a4576bb 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -162,11 +162,17 @@
<!-- Doze: does this device support STATE_DOZE and STATE_DOZE_SUSPEND? -->
<bool name="doze_display_state_supported">false</bool>
- <!-- Doze: should the significant motion sensor be used as a tease signal? -->
- <bool name="doze_tease_on_significant_motion">false</bool>
+ <!-- Doze: should the significant motion sensor be used as a pulse signal? -->
+ <bool name="doze_pulse_on_significant_motion">false</bool>
- <!-- Doze: maximum brightness to use when teasing -->
- <integer name="doze_tease_brightness">80</integer>
+ <!-- Doze: maximum brightness to use when pulsing -->
+ <integer name="doze_pulse_brightness">40</integer>
+
+ <!-- Doze: number of pulses when doing multiple pulses in quick succession -->
+ <integer name="doze_multipulse_count">3</integer>
+
+ <!-- Doze: interval between pulses when following the notification light -->
+ <integer name="doze_notification_pulse_interval">30000</integer>
<!-- Volume: time to delay dismissing the volume panel after a click is performed -->
<integer name="volume_panel_dismiss_delay">200</integer>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 296cdad..83a9a81 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -741,4 +741,38 @@
<!-- Text which is shown in the notification shade when there are no notifications. [CHAR LIMIT=30] -->
<string name="empty_shade_text">No notifications</string>
+
+ <!-- Footer device owned text [CHAR LIMIT=50] -->
+ <string name="device_owned_footer">Device may be monitored</string>
+
+ <!-- Footer vpn present text [CHAR LIMIT=50] -->
+ <string name="vpn_footer">Network may be monitored</string>
+
+ <!-- Monitoring dialog title for device owned devices [CHAR LIMIT=35] -->
+ <string name="monitoring_title_device_owned">Device monitoring</string>
+
+ <!-- Monitoring dialog title for normal devices [CHAR LIMIT=35]-->
+ <string name="monitoring_title">Network monitoring</string>
+
+ <!-- Monitoring dialog open app button [CHAR LIMIT=30] -->
+ <string name="open_app">Open app</string>
+
+ <!-- Monitoring dialog disconnect vpn button [CHAR LIMIT=30] -->
+ <string name="disconnect_vpn">Disconnect VPN</string>
+
+ <!-- Monitoring dialog device owner body text [CHAR LIMIT=300] -->
+ <string name="monitoring_description_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator can monitor your network activity, including emails, apps and secure websites.\n\nFor more information, contact your administrator.</string>
+
+ <!-- Monitoring dialog non-legacy VPN text [CHAR LIMIT=300] -->
+ <string name="monitoring_description_vpn">You gave \"<xliff:g id="application">%1$s</xliff:g>\" permission to set up a VPN connection.\n\nThis app can monitor your network activity, including emails, apps and secure websites.</string>
+
+ <!-- Monitoring dialog legacy VPN text [CHAR LIMIT=300] -->
+ <string name="monitoring_description_legacy_vpn">You\'re connected to a VPN (\"<xliff:g id="application">%1$s</xliff:g>\").\n\nYour VPN service provider can monitor your network activity including emails, apps, and secure websites.</string>
+
+ <!-- Monitoring dialog non-legacy VPN with device owner text [CHAR LIMIT=300] -->
+ <string name="monitoring_description_vpn_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator is capable of monitoring your network activity including emails, apps, and secure websites. For more information, contact your administrator.\n\nAlso, you gave \"<xliff:g id="application">%2$s</xliff:g>\" permission to set up a VPN connection. This app can monitor network activity too.</string>
+
+ <!-- Monitoring dialog legacy VPN with device owner text [CHAR LIMIT=300] -->
+ <string name="monitoring_description_legacy_vpn_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator is capable of monitoring your network activity including emails, apps, and secure websites. For more information, contact your administrator.\n\nAlso, you\'re connected to a VPN (\"<xliff:g id="application">%2$s</xliff:g>\"). Your VPN service provider can monitor network activity too.</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 13c15f5..943a294 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -19,6 +19,8 @@
import static android.os.PowerManager.BRIGHTNESS_OFF;
import static android.os.PowerManager.BRIGHTNESS_ON;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -42,12 +44,16 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Date;
public class DozeService extends DreamService {
private static final String TAG = "DozeService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String TEASE_ACTION = "com.android.systemui.doze.tease";
+ private static final String ACTION_BASE = "com.android.systemui.doze";
+ private static final String PULSE_ACTION = ACTION_BASE + ".pulse";
+ private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse";
+ private static final String EXTRA_PULSES = "pulses";
private final String mTag = String.format(TAG + ".%08x", hashCode());
private final Context mContext = this;
@@ -58,13 +64,18 @@
private Sensor mSigMotionSensor;
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
+ private AlarmManager mAlarmManager;
private int mMaxBrightness;
private boolean mDreaming;
- private boolean mTeaseReceiverRegistered;
+ private boolean mBroadcastReceiverRegistered;
private boolean mSigMotionConfigured;
private boolean mSigMotionEnabled;
private boolean mDisplayStateSupported;
private int mDisplayStateWhenOn;
+ private boolean mNotificationLightOn;
+ private PendingIntent mNotificationPulseIntent;
+ private int mMultipulseCount;
+ private int mNotificationPulseInterval;
public DozeService() {
if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -75,12 +86,15 @@
protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dumpOnHandler(fd, pw, args);
pw.print(" mDreaming: "); pw.println(mDreaming);
- pw.print(" mTeaseReceiverRegistered: "); pw.println(mTeaseReceiverRegistered);
+ pw.print(" mBroadcastReceiverRegistered: "); pw.println(mBroadcastReceiverRegistered);
pw.print(" mSigMotionSensor: "); pw.println(mSigMotionSensor);
pw.print(" mSigMotionConfigured: "); pw.println(mSigMotionConfigured);
pw.print(" mSigMotionEnabled: "); pw.println(mSigMotionEnabled);
pw.print(" mMaxBrightness: "); pw.println(mMaxBrightness);
pw.print(" mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
+ pw.print(" mNotificationLightOn: "); pw.println(mNotificationLightOn);
+ pw.print(" mMultipulseCount: "); pw.println(mMultipulseCount);
+ pw.print(" mNotificationPulseInterval: "); pw.println(mNotificationPulseInterval);
}
@Override
@@ -99,14 +113,21 @@
mSigMotionSensor = mSensors.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
final Resources res = mContext.getResources();
- mSigMotionConfigured = SystemProperties.getBoolean("doze.tease.sigmotion",
- res.getBoolean(R.bool.doze_tease_on_significant_motion));
+ mSigMotionConfigured = SystemProperties.getBoolean("doze.pulse.sigmotion",
+ res.getBoolean(R.bool.doze_pulse_on_significant_motion));
mDisplayStateSupported = SystemProperties.getBoolean("doze.display.supported",
res.getBoolean(R.bool.doze_display_state_supported));
- mMaxBrightness = MathUtils.constrain(res.getInteger(R.integer.doze_tease_brightness),
+ mMaxBrightness = MathUtils.constrain(res.getInteger(R.integer.doze_pulse_brightness),
BRIGHTNESS_OFF, BRIGHTNESS_ON);
-
+ mNotificationPulseIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(NOTIFICATION_PULSE_ACTION).setPackage(getPackageName()),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ mMultipulseCount = SystemProperties.getInt("doze.multipulses",
+ res.getInteger(R.integer.doze_multipulse_count));
+ mNotificationPulseInterval = SystemProperties.getInt("doze.notification.pulse",
+ res.getInteger(R.integer.doze_notification_pulse_interval));
mDisplayStateWhenOn = mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON;
setDozeScreenState(mDisplayStateWhenOn);
}
@@ -122,7 +143,7 @@
super.onDreamingStarted();
if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze());
mDreaming = true;
- listenForTeaseSignals(true);
+ listenForPulseSignals(true);
requestDoze();
}
@@ -160,7 +181,7 @@
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
- listenForTeaseSignals(false);
+ listenForPulseSignals(false);
stopDozing();
dozingStopped();
}
@@ -187,9 +208,17 @@
}
}
- private void requestTease() {
+ private void requestMultipulse() {
+ requestPulse(mMultipulseCount);
+ }
+
+ private void requestPulse() {
+ requestPulse(1);
+ }
+
+ private void requestPulse(int pulses) {
if (mHost != null) {
- mHost.requestTease(this);
+ mHost.requestPulse(pulses, this);
}
}
@@ -199,10 +228,10 @@
}
}
- private void listenForTeaseSignals(boolean listen) {
- if (DEBUG) Log.d(mTag, "listenForTeaseSignals: " + listen);
+ private void listenForPulseSignals(boolean listen) {
+ if (DEBUG) Log.d(mTag, "listenForPulseSignals: " + listen);
listenForSignificantMotion(listen);
- listenForBroadcast(listen);
+ listenForBroadcasts(listen);
listenForNotifications(listen);
}
@@ -216,15 +245,17 @@
}
}
- private void listenForBroadcast(boolean listen) {
+ private void listenForBroadcasts(boolean listen) {
if (listen) {
- mContext.registerReceiver(mTeaseReceiver, new IntentFilter(TEASE_ACTION));
- mTeaseReceiverRegistered = true;
+ final IntentFilter filter = new IntentFilter(PULSE_ACTION);
+ filter.addAction(NOTIFICATION_PULSE_ACTION);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ mBroadcastReceiverRegistered = true;
} else {
- if (mTeaseReceiverRegistered) {
- mContext.unregisterReceiver(mTeaseReceiver);
+ if (mBroadcastReceiverRegistered) {
+ mContext.unregisterReceiver(mBroadcastReceiver);
}
- mTeaseReceiverRegistered = false;
+ mBroadcastReceiverRegistered = false;
}
}
@@ -237,6 +268,15 @@
}
}
+ private void rescheduleNotificationPulse() {
+ mAlarmManager.cancel(mNotificationPulseIntent);
+ if (mNotificationLightOn) {
+ final long time = System.currentTimeMillis() + mNotificationPulseInterval;
+ if (DEBUG) Log.d(TAG, "Scheduling pulse for " + new Date(time));
+ mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, mNotificationPulseIntent);
+ }
+ }
+
private static String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("TriggerEvent[")
@@ -269,16 +309,23 @@
v.vibrate(1000);
}
}
- requestTease();
+ requestPulse();
listenForSignificantMotion(true); // reregister, this sensor only fires once
}
};
- private final BroadcastReceiver mTeaseReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (DEBUG) Log.d(mTag, "Received tease intent");
- requestTease();
+ if (PULSE_ACTION.equals(intent.getAction())) {
+ if (DEBUG) Log.d(mTag, "Received pulse intent");
+ requestPulse(intent.getIntExtra(EXTRA_PULSES, mMultipulseCount));
+ }
+ if (NOTIFICATION_PULSE_ACTION.equals(intent.getAction())) {
+ if (DEBUG) Log.d(mTag, "Received notification pulse intent");
+ requestPulse();
+ rescheduleNotificationPulse();
+ }
}
};
@@ -288,10 +335,19 @@
if (DEBUG) Log.d(mTag, "onNewNotifications");
// noop for now
}
+
@Override
public void onBuzzBeepBlinked() {
if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
- requestTease();
+ requestMultipulse();
+ }
+
+ @Override
+ public void onNotificationLight(boolean on) {
+ if (DEBUG) Log.d(mTag, "onNotificationLight on=" + on);
+ if (mNotificationLightOn == on) return;
+ mNotificationLightOn = on;
+ rescheduleNotificationPulse();
}
};
@@ -299,12 +355,13 @@
void addCallback(Callback callback);
void removeCallback(Callback callback);
void requestDoze(DozeService dozeService);
- void requestTease(DozeService dozeService);
+ void requestPulse(int pulses, DozeService dozeService);
void dozingStopped(DozeService dozeService);
public interface Callback {
void onNewNotifications();
void onBuzzBeepBlinked();
+ void onNotificationLight(boolean on);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
new file mode 100644
index 0000000..f04a7b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+package com.android.systemui.qs;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityController.VpnCallback;
+
+public class QSFooter implements OnClickListener, DialogInterface.OnClickListener {
+ protected static final String TAG = "QSFooter";
+ protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final View mRootView;
+ private final TextView mFooterText;
+ private final ImageView mFooterIcon;
+ private final Context mContext;
+ private final Callback mCallback = new Callback();
+
+ private SecurityController mSecurityController;
+ private AlertDialog mDialog;
+ private QSTileHost mHost;
+ private Handler mHandler;
+
+ public QSFooter(QSPanel qsPanel, Context context) {
+ mRootView = LayoutInflater.from(context)
+ .inflate(R.layout.quick_settings_footer, qsPanel, false);
+ mRootView.setOnClickListener(this);
+ mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
+ mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
+ mContext = context;
+ }
+
+ public void setHost(QSTileHost host) {
+ mHost = host;
+ mSecurityController = host.getSecurityController();
+ mHandler = new H(host.getLooper());
+ }
+
+ public void setListening(boolean listening) {
+ if (listening) {
+ mSecurityController.addCallback(mCallback);
+ } else {
+ mSecurityController.removeCallback(mCallback);
+ }
+ }
+
+ public View getView() {
+ return mRootView;
+ }
+
+ public boolean hasFooter() {
+ return mRootView.getVisibility() != View.GONE;
+ }
+
+ @Override
+ public void onClick(View v) {
+ mHandler.sendEmptyMessage(H.CLICK);
+ }
+
+ private void handleClick() {
+ mHost.collapsePanels();
+ // TODO: Delay dialog creation until after panels are collapsed.
+ createDialog();
+ }
+
+ public void refreshState() {
+ mHandler.sendEmptyMessage(H.REFRESH_STATE);
+ }
+
+ private void handleRefreshState() {
+ if (mSecurityController.hasDeviceOwner()) {
+ mFooterText.setText(R.string.device_owned_footer);
+ mRootView.setVisibility(View.VISIBLE);
+ mFooterIcon.setVisibility(View.INVISIBLE);
+ } else if (mSecurityController.isVpnEnabled()) {
+ mFooterText.setText(R.string.vpn_footer);
+ mRootView.setVisibility(View.VISIBLE);
+ mFooterIcon.setVisibility(View.VISIBLE);
+ } else {
+ mRootView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ if (mSecurityController.isLegacyVpn()) {
+ mSecurityController.disconnectFromLegacyVpn();
+ } else {
+ mSecurityController.openVpnApp();
+ }
+ }
+ }
+
+ private void createDialog() {
+ mDialog = new SystemUIDialog(mContext);
+ mDialog.setTitle(getTitle());
+ mDialog.setMessage(getMessage());
+ mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
+ if (mSecurityController.isVpnEnabled()) {
+ mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);
+ }
+ mDialog.show();
+ }
+
+ private String getNegativeButton() {
+ if (mSecurityController.isLegacyVpn()) {
+ return mContext.getString(R.string.disconnect_vpn);
+ } else {
+ return mContext.getString(R.string.open_app);
+ }
+ }
+
+ private String getPositiveButton() {
+ return mContext.getString(R.string.quick_settings_done);
+ }
+
+ private String getMessage() {
+ if (mSecurityController.hasDeviceOwner()) {
+ if (mSecurityController.isVpnEnabled()) {
+ if (mSecurityController.isLegacyVpn()) {
+ return mContext.getString(
+ R.string.monitoring_description_legacy_vpn_device_owned,
+ mSecurityController.getDeviceOwnerName(),
+ mSecurityController.getLegacyVpnName());
+ } else {
+ return mContext.getString(R.string.monitoring_description_vpn_device_owned,
+ mSecurityController.getDeviceOwnerName(),
+ mSecurityController.getVpnApp());
+ }
+ } else {
+ return mContext.getString(R.string.monitoring_description_device_owned,
+ mSecurityController.getDeviceOwnerName());
+ }
+ } else {
+ if (mSecurityController.isLegacyVpn()) {
+ return mContext.getString(R.string.monitoring_description_legacy_vpn,
+ mSecurityController.getLegacyVpnName());
+
+ } else {
+ return mContext.getString(R.string.monitoring_description_vpn,
+ mSecurityController.getVpnApp());
+ }
+ }
+ }
+
+ private int getTitle() {
+ if (mSecurityController.hasDeviceOwner()) {
+ return R.string.monitoring_title_device_owned;
+ }
+ return R.string.monitoring_title;
+ }
+
+ private class Callback implements VpnCallback {
+ @Override
+ public void onVpnStateChanged() {
+ refreshState();
+ }
+ }
+
+ private class H extends Handler {
+ private static final int CLICK = 0;
+ private static final int REFRESH_STATE = 1;
+
+ private H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ String name = null;
+ try {
+ if (msg.what == REFRESH_STATE) {
+ name = "handleRefreshState";
+ handleRefreshState();
+ } else if (msg.what == CLICK) {
+ name = "handleClick";
+ handleClick();
+ }
+ } catch (Throwable t) {
+ final String error = "Error in " + name;
+ Log.w(TAG, error, t);
+ mHost.warn(error, t);
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 1173053..59f3b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -67,6 +67,8 @@
private BrightnessController mBrightnessController;
private QSTileHost mHost;
+ private QSFooter mFooter;
+
public QSPanel(Context context) {
this(context, null);
}
@@ -83,8 +85,10 @@
mDetail.setClickable(true);
mBrightnessView = LayoutInflater.from(context).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
+ mFooter = new QSFooter(this, context);
addView(mDetail);
addView(mBrightnessView);
+ addView(mFooter.getView());
mClipper = new QSDetailClipper(mDetail);
updateResources();
@@ -106,6 +110,7 @@
public void setHost(QSTileHost host) {
mHost = host;
+ mFooter.setHost(host);
}
public QSTileHost getHost() {
@@ -144,6 +149,7 @@
for (TileRecord r : mRecords) {
r.tile.setListening(mListening);
}
+ mFooter.setListening(mListening);
if (mListening) {
refreshAllTiles();
}
@@ -158,6 +164,7 @@
for (TileRecord r : mRecords) {
r.tile.refreshState();
}
+ mFooter.refreshState();
}
public void showDetailAdapter(boolean show, DetailAdapter adapter) {
@@ -287,6 +294,7 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
mBrightnessView.measure(exactly(width), MeasureSpec.UNSPECIFIED);
+ mFooter.getView().measure(exactly(width), MeasureSpec.UNSPECIFIED);
int r = -1;
int c = -1;
int rows = 0;
@@ -315,6 +323,9 @@
record.tileView.measure(exactly(cw), exactly(ch));
}
int h = rows == 0 ? mBrightnessView.getHeight() : (getRowTop(rows) + mPanelPaddingBottom);
+ if (mFooter.hasFooter()) {
+ h += mFooter.getView().getHeight();
+ }
mDetail.measure(exactly(width), exactly(h));
setMeasuredDimension(width, h);
}
@@ -341,6 +352,11 @@
}
final int dh = Math.max(mDetail.getMeasuredHeight(), getMeasuredHeight());
mDetail.layout(0, 0, mDetail.getMeasuredWidth(), dh);
+ if (mFooter.hasFooter()) {
+ View footer = mFooter.getView();
+ footer.layout(0, getMeasuredHeight() - footer.getMeasuredHeight(),
+ footer.getMeasuredWidth(), getMeasuredHeight());
+ }
}
private int getRowTop(int row) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index a82c907..9107790 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -55,6 +55,8 @@
private static final int MSG_SHOW_RECENT_APPS = 14 << MSG_SHIFT;
private static final int MSG_HIDE_RECENT_APPS = 15 << MSG_SHIFT;
private static final int MSG_BUZZ_BEEP_BLINKED = 16 << MSG_SHIFT;
+ private static final int MSG_NOTIFICATION_LIGHT_OFF = 17 << MSG_SHIFT;
+ private static final int MSG_NOTIFICATION_LIGHT_PULSE = 18 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -95,6 +97,8 @@
public void hideSearchPanel();
public void setWindowState(int window, int state);
public void buzzBeepBlinked();
+ public void notificationLightOff();
+ public void notificationLightPulse(int argb, int onMillis, int offMillis);
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -230,6 +234,19 @@
}
}
+ public void notificationLightOff() {
+ synchronized (mList) {
+ mHandler.sendEmptyMessage(MSG_NOTIFICATION_LIGHT_OFF);
+ }
+ }
+
+ public void notificationLightPulse(int argb, int onMillis, int offMillis) {
+ synchronized (mList) {
+ mHandler.obtainMessage(MSG_NOTIFICATION_LIGHT_PULSE, onMillis, offMillis, argb)
+ .sendToTarget();
+ }
+ }
+
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
@@ -306,7 +323,12 @@
case MSG_BUZZ_BEEP_BLINKED:
mCallbacks.buzzBeepBlinked();
break;
-
+ case MSG_NOTIFICATION_LIGHT_OFF:
+ mCallbacks.notificationLightOff();
+ break;
+ case MSG_NOTIFICATION_LIGHT_PULSE:
+ mCallbacks.notificationLightPulse((Integer) msg.obj, msg.arg1, msg.arg2);
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index fe9ea50..99b0259 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -22,7 +22,6 @@
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
-import static com.android.keyguard.KeyguardHostView.OnDismissAction;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
@@ -106,6 +105,7 @@
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DemoMode;
import com.android.systemui.EventLogTags;
@@ -129,20 +129,21 @@
import com.android.systemui.statusbar.SpeedBumpView;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -212,6 +213,7 @@
// These are no longer handled by the policy, because we need custom strategies for them
BluetoothControllerImpl mBluetoothController;
+ SecurityControllerImpl mSecurityController;
BatteryController mBatteryController;
LocationControllerImpl mLocationController;
NetworkControllerImpl mNetworkController;
@@ -736,6 +738,7 @@
mNetworkController = new NetworkControllerImpl(mContext);
mHotspotController = new HotspotControllerImpl(mContext);
mBluetoothController = new BluetoothControllerImpl(mContext);
+ mSecurityController = new SecurityControllerImpl(mContext);
if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
mRotationLockController = new RotationLockControllerImpl(mContext);
}
@@ -797,7 +800,8 @@
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, mHotspotController,
mCastController, mFlashlightController,
- mUserSwitcherController, mKeyguardMonitor);
+ mUserSwitcherController, mKeyguardMonitor,
+ mSecurityController);
mQSPanel.setHost(qsh);
for (QSTile<?> tile : qsh.getTiles()) {
mQSPanel.addTile(tile);
@@ -2332,6 +2336,20 @@
}
}
+ @Override
+ public void notificationLightOff() {
+ if (mDozeServiceHost != null) {
+ mDozeServiceHost.fireNotificationLight(false);
+ }
+ }
+
+ @Override
+ public void notificationLightPulse(int argb, int onMillis, int offMillis) {
+ if (mDozeServiceHost != null) {
+ mDozeServiceHost.fireNotificationLight(true);
+ }
+ }
+
@Override // CommandQueue
public void setSystemUiVisibility(int vis, int mask) {
final int oldVal = mSystemUiVisibility;
@@ -2776,6 +2794,9 @@
if (mNextAlarmController != null) {
mNextAlarmController.dump(fd, pw, args);
}
+ if (mSecurityController != null) {
+ mSecurityController.dump(fd, pw, args);
+ }
}
private String hunStateToString(Entry entry) {
@@ -3792,6 +3813,12 @@
}
}
+ public void fireNotificationLight(boolean on) {
+ for (Callback callback : mCallbacks) {
+ callback.onNotificationLight(on);
+ }
+ }
+
public void fireNewNotifications() {
for (Callback callback : mCallbacks) {
callback.onNewNotifications();
@@ -3816,10 +3843,10 @@
}
@Override
- public void requestTease(DozeService dozeService) {
+ public void requestPulse(int pulses, DozeService dozeService) {
if (dozeService == null) return;
dozeService.stayAwake(PROCESSING_TIME);
- mHandler.obtainMessage(H.REQUEST_TEASE, dozeService).sendToTarget();
+ mHandler.obtainMessage(H.REQUEST_PULSE, pulses, 0, dozeService).sendToTarget();
}
@Override
@@ -3838,9 +3865,9 @@
mCurrentDozeService.startDozing();
}
- private void handleRequestTease(DozeService dozeService) {
+ private void handleRequestPulse(int pulses, DozeService dozeService) {
if (!dozeService.equals(mCurrentDozeService)) return;
- final long stayAwake = mScrimController.tease();
+ final long stayAwake = mScrimController.pulse(pulses);
mCurrentDozeService.stayAwake(stayAwake);
}
@@ -3856,15 +3883,15 @@
private final class H extends Handler {
private static final int REQUEST_DOZE = 1;
- private static final int REQUEST_TEASE = 2;
+ private static final int REQUEST_PULSE = 2;
private static final int DOZING_STOPPED = 3;
@Override
public void handleMessage(Message msg) {
if (msg.what == REQUEST_DOZE) {
handleRequestDoze((DozeService) msg.obj);
- } else if (msg.what == REQUEST_TEASE) {
- handleRequestTease((DozeService) msg.obj);
+ } else if (msg.what == REQUEST_PULSE) {
+ handleRequestPulse(msg.arg1, (DozeService) msg.obj);
} else if (msg.what == DOZING_STOPPED) {
handleDozingStopped((DozeService) msg.obj);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index c2fd24c..8f25fb97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -65,13 +66,15 @@
private final FlashlightController mFlashlight;
private final UserSwitcherController mUserSwitcherController;
private final KeyguardMonitor mKeyguard;
+ private final SecurityController mSecurity;
public QSTileHost(Context context, PhoneStatusBar statusBar,
BluetoothController bluetooth, LocationController location,
RotationLockController rotation, NetworkController network,
ZenModeController zen, HotspotController hotspot,
CastController cast, FlashlightController flashlight,
- UserSwitcherController userSwitcher, KeyguardMonitor keyguard) {
+ UserSwitcherController userSwitcher, KeyguardMonitor keyguard,
+ SecurityController security) {
mContext = context;
mStatusBar = statusBar;
mBluetooth = bluetooth;
@@ -84,6 +87,7 @@
mFlashlight = flashlight;
mUserSwitcherController = userSwitcher;
mKeyguard = keyguard;
+ mSecurity = security;
final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
ht.start();
@@ -189,4 +193,8 @@
public UserSwitcherController getUserSwitcherController() {
return mUserSwitcherController;
}
+
+ public SecurityController getSecurityController() {
+ return mSecurity;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 7bce664..cbd66aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -43,14 +43,13 @@
private static final long ANIMATION_DURATION = 220;
private static final int TAG_KEY_ANIM = R.id.scrim;
- private static final int NUM_TEASES = 3;
- private static final long TEASE_IN_ANIMATION_DURATION = 1000;
- private static final long TEASE_VISIBLE_DURATION = 2000;
- private static final long TEASE_OUT_ANIMATION_DURATION = 1000;
- private static final long TEASE_INVISIBLE_DURATION = 1000;
- private static final long TEASE_DURATION = TEASE_IN_ANIMATION_DURATION
- + TEASE_VISIBLE_DURATION + TEASE_OUT_ANIMATION_DURATION + TEASE_INVISIBLE_DURATION;
- private static final long PRE_TEASE_DELAY = 1000;
+ private static final long PULSE_IN_ANIMATION_DURATION = 1000;
+ private static final long PULSE_VISIBLE_DURATION = 2000;
+ private static final long PULSE_OUT_ANIMATION_DURATION = 1000;
+ private static final long PULSE_INVISIBLE_DURATION = 1000;
+ private static final long PULSE_DURATION = PULSE_IN_ANIMATION_DURATION
+ + PULSE_VISIBLE_DURATION + PULSE_OUT_ANIMATION_DURATION + PULSE_INVISIBLE_DURATION;
+ private static final long PRE_PULSE_DELAY = 1000;
private final View mScrimBehind;
private final View mScrimInFront;
@@ -70,7 +69,7 @@
private Runnable mOnAnimationFinished;
private boolean mAnimationStarted;
private boolean mDozing;
- private int mTeasesRemaining;
+ private int mPulsesRemaining;
private final Interpolator mInterpolator = new DecelerateInterpolator();
public ScrimController(View scrimBehind, View scrimInFront) {
@@ -126,23 +125,23 @@
if (mDozing == dozing) return;
mDozing = dozing;
if (!mDozing) {
- cancelTeasing();
+ cancelPulsing();
}
scheduleUpdate();
}
/** When dozing, fade screen contents in and out a few times using the front scrim. */
- public long tease() {
+ public long pulse(int pulses) {
if (!mDozing) return 0;
- mTeasesRemaining = NUM_TEASES;
- mScrimInFront.postDelayed(mTeaseIn, PRE_TEASE_DELAY);
- return PRE_TEASE_DELAY + NUM_TEASES * TEASE_DURATION;
+ mPulsesRemaining = Math.max(pulses, mPulsesRemaining);
+ mScrimInFront.postDelayed(mPulseIn, PRE_PULSE_DELAY);
+ return PRE_PULSE_DELAY + mPulsesRemaining * PULSE_DURATION;
}
- private void cancelTeasing() {
- mTeasesRemaining = 0;
- mScrimInFront.removeCallbacks(mTeaseIn);
- mScrimInFront.removeCallbacks(mTeaseOut);
+ private void cancelPulsing() {
+ mPulsesRemaining = 0;
+ mScrimInFront.removeCallbacks(mPulseIn);
+ mScrimInFront.removeCallbacks(mPulseOut);
}
private void scheduleUpdate() {
@@ -285,49 +284,49 @@
return true;
}
- private final Runnable mTeaseIn = new Runnable() {
+ private final Runnable mPulseIn = new Runnable() {
@Override
public void run() {
- if (DEBUG) Log.d(TAG, "Tease in, mDozing=" + mDozing
- + " mTeasesRemaining=" + mTeasesRemaining);
- if (!mDozing || mTeasesRemaining == 0) return;
- mTeasesRemaining--;
- mDurationOverride = TEASE_IN_ANIMATION_DURATION;
+ if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing
+ + " mPulsesRemaining=" + mPulsesRemaining);
+ if (!mDozing || mPulsesRemaining == 0) return;
+ mPulsesRemaining--;
+ mDurationOverride = PULSE_IN_ANIMATION_DURATION;
mAnimationDelay = 0;
mAnimateChange = true;
- mOnAnimationFinished = mTeaseInFinished;
+ mOnAnimationFinished = mPulseInFinished;
setScrimColor(mScrimInFront, 0);
}
};
- private final Runnable mTeaseInFinished = new Runnable() {
+ private final Runnable mPulseInFinished = new Runnable() {
@Override
public void run() {
- if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing);
+ if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
if (!mDozing) return;
- mScrimInFront.postDelayed(mTeaseOut, TEASE_VISIBLE_DURATION);
+ mScrimInFront.postDelayed(mPulseOut, PULSE_VISIBLE_DURATION);
}
};
- private final Runnable mTeaseOut = new Runnable() {
+ private final Runnable mPulseOut = new Runnable() {
@Override
public void run() {
- if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing);
+ if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
if (!mDozing) return;
- mDurationOverride = TEASE_OUT_ANIMATION_DURATION;
+ mDurationOverride = PULSE_OUT_ANIMATION_DURATION;
mAnimationDelay = 0;
mAnimateChange = true;
- mOnAnimationFinished = mTeaseOutFinished;
+ mOnAnimationFinished = mPulseOutFinished;
setScrimColor(mScrimInFront, 1);
}
};
- private final Runnable mTeaseOutFinished = new Runnable() {
+ private final Runnable mPulseOutFinished = new Runnable() {
@Override
public void run() {
- if (DEBUG) Log.d(TAG, "Tease out finished, mTeasesRemaining=" + mTeasesRemaining);
- if (mTeasesRemaining > 0) {
- mScrimInFront.postDelayed(mTeaseIn, TEASE_INVISIBLE_DURATION);
+ if (DEBUG) Log.d(TAG, "Pulse out finished, mPulsesRemaining=" + mPulsesRemaining);
+ if (mPulsesRemaining > 0) {
+ mScrimInFront.postDelayed(mPulseIn, PULSE_INVISIBLE_DURATION);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
new file mode 100644
index 0000000..ede8129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package com.android.systemui.statusbar.policy;
+
+public interface SecurityController {
+
+ boolean hasDeviceOwner();
+ String getDeviceOwnerName();
+ boolean isVpnEnabled();
+ String getVpnApp();
+ boolean isLegacyVpn();
+ String getLegacyVpnName();
+ void openVpnApp();
+ void disconnectFromLegacyVpn();
+
+ void addCallback(VpnCallback callback);
+ void removeCallback(VpnCallback callback);
+
+ public interface VpnCallback {
+ void onVpnStateChanged();
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
new file mode 100644
index 0000000..8e04e5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+package com.android.systemui.statusbar.policy;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.IConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.net.VpnConfig;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class SecurityControllerImpl implements SecurityController {
+
+ private static final String TAG = "SecurityController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+ private final Context mContext;
+ private final ConnectivityManager mConnectivityManager;
+ private final IConnectivityManager mConnectivityService = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ private final DevicePolicyManager mDevicePolicyManager;
+ private final ArrayList<VpnCallback> mCallbacks = new ArrayList<VpnCallback>();
+
+ private boolean mIsVpnEnabled;
+ private VpnConfig mVpnConfig;
+ private String mVpnName;
+
+ public SecurityControllerImpl(Context context) {
+ mContext = context;
+ mDevicePolicyManager = (DevicePolicyManager)
+ context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mConnectivityManager = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ // TODO: re-register network callback on user change.
+ mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("SecurityController state:");
+ pw.print(" mIsVpnEnabled="); pw.println(mIsVpnEnabled);
+ pw.print(" mVpnConfig="); pw.println(mVpnConfig);
+ pw.print(" mVpnName="); pw.println(mVpnName);
+ }
+
+ @Override
+ public boolean hasDeviceOwner() {
+ return !TextUtils.isEmpty(mDevicePolicyManager.getDeviceOwner());
+ }
+
+ @Override
+ public String getDeviceOwnerName() {
+ return mDevicePolicyManager.getDeviceOwnerName();
+ }
+
+ @Override
+ public boolean isVpnEnabled() {
+ // TODO: Remove once using NetworkCallback for updates.
+ updateState();
+
+ return mIsVpnEnabled;
+ }
+
+ @Override
+ public boolean isLegacyVpn() {
+ return mVpnConfig.legacy;
+ }
+
+ @Override
+ public String getVpnApp() {
+ return mVpnName;
+ }
+
+ @Override
+ public String getLegacyVpnName() {
+ return mVpnConfig.session;
+ }
+
+ @Override
+ public void openVpnApp() {
+ Intent i = mContext.getPackageManager().getLaunchIntentForPackage(mVpnConfig.user);
+ if (i != null) {
+ i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(i);
+ }
+ }
+
+ @Override
+ public void disconnectFromLegacyVpn() {
+ try {
+ mConnectivityService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to disconnect from VPN", e);
+ }
+ }
+
+ @Override
+ public void addCallback(VpnCallback callback) {
+ if (callback == null) return;
+ if (DEBUG) Log.d(TAG, "removeCallback " + callback);
+ mCallbacks.remove(callback);
+ }
+
+ @Override
+ public void removeCallback(VpnCallback callback) {
+ if (callback == null || mCallbacks.contains(callback)) return;
+ if (DEBUG) Log.d(TAG, "addCallback " + callback);
+ mCallbacks.add(callback);
+ }
+
+ private void fireCallbacks() {
+ for (VpnCallback callback : mCallbacks) {
+ callback.onVpnStateChanged();
+ }
+ }
+
+ private void updateState() {
+ try {
+ mVpnConfig = mConnectivityService.getVpnConfig();
+
+ // TODO: Remove once using NetworkCallback for updates.
+ mIsVpnEnabled = mVpnConfig != null;
+
+ if (mVpnConfig != null && !mVpnConfig.legacy) {
+ ApplicationInfo info =
+ mContext.getPackageManager().getApplicationInfo(mVpnConfig.user, 0);
+ mVpnName = mContext.getPackageManager().getApplicationLabel(info).toString();
+ }
+ } catch (RemoteException | NameNotFoundException e) {
+ Log.w(TAG, "Unable to get current VPN", e);
+ }
+ }
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ public void onCapabilitiesChanged(android.net.Network network,
+ android.net.NetworkCapabilities networkCapabilities) {
+ if (DEBUG) Log.d(TAG, "onCapabilitiesChanged " + networkCapabilities);
+ mIsVpnEnabled = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
+ updateState();
+ fireCallbacks();
+ }
+ };
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index a123bf7..049add5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -99,6 +99,14 @@
public void buzzBeepBlinked() {
}
+ @Override // CommandQueue
+ public void notificationLightOff() {
+ }
+
+ @Override // CommandQueue
+ public void notificationLightPulse(int argb, int onMillis, int offMillis) {
+ }
+
@Override
protected WindowManager.LayoutParams getSearchLayoutParams(
LayoutParams layoutParams) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 53006f3..822bd5a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -709,6 +709,7 @@
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
+ mStatusBar.notificationLightOff();
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
// reload per-user settings
mSettingsObserver.update(null);
@@ -1442,7 +1443,8 @@
}
pw.println(" ");
}
-
+ pw.println(" mUseAttentionLight=" + mUseAttentionLight);
+ pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
pw.println(" mSoundNotification=" + mSoundNotification);
pw.println(" mVibrateNotification=" + mVibrateNotification);
pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
@@ -2376,6 +2378,7 @@
// Don't flash while we are in a call or screen is on
if (mLedNotification == null || mInCall || mScreenOn) {
mNotificationLight.turnOff();
+ mStatusBar.notificationLightOff();
} else {
final Notification ledno = mLedNotification.sbn.getNotification();
int ledARGB = ledno.ledARGB;
@@ -2390,6 +2393,7 @@
// pulse repeatedly
mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
ledOnMS, ledOffMS);
+ mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
}
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 463f763..c28e0bc 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -21,4 +21,6 @@
public interface StatusBarManagerInternal {
void setNotificationDelegate(NotificationDelegate delegate);
void buzzBeepBlinked();
+ void notificationLightPulse(int argb, int onMillis, int offMillis);
+ void notificationLightOff();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f33943d..263767d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -107,10 +107,13 @@
* Private API used by NotificationManagerService.
*/
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
+ private boolean mNotificationLightOn;
+
@Override
public void setNotificationDelegate(NotificationDelegate delegate) {
mNotificationDelegate = delegate;
}
+
@Override
public void buzzBeepBlinked() {
if (mBar != null) {
@@ -120,6 +123,30 @@
}
}
}
+
+ @Override
+ public void notificationLightPulse(int argb, int onMillis, int offMillis) {
+ mNotificationLightOn = true;
+ if (mBar != null) {
+ try {
+ mBar.notificationLightPulse(argb, onMillis, offMillis);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
+ public void notificationLightOff() {
+ if (mNotificationLightOn) {
+ mNotificationLightOn = false;
+ if (mBar != null) {
+ try {
+ mBar.notificationLightOff();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
};
// ================================================================================