diff options
author | DvTonder <david.vantonder@gmail.com> | 2012-07-15 10:28:15 -0400 |
---|---|---|
committer | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2012-11-21 00:25:18 +0000 |
commit | 0b636d08439c291322339e02be605be54f589b91 (patch) | |
tree | 1abb1921eab3532617ddda7e07939f99de093280 | |
parent | 8415ee214d09ed7f399d8ff68d4f78c76c68fe08 (diff) | |
download | frameworks_base-0b636d08439c291322339e02be605be54f589b91.zip frameworks_base-0b636d08439c291322339e02be605be54f589b91.tar.gz frameworks_base-0b636d08439c291322339e02be605be54f589b91.tar.bz2 |
Framework: Port CM9 features to CM10
This commit includes:
- Power menu Reboot
- Power menu screenshot
- Profiles
- Lock screen Calendar
- Lock screen Weather
- Notification light customization
- Battery light customization
- IME Selector notification toggle
- and a few more things to support Settings
Change-Id: Ibd63116df90b06f6ce6adb8a0343059bbb999bfb
221 files changed, 6868 insertions, 230 deletions
@@ -70,6 +70,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ + core/java/android/app/IProfileManager.aidl \ core/java/android/app/IProcessObserver.aidl \ core/java/android/app/ISearchManager.aidl \ core/java/android/app/ISearchManagerCallback.aidl \ @@ -270,6 +271,8 @@ aidl_files := \ frameworks/base/core/java/android/accounts/IAccountAuthenticator.aidl \ frameworks/base/core/java/android/accounts/IAccountAuthenticatorResponse.aidl \ frameworks/base/core/java/android/app/Notification.aidl \ + frameworks/base/core/java/android/app/NotificationGroup.aidl \ + frameworks/base/core/java/android/app/Profile.aidl \ frameworks/base/core/java/android/app/PendingIntent.aidl \ frameworks/base/core/java/android/bluetooth/BluetoothDevice.aidl \ frameworks/base/core/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl \ diff --git a/core/java/android/app/ConnectionSettings.java b/core/java/android/app/ConnectionSettings.java new file mode 100755 index 0000000..00af439 --- /dev/null +++ b/core/java/android/app/ConnectionSettings.java @@ -0,0 +1,216 @@ +package android.app; + +import android.bluetooth.BluetoothAdapter; +import android.content.ContentResolver; +import android.content.Context; +import android.location.LocationManager; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.net.wimax.WimaxHelper; +import android.os.Parcel; +import android.os.Parcelable; +import android.provider.Settings; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** @hide */ +public final class ConnectionSettings implements Parcelable { + + private int mConnectionId; + private int mValue; + private boolean mOverride; + private boolean mDirty; + + public static final int PROFILE_CONNECTION_MOBILEDATA = 0; + public static final int PROFILE_CONNECTION_WIFI = 1; + public static final int PROFILE_CONNECTION_WIFIAP = 2; + public static final int PROFILE_CONNECTION_WIMAX = 3; + public static final int PROFILE_CONNECTION_GPS = 4; + public static final int PROFILE_CONNECTION_SYNC = 5; + public static final int PROFILE_CONNECTION_BLUETOOTH = 7; + + /** @hide */ + public static final Parcelable.Creator<ConnectionSettings> CREATOR = new Parcelable.Creator<ConnectionSettings>() { + public ConnectionSettings createFromParcel(Parcel in) { + return new ConnectionSettings(in); + } + + @Override + public ConnectionSettings[] newArray(int size) { + return new ConnectionSettings[size]; + } + }; + + + public ConnectionSettings(Parcel parcel) { + readFromParcel(parcel); + } + + public ConnectionSettings(int connectionId) { + this(connectionId, 0, false); + } + + public ConnectionSettings(int connectionId, int value, boolean override) { + mConnectionId = connectionId; + mValue = value; + mOverride = override; + mDirty = false; + } + + public int getConnectionId() { + return mConnectionId; + } + + public int getValue() { + return mValue; + } + + public void setValue(int value) { + mValue = value; + mDirty = true; + } + + public void setOverride(boolean override) { + mOverride = override; + mDirty = true; + } + + public boolean isOverride() { + return mOverride; + } + + /** @hide */ + public boolean isDirty() { + return mDirty; + } + + public void processOverride(Context context) { + BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter(); + LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + + boolean forcedState = getValue() == 1; + boolean currentState; + + switch (getConnectionId()) { + case PROFILE_CONNECTION_MOBILEDATA: + currentState = cm.getMobileDataEnabled(); + if (forcedState != currentState) { + cm.setMobileDataEnabled(forcedState); + } + break; + case PROFILE_CONNECTION_BLUETOOTH: + currentState = bta.isEnabled(); + if (forcedState && !currentState) { + bta.enable(); + } else if (!forcedState && currentState) { + bta.disable(); + } + break; + case PROFILE_CONNECTION_GPS: + currentState = lm.isProviderEnabled(LocationManager.GPS_PROVIDER); + if (currentState != forcedState) { + Settings.Secure.setLocationProviderEnabled(context.getContentResolver(), + LocationManager.GPS_PROVIDER, forcedState); + } + break; + case PROFILE_CONNECTION_SYNC: + currentState = ContentResolver.getMasterSyncAutomatically(); + if (forcedState != currentState) { + ContentResolver.setMasterSyncAutomatically(forcedState); + } + break; + case PROFILE_CONNECTION_WIFI: + int wifiApState = wm.getWifiApState(); + currentState = wm.isWifiEnabled(); + if (currentState != forcedState) { + // Disable wifi tether + if (forcedState && (wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || + (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED)) { + wm.setWifiApEnabled(null, false); + } + wm.setWifiEnabled(forcedState); + } + break; + case PROFILE_CONNECTION_WIFIAP: + int wifiState = wm.getWifiState(); + currentState = wm.isWifiApEnabled(); + if (currentState != forcedState) { + // Disable wifi + if (forcedState && (wifiState == WifiManager.WIFI_STATE_ENABLING) || (wifiState == WifiManager.WIFI_STATE_ENABLED)) { + wm.setWifiEnabled(false); + } + wm.setWifiApEnabled(null, forcedState); + } + break; + case PROFILE_CONNECTION_WIMAX: + if (WimaxHelper.isWimaxSupported(context)) { + currentState = WimaxHelper.isWimaxEnabled(context); + if (currentState != forcedState) { + WimaxHelper.setWimaxEnabled(context, forcedState); + } + } + break; + } + } + + /** @hide */ + public static ConnectionSettings fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + int event = xpp.next(); + ConnectionSettings connectionDescriptor = new ConnectionSettings(0); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("connectionDescriptor")) { + if (event == XmlPullParser.START_TAG) { + String name = xpp.getName(); + if (name.equals("connectionId")) { + connectionDescriptor.mConnectionId = Integer.parseInt(xpp.nextText()); + } else if (name.equals("value")) { + connectionDescriptor.mValue = Integer.parseInt(xpp.nextText()); + } else if (name.equals("override")) { + connectionDescriptor.mOverride = Boolean.parseBoolean(xpp.nextText()); + } + } + event = xpp.next(); + } + return connectionDescriptor; + } + + /** @hide */ + public void getXmlString(StringBuilder builder, Context context) { + builder.append("<connectionDescriptor>\n<connectionId>"); + builder.append(mConnectionId); + builder.append("</connectionId>\n<value>"); + builder.append(mValue); + builder.append("</value>\n<override>"); + builder.append(mOverride); + builder.append("</override>\n</connectionDescriptor>\n"); + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mConnectionId); + dest.writeInt(mOverride ? 1 : 0); + dest.writeInt(mValue); + dest.writeInt(mDirty ? 1 : 0); + } + + /** @hide */ + public void readFromParcel(Parcel in) { + mConnectionId = in.readInt(); + mOverride = in.readInt() != 0; + mValue = in.readInt(); + mDirty = in.readInt() != 0; + } + + +} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 95b6bed..20ed560 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -535,6 +535,12 @@ class ContextImpl extends Context { IUserManager service = IUserManager.Stub.asInterface(b); return new UserManager(ctx, service); }}); + + registerService(PROFILE_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + final Context outerContext = ctx.getOuterContext(); + return new ProfileManager (outerContext, ctx.mMainThread.getHandler()); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/IProfileManager.aidl b/core/java/android/app/IProfileManager.aidl new file mode 100644 index 0000000..c7c6744 --- /dev/null +++ b/core/java/android/app/IProfileManager.aidl @@ -0,0 +1,50 @@ +/* //device/java/android/android/app/IProfileManager.aidl +** +** Copyright 2007, 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 android.app; + +import android.app.Profile; +import android.app.NotificationGroup; +import android.os.ParcelUuid; + +/** {@hide} */ +interface IProfileManager +{ + boolean setActiveProfile(in ParcelUuid profileParcelUuid); + boolean setActiveProfileByName(String profileName); + Profile getActiveProfile(); + + boolean addProfile(in Profile profile); + boolean removeProfile(in Profile profile); + void updateProfile(in Profile profile); + + Profile getProfile(in ParcelUuid profileParcelUuid); + Profile getProfileByName(String profileName); + Profile[] getProfiles(); + boolean profileExists(in ParcelUuid profileUuid); + boolean profileExistsByName(String profileName); + boolean notificationGroupExistsByName(String notificationGroupName); + + NotificationGroup[] getNotificationGroups(); + void addNotificationGroup(in NotificationGroup group); + void removeNotificationGroup(in NotificationGroup group); + void updateNotificationGroup(in NotificationGroup group); + NotificationGroup getNotificationGroupForPackage(in String pkg); + NotificationGroup getNotificationGroup(in ParcelUuid groupParcelUuid); + + void resetAll(); +} diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index e79b214..238a63e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -437,6 +437,15 @@ public class Notification implements Parcelable */ public static final String EXTRA_PEOPLE = "android.people"; + /** + * Bit to be bitwise-ored into the {@link #flags} field that should be + * set if this notification should force the led to pulse even if the + * screen has been shut off while the notification was active. + * + * @hide + */ + public static final int FLAG_FORCE_LED_SCREEN_OFF = 0x00000100; + private Bundle extras; /** diff --git a/core/java/android/app/NotificationGroup.aidl b/core/java/android/app/NotificationGroup.aidl new file mode 100644 index 0000000..44b6290 --- /dev/null +++ b/core/java/android/app/NotificationGroup.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2012, The CyanogenMod 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 android.app; + +parcelable NotificationGroup; diff --git a/core/java/android/app/NotificationGroup.java b/core/java/android/app/NotificationGroup.java new file mode 100644 index 0000000..bcb70d3 --- /dev/null +++ b/core/java/android/app/NotificationGroup.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2011 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 android.app; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** @hide */ +public class NotificationGroup implements Parcelable { + private static final String TAG = "NotificationGroup"; + + private String mName; + private int mNameResId; + + private UUID mUuid; + + private Set<String> mPackages = new HashSet<String>(); + + private boolean mDirty; + + public static final Parcelable.Creator<NotificationGroup> CREATOR = new Parcelable.Creator<NotificationGroup>() { + public NotificationGroup createFromParcel(Parcel in) { + return new NotificationGroup(in); + } + + @Override + public NotificationGroup[] newArray(int size) { + return new NotificationGroup[size]; + } + }; + + public NotificationGroup(String name) { + this(name, -1, null); + } + + public NotificationGroup(String name, int nameResId, UUID uuid) { + mName = name; + mNameResId = nameResId; + mUuid = (uuid != null) ? uuid : UUID.randomUUID(); + mDirty = uuid == null; + } + + private NotificationGroup(Parcel in) { + readFromParcel(in); + } + + @Override + public String toString() { + return getName(); + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + mNameResId = -1; + mDirty = true; + } + + public UUID getUuid() { + return mUuid; + } + + public void addPackage(String pkg) { + mPackages.add(pkg); + mDirty = true; + } + + public String[] getPackages() { + return mPackages.toArray(new String[mPackages.size()]); + } + + public void removePackage(String pkg) { + mPackages.remove(pkg); + mDirty = true; + } + + public boolean hasPackage(String pkg) { + return mPackages.contains(pkg); + } + + public boolean isDirty() { + return mDirty; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mName); + dest.writeInt(mNameResId); + dest.writeInt(mDirty ? 1 : 0); + new ParcelUuid(mUuid).writeToParcel(dest, 0); + dest.writeStringArray(getPackages()); + } + + public void readFromParcel(Parcel in) { + mName = in.readString(); + mNameResId = in.readInt(); + mDirty = in.readInt() != 0; + mUuid = ParcelUuid.CREATOR.createFromParcel(in).getUuid(); + mPackages.addAll(Arrays.asList(in.readStringArray())); + } + + public void getXmlString(StringBuilder builder, Context context) { + builder.append("<notificationGroup "); + if (mNameResId > 0) { + builder.append("nameres=\""); + builder.append(context.getResources().getResourceEntryName(mNameResId)); + } else { + builder.append("name=\""); + builder.append(TextUtils.htmlEncode(getName())); + } + builder.append("\" uuid=\""); + builder.append(TextUtils.htmlEncode(getUuid().toString())); + builder.append("\">\n"); + for (String pkg : mPackages) { + builder.append("<package>" + TextUtils.htmlEncode(pkg) + "</package>\n"); + } + builder.append("</notificationGroup>\n"); + mDirty = false; + } + + public static NotificationGroup fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + String value = xpp.getAttributeValue(null, "nameres"); + int nameResId = -1; + String name = null; + UUID uuid = null; + + if (value != null) { + nameResId = context.getResources().getIdentifier(value, "string", "android"); + if (nameResId > 0) { + name = context.getResources().getString(nameResId); + } + } + + if (name == null) { + name = xpp.getAttributeValue(null, "name"); + } + + value = xpp.getAttributeValue(null, "uuid"); + if (value != null) { + try { + uuid = UUID.fromString(value); + } catch (IllegalArgumentException e) { + Log.w(TAG, "UUID not recognized for " + name + ", using new one."); + } + } + + NotificationGroup notificationGroup = new NotificationGroup(name, nameResId, uuid); + int event = xpp.next(); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("notificationGroup")) { + if (event == XmlPullParser.START_TAG) { + if (xpp.getName().equals("package")) { + String pkg = xpp.nextText(); + notificationGroup.addPackage(pkg); + } + } + event = xpp.next(); + } + + /* we just loaded from XML, no need to save */ + notificationGroup.mDirty = false; + + return notificationGroup; + } +} diff --git a/core/java/android/app/Profile.aidl b/core/java/android/app/Profile.aidl new file mode 100644 index 0000000..d75bd76 --- /dev/null +++ b/core/java/android/app/Profile.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2007, 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 android.app; + +parcelable Profile; diff --git a/core/java/android/app/Profile.java b/core/java/android/app/Profile.java new file mode 100644 index 0000000..54e187d --- /dev/null +++ b/core/java/android/app/Profile.java @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2011 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 android.app; + +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * @hide + */ +public final class Profile implements Parcelable, Comparable { + + private String mName; + + private int mNameResId; + + private UUID mUuid; + + private ArrayList<UUID> mSecondaryUuids = new ArrayList<UUID>(); + + private Map<UUID, ProfileGroup> profileGroups = new HashMap<UUID, ProfileGroup>(); + + private ProfileGroup mDefaultGroup; + + private boolean mStatusBarIndicator = false; + + private boolean mDirty; + + private static final String TAG = "Profile"; + + private int mProfileType; + + private static final int CONDITIONAL_TYPE = 1; + + private static final int TOGGLE_TYPE = 0; + + private Map<Integer, StreamSettings> streams = new HashMap<Integer, StreamSettings>(); + + private Map<Integer, ConnectionSettings> connections = new HashMap<Integer, ConnectionSettings>(); + + private Map<Integer, VibratorSettings> vibrators = new HashMap<Integer, VibratorSettings>(); + + private int mScreenLockMode = LockMode.DEFAULT; + + private int mAirplaneMode = AirplaneMode.DEFAULT; + + /** @hide */ + public static class LockMode { + public static final int DEFAULT = 0; + public static final int INSECURE = 1; + public static final int DISABLE = 2; + } + + /** @hide */ + public static class AirplaneMode { + public static final int DEFAULT = 0; + public static final int ENABLE = 1; + public static final int DISABLE = 2; + } + + /** @hide */ + public static final Parcelable.Creator<Profile> CREATOR = new Parcelable.Creator<Profile>() { + public Profile createFromParcel(Parcel in) { + return new Profile(in); + } + + @Override + public Profile[] newArray(int size) { + return new Profile[size]; + } + }; + + /** @hide */ + public Profile(String name) { + this(name, -1, UUID.randomUUID()); + } + + private Profile(String name, int nameResId, UUID uuid) { + mName = name; + mNameResId = nameResId; + mUuid = uuid; + mProfileType = TOGGLE_TYPE; //Default to toggle type + mDirty = false; + } + + private Profile(Parcel in) { + readFromParcel(in); + } + + public int compareTo(Object obj) + { + Profile tmp = (Profile) obj; + if (mName.compareTo(tmp.mName) < 0) { + return -1; + } else if (mName.compareTo(tmp.mName) > 0) { + return 1; + } + return 0; + } + + /** @hide */ + public void addProfileGroup(ProfileGroup value) { + if (value.isDefaultGroup()) { + /* we must not have more than one default group */ + if (mDefaultGroup != null) { + return; + } + mDefaultGroup = value; + } + profileGroups.put(value.getUuid(), value); + mDirty = true; + } + + /** @hide */ + public void removeProfileGroup(UUID uuid) { + if (!profileGroups.get(uuid).isDefaultGroup()) { + profileGroups.remove(uuid); + } else { + Log.e(TAG, "Cannot remove default group: " + uuid); + } + } + + public ProfileGroup[] getProfileGroups() { + return profileGroups.values().toArray(new ProfileGroup[profileGroups.size()]); + } + + public ProfileGroup getProfileGroup(UUID uuid) { + return profileGroups.get(uuid); + } + + public ProfileGroup getDefaultGroup() { + return mDefaultGroup; + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mName); + dest.writeInt(mNameResId); + new ParcelUuid(mUuid).writeToParcel(dest, 0); + ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(mSecondaryUuids.size()); + for (UUID u : mSecondaryUuids) { + uuids.add(new ParcelUuid(u)); + } + dest.writeParcelableArray(uuids.toArray(new Parcelable[uuids.size()]), flags); + dest.writeInt(mStatusBarIndicator ? 1 : 0); + dest.writeInt(mProfileType); + dest.writeInt(mDirty ? 1 : 0); + dest.writeParcelableArray( + profileGroups.values().toArray(new Parcelable[profileGroups.size()]), flags); + dest.writeParcelableArray( + streams.values().toArray(new Parcelable[streams.size()]), flags); + dest.writeParcelableArray( + connections.values().toArray(new Parcelable[connections.size()]), flags); + dest.writeParcelableArray(vibrators.values().toArray(new Parcelable[vibrators.size()]), flags); + dest.writeInt(mScreenLockMode); + dest.writeInt(mAirplaneMode); + } + + /** @hide */ + public void readFromParcel(Parcel in) { + mName = in.readString(); + mNameResId = in.readInt(); + mUuid = ParcelUuid.CREATOR.createFromParcel(in).getUuid(); + for (Parcelable parcel : in.readParcelableArray(null)) { + ParcelUuid u = (ParcelUuid) parcel; + mSecondaryUuids.add(u.getUuid()); + } + mStatusBarIndicator = (in.readInt() == 1); + mProfileType = in.readInt(); + mDirty = (in.readInt() == 1); + for (Parcelable group : in.readParcelableArray(null)) { + ProfileGroup grp = (ProfileGroup) group; + profileGroups.put(grp.getUuid(), grp); + if (grp.isDefaultGroup()) { + mDefaultGroup = grp; + } + } + for (Parcelable parcel : in.readParcelableArray(null)) { + StreamSettings stream = (StreamSettings) parcel; + streams.put(stream.getStreamId(), stream); + } + for (Parcelable parcel : in.readParcelableArray(null)) { + ConnectionSettings connection = (ConnectionSettings) parcel; + connections.put(connection.getConnectionId(), connection); + } + for (Parcelable parcel : in.readParcelableArray(null)) { + VibratorSettings vibrator = (VibratorSettings) parcel; + vibrators.put(vibrator.getVibratorId(), vibrator); + } + mScreenLockMode = in.readInt(); + mAirplaneMode = in.readInt(); + } + + public String getName() { + return mName; + } + + /** @hide */ + public void setName(String name) { + mName = name; + mNameResId = -1; + mDirty = true; + } + + public int getProfileType() { + return mProfileType; + } + + /** @hide */ + public void setProfileType(int type) { + mProfileType = type; + mDirty = true; + } + + public UUID getUuid() { + if (this.mUuid == null) this.mUuid = UUID.randomUUID(); + return this.mUuid; + } + + public UUID[] getSecondaryUuids() { + return mSecondaryUuids.toArray(new UUID[mSecondaryUuids.size()]); + } + + public void setSecondaryUuids(List<UUID> uuids) { + mSecondaryUuids.clear(); + if (uuids != null) { + mSecondaryUuids.addAll(uuids); + mDirty = true; + } + } + + public void addSecondaryUuid(UUID uuid) { + if (uuid != null) { + mSecondaryUuids.add(uuid); + mDirty = true; + } + } + + public boolean getStatusBarIndicator() { + return mStatusBarIndicator; + } + + public void setStatusBarIndicator(boolean newStatusBarIndicator) { + mStatusBarIndicator = newStatusBarIndicator; + mDirty = true; + } + + public boolean isConditionalType() { + return(mProfileType == CONDITIONAL_TYPE ? true : false); + } + + public void setConditionalType() { + mProfileType = CONDITIONAL_TYPE; + mDirty = true; + } + + public int getScreenLockMode() { + return mScreenLockMode; + } + + public void setScreenLockMode(int screenLockMode) { + if (screenLockMode < LockMode.DEFAULT || screenLockMode > LockMode.DISABLE) { + mScreenLockMode = LockMode.DEFAULT; + } else { + mScreenLockMode = screenLockMode; + } + mDirty = true; + } + + public int getAirplaneMode() { + return mAirplaneMode; + } + + public void setAirplaneMode(int airplaneMode) { + if (airplaneMode < AirplaneMode.DEFAULT || airplaneMode > AirplaneMode.DISABLE) { + mAirplaneMode = AirplaneMode.DEFAULT; + } else { + mAirplaneMode = airplaneMode; + } + mDirty = true; + } + + /** @hide */ + public boolean isDirty() { + if (mDirty) { + return true; + } + for (ProfileGroup group : profileGroups.values()) { + if (group.isDirty()) { + return true; + } + } + for (StreamSettings stream : streams.values()) { + if (stream.isDirty()) { + return true; + } + } + for (ConnectionSettings conn : connections.values()) { + if (conn.isDirty()) { + return true; + } + } + for (VibratorSettings vibrator : vibrators.values()) { + if (vibrator.isDirty()) { + return true; + } + } + return false; + } + + /** @hide */ + public void getXmlString(StringBuilder builder, Context context) { + builder.append("<profile "); + if (mNameResId > 0) { + builder.append("nameres=\""); + builder.append(context.getResources().getResourceEntryName(mNameResId)); + } else { + builder.append("name=\""); + builder.append(TextUtils.htmlEncode(getName())); + } + builder.append("\" uuid=\""); + builder.append(TextUtils.htmlEncode(getUuid().toString())); + builder.append("\">\n"); + + builder.append("<uuids>"); + for (UUID u : mSecondaryUuids) { + builder.append("<uuid>"); + builder.append(TextUtils.htmlEncode(u.toString())); + builder.append("</uuid>"); + } + builder.append("</uuids>\n"); + + builder.append("<profiletype>"); + builder.append(getProfileType() == TOGGLE_TYPE ? "toggle" : "conditional"); + builder.append("</profiletype>\n"); + + builder.append("<statusbar>"); + builder.append(getStatusBarIndicator() ? "yes" : "no"); + builder.append("</statusbar>\n"); + + builder.append("<screen-lock-mode>"); + builder.append(mScreenLockMode); + builder.append("</screen-lock-mode>\n"); + + builder.append("<airplane-mode>"); + builder.append(mAirplaneMode); + builder.append("</airplane-mode>\n"); + + for (ProfileGroup pGroup : profileGroups.values()) { + pGroup.getXmlString(builder, context); + } + for (StreamSettings sd : streams.values()) { + sd.getXmlString(builder, context); + } + for (ConnectionSettings cs : connections.values()) { + cs.getXmlString(builder, context); + } + for (VibratorSettings vs : vibrators.values()) { + vs.getXmlString(builder, context); + } + builder.append("</profile>\n"); + mDirty = false; + } + + private static List<UUID> readSecondaryUuidsFromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, + IOException { + ArrayList<UUID> uuids = new ArrayList<UUID>(); + int event = xpp.next(); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("uuids")) { + if (event == XmlPullParser.START_TAG) { + String name = xpp.getName(); + if (name.equals("uuid")) { + try { + uuids.add(UUID.fromString(xpp.nextText())); + } catch (NullPointerException e) { + Log.w(TAG, "Null Pointer - invalid UUID"); + } catch (IllegalArgumentException e) { + Log.w(TAG, "UUID not recognized"); + } + } + } + event = xpp.next(); + } + return uuids; + } + + /** @hide */ + public static Profile fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + String value = xpp.getAttributeValue(null, "nameres"); + int profileNameResId = -1; + String profileName = null; + + if (value != null) { + profileNameResId = context.getResources().getIdentifier(value, "string", "android"); + if (profileNameResId > 0) { + profileName = context.getResources().getString(profileNameResId); + } + } + + if (profileName == null) { + profileName = xpp.getAttributeValue(null, "name"); + } + + UUID profileUuid = UUID.randomUUID(); + try { + profileUuid = UUID.fromString(xpp.getAttributeValue(null, "uuid")); + } catch (NullPointerException e) { + Log.w(TAG, + "Null Pointer - UUID not found for " + + profileName + + ". New UUID generated: " + + profileUuid.toString() + ); + } catch (IllegalArgumentException e) { + Log.w(TAG, + "UUID not recognized for " + + profileName + + ". New UUID generated: " + + profileUuid.toString() + ); + } + + Profile profile = new Profile(profileName, profileNameResId, profileUuid); + int event = xpp.next(); + while (event != XmlPullParser.END_TAG) { + if (event == XmlPullParser.START_TAG) { + String name = xpp.getName(); + if (name.equals("uuids")) { + profile.setSecondaryUuids(readSecondaryUuidsFromXml(xpp, context)); + } + if (name.equals("statusbar")) { + profile.setStatusBarIndicator(xpp.nextText().equals("yes")); + } + if (name.equals("profiletype")) { + profile.setProfileType(xpp.nextText().equals("toggle") ? TOGGLE_TYPE : CONDITIONAL_TYPE); + } + if (name.equals("screen-lock-mode")) { + profile.setScreenLockMode(Integer.valueOf(xpp.nextText())); + } + if (name.equals("airplane-mode")) { + profile.setAirplaneMode(Integer.valueOf(xpp.nextText())); + } + if (name.equals("profileGroup")) { + ProfileGroup pg = ProfileGroup.fromXml(xpp, context); + profile.addProfileGroup(pg); + } + if (name.equals("streamDescriptor")) { + StreamSettings sd = StreamSettings.fromXml(xpp, context); + profile.setStreamSettings(sd); + } + if (name.equals("connectionDescriptor")) { + ConnectionSettings cs = ConnectionSettings.fromXml(xpp, context); + profile.connections.put(cs.getConnectionId(), cs); + } + if (name.equals("vibratorDescriptor")) { + VibratorSettings vs = VibratorSettings.fromXml(xpp, context); + profile.setVibratorSettings(vs); + } + } + event = xpp.next(); + } + + /* we just loaded from XML, so nothing needs saving */ + profile.mDirty = false; + + return profile; + } + + /** @hide */ + public void doSelect(Context context) { + // Set stream volumes + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + for (StreamSettings sd : streams.values()) { + if (sd.isOverride()) { + am.setStreamVolume(sd.getStreamId(), sd.getValue(), 0); + } + } + // Set connections + for (ConnectionSettings cs : connections.values()) { + if (cs.isOverride()) { + cs.processOverride(context); + } + } + // Set vibrators + for (VibratorSettings vs : vibrators.values()) { + if (vs.isOverride()) { + vs.processOverride(context); + } + } + // Set airplane mode + doSelectAirplaneMode(context); + } + + private void doSelectAirplaneMode(Context context) { + if (getAirplaneMode() != AirplaneMode.DEFAULT) { + int current = Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0); + int target = getAirplaneMode(); + if (current == 1 && target == AirplaneMode.DISABLE || current == 0 && target == AirplaneMode.ENABLE) { + Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 1 - current); + Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intent.putExtra("state", target != AirplaneMode.DISABLE); + context.sendBroadcast(intent); + } + } + } + + /** @hide */ + public StreamSettings getSettingsForStream(int streamId){ + return streams.get(streamId); + } + + /** @hide */ + public void setStreamSettings(StreamSettings descriptor){ + streams.put(descriptor.getStreamId(), descriptor); + mDirty = true; + } + + /** @hide */ + public Collection<StreamSettings> getStreamSettings(){ + return streams.values(); + } + + /** @hide */ + public VibratorSettings getSettingsForVibrator(int vibratorId) { + return vibrators.get(vibratorId); + } + + /** @hide */ + public void setVibratorSettings(VibratorSettings descriptor) { + vibrators.put(descriptor.getVibratorId(), descriptor); + mDirty = true; + } + + /** @hide */ + public Collection<VibratorSettings> getVibratorSettings() { + return vibrators.values(); + } + + /** @hide */ + public ConnectionSettings getSettingsForConnection(int connectionId){ + return connections.get(connectionId); + } + + /** @hide */ + public void setConnectionSettings(ConnectionSettings descriptor){ + connections.put(descriptor.getConnectionId(), descriptor); + } + + /** @hide */ + public Collection<ConnectionSettings> getConnectionSettings(){ + return connections.values(); + } + +} diff --git a/core/java/android/app/ProfileGroup.java b/core/java/android/app/ProfileGroup.java new file mode 100644 index 0000000..b3b70d6 --- /dev/null +++ b/core/java/android/app/ProfileGroup.java @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2011 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 android.app; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; +import android.text.TextUtils; +import android.util.Log; + +import java.io.IOException; +import java.util.UUID; + +/** + * @hide + */ +public final class ProfileGroup implements Parcelable { + private static final String TAG = "ProfileGroup"; + + private String mName; + private int mNameResId; + + private UUID mUuid; + + private Uri mSoundOverride = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + private Uri mRingerOverride = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); + + private Mode mSoundMode = Mode.DEFAULT; + private Mode mRingerMode = Mode.DEFAULT; + private Mode mVibrateMode = Mode.DEFAULT; + private Mode mLightsMode = Mode.DEFAULT; + + private boolean mDefaultGroup = false; + private boolean mDirty; + + /** @hide */ + public static final Parcelable.Creator<ProfileGroup> CREATOR = new Parcelable.Creator<ProfileGroup>() { + public ProfileGroup createFromParcel(Parcel in) { + return new ProfileGroup(in); + } + + @Override + public ProfileGroup[] newArray(int size) { + return new ProfileGroup[size]; + } + }; + + /** @hide */ + public ProfileGroup(UUID uuid, boolean defaultGroup) { + this(null, uuid, defaultGroup); + } + + private ProfileGroup(String name, UUID uuid, boolean defaultGroup) { + mName = name; + mUuid = (uuid != null) ? uuid : UUID.randomUUID(); + mDefaultGroup = defaultGroup; + mDirty = uuid == null; + } + + /** @hide */ + private ProfileGroup(Parcel in) { + readFromParcel(in); + } + + /** @hide */ + public boolean matches(NotificationGroup group, boolean defaultGroup) { + if (mUuid.equals(group.getUuid())) { + return true; + } + + /* fallback matches for backwards compatibility */ + boolean matches = false; + + /* fallback attempt 1: match name */ + if (mName != null && mName.equals(group.getName())) { + matches = true; + /* fallback attempt 2: match for the 'defaultGroup' flag to match the wildcard group */ + } else if (mDefaultGroup && defaultGroup) { + matches = true; + } + + if (!matches) { + return false; + } + + mName = null; + mUuid = group.getUuid(); + mDirty = true; + + return true; + } + + public UUID getUuid() { + return mUuid; + } + + public boolean isDefaultGroup() { + return mDefaultGroup; + } + + /** @hide */ + public boolean isDirty() { + return mDirty; + } + + /** @hide */ + public void setSoundOverride(Uri sound) { + mSoundOverride = sound; + mDirty = true; + } + + public Uri getSoundOverride() { + return mSoundOverride; + } + + /** @hide */ + public void setRingerOverride(Uri ringer) { + mRingerOverride = ringer; + mDirty = true; + } + + public Uri getRingerOverride() { + return mRingerOverride; + } + + /** @hide */ + public void setSoundMode(Mode soundMode) { + mSoundMode = soundMode; + mDirty = true; + } + + public Mode getSoundMode() { + return mSoundMode; + } + + /** @hide */ + public void setRingerMode(Mode ringerMode) { + mRingerMode = ringerMode; + mDirty = true; + } + + public Mode getRingerMode() { + return mRingerMode; + } + + /** @hide */ + public void setVibrateMode(Mode vibrateMode) { + mVibrateMode = vibrateMode; + mDirty = true; + } + + public Mode getVibrateMode() { + return mVibrateMode; + } + + /** @hide */ + public void setLightsMode(Mode lightsMode) { + mLightsMode = lightsMode; + mDirty = true; + } + + public Mode getLightsMode() { + return mLightsMode; + } + + // TODO : add support for LEDs / screen etc. + + /** @hide */ + public Notification processNotification(Notification notification) { + + switch (mSoundMode) { + case OVERRIDE: + notification.sound = mSoundOverride; + break; + case SUPPRESS: + silenceNotification(notification); + break; + case DEFAULT: + } + switch (mVibrateMode) { + case OVERRIDE: + notification.defaults |= Notification.DEFAULT_VIBRATE; + break; + case SUPPRESS: + suppressVibrate(notification); + break; + case DEFAULT: + } + switch (mLightsMode) { + case OVERRIDE: + notification.defaults |= Notification.DEFAULT_LIGHTS; + break; + case SUPPRESS: + suppressLights(notification); + break; + case DEFAULT: + } + return notification; + } + + private void silenceNotification(Notification notification) { + notification.defaults &= (~Notification.DEFAULT_SOUND); + notification.sound = null; + } + + private void suppressVibrate(Notification notification) { + notification.defaults &= (~Notification.DEFAULT_VIBRATE); + notification.vibrate = null; + } + + private void suppressLights(Notification notification) { + notification.defaults &= (~Notification.DEFAULT_LIGHTS); + notification.flags &= (~Notification.FLAG_SHOW_LIGHTS); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mName); + new ParcelUuid(mUuid).writeToParcel(dest, 0); + dest.writeInt(mDefaultGroup ? 1 : 0); + dest.writeInt(mDirty ? 1 : 0); + dest.writeParcelable(mSoundOverride, flags); + dest.writeParcelable(mRingerOverride, flags); + + dest.writeString(mSoundMode.name()); + dest.writeString(mRingerMode.name()); + dest.writeString(mVibrateMode.name()); + dest.writeString(mLightsMode.name()); + } + + /** @hide */ + public void readFromParcel(Parcel in) { + mName = in.readString(); + mUuid = ParcelUuid.CREATOR.createFromParcel(in).getUuid(); + mDefaultGroup = in.readInt() != 0; + mDirty = in.readInt() != 0; + mSoundOverride = in.readParcelable(null); + mRingerOverride = in.readParcelable(null); + + mSoundMode = Mode.valueOf(Mode.class, in.readString()); + mRingerMode = Mode.valueOf(Mode.class, in.readString()); + mVibrateMode = Mode.valueOf(Mode.class, in.readString()); + mLightsMode = Mode.valueOf(Mode.class, in.readString()); + } + + public enum Mode { + SUPPRESS, DEFAULT, OVERRIDE; + } + + /** @hide */ + public void getXmlString(StringBuilder builder, Context context) { + builder.append("<profileGroup uuid=\""); + builder.append(TextUtils.htmlEncode(mUuid.toString())); + if (mName != null) { + builder.append("\" name=\""); + builder.append(mName); + } + builder.append("\" default=\""); + builder.append(isDefaultGroup()); + builder.append("\">\n<sound>"); + builder.append(TextUtils.htmlEncode(mSoundOverride.toString())); + builder.append("</sound>\n<ringer>"); + builder.append(TextUtils.htmlEncode(mRingerOverride.toString())); + builder.append("</ringer>\n<soundMode>"); + builder.append(mSoundMode); + builder.append("</soundMode>\n<ringerMode>"); + builder.append(mRingerMode); + builder.append("</ringerMode>\n<vibrateMode>"); + builder.append(mVibrateMode); + builder.append("</vibrateMode>\n<lightsMode>"); + builder.append(mLightsMode); + builder.append("</lightsMode>\n</profileGroup>\n"); + mDirty = false; + } + + /** @hide */ + public static ProfileGroup fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + String name = xpp.getAttributeValue(null, "name"); + UUID uuid = null; + String value = xpp.getAttributeValue(null, "uuid"); + + if (value != null) { + try { + uuid = UUID.fromString(value); + } catch (IllegalArgumentException e) { + Log.w(TAG, "UUID not recognized for " + name + ", using new one."); + } + } + + value = xpp.getAttributeValue(null, "default"); + boolean defaultGroup = TextUtils.equals(value, "true"); + + ProfileGroup profileGroup = new ProfileGroup(name, uuid, defaultGroup); + int event = xpp.next(); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("profileGroup")) { + if (event == XmlPullParser.START_TAG) { + name = xpp.getName(); + if (name.equals("sound")) { + profileGroup.setSoundOverride(Uri.parse(xpp.nextText())); + } else if (name.equals("ringer")) { + profileGroup.setRingerOverride(Uri.parse(xpp.nextText())); + } else if (name.equals("soundMode")) { + profileGroup.setSoundMode(Mode.valueOf(xpp.nextText())); + } else if (name.equals("ringerMode")) { + profileGroup.setRingerMode(Mode.valueOf(xpp.nextText())); + } else if (name.equals("vibrateMode")) { + profileGroup.setVibrateMode(Mode.valueOf(xpp.nextText())); + } else if (name.equals("lightsMode")) { + profileGroup.setLightsMode(Mode.valueOf(xpp.nextText())); + } + } + event = xpp.next(); + } + + /* we just loaded from XML, no need to save */ + profileGroup.mDirty = false; + + return profileGroup; + } +} diff --git a/core/java/android/app/ProfileManager.java b/core/java/android/app/ProfileManager.java new file mode 100644 index 0000000..4a0f1b7 --- /dev/null +++ b/core/java/android/app/ProfileManager.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2011 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 android.app; + +import java.util.UUID; + +import android.content.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +/** + * @hide + */ +public class ProfileManager { + + private static IProfileManager sService; + + private Context mContext; + + private static final String TAG = "ProfileManager"; + + /** @hide */ + static public IProfileManager getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(Context.PROFILE_SERVICE); + sService = IProfileManager.Stub.asInterface(b); + return sService; + } + + /** @hide */ + ProfileManager(Context context, Handler handler) { + mContext = context; + } + + @Deprecated + public void setActiveProfile(String profileName) { + try { + getService().setActiveProfileByName(profileName); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + public void setActiveProfile(UUID profileUuid) { + try { + getService().setActiveProfile(new ParcelUuid(profileUuid)); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + public Profile getActiveProfile() { + try { + return getService().getActiveProfile(); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + /** @hide */ + public void addProfile(Profile profile) { + try { + getService().addProfile(profile); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + /** @hide */ + public void removeProfile(Profile profile) { + try { + getService().removeProfile(profile); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + /** @hide */ + public void updateProfile(Profile profile) { + try { + getService().updateProfile(profile); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + @Deprecated + public Profile getProfile(String profileName) { + try { + return getService().getProfileByName(profileName); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + public Profile getProfile(UUID profileUuid) { + try { + return getService().getProfile(new ParcelUuid(profileUuid)); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + public String[] getProfileNames() { + try { + Profile[] profiles = getService().getProfiles(); + String[] names = new String[profiles.length]; + for (int i = 0; i < profiles.length; i++) { + names[i] = profiles[i].getName(); + } + return names; + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + public Profile[] getProfiles() { + try { + return getService().getProfiles(); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + public boolean profileExists(String profileName) { + try { + return getService().profileExistsByName(profileName); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + // To be on the safe side, we'll return "true", to prevent duplicate profiles + // from being created. + return true; + } + } + + public boolean profileExists(UUID profileUuid) { + try { + return getService().profileExists(new ParcelUuid(profileUuid)); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + // To be on the safe side, we'll return "true", to prevent duplicate profiles + // from being created. + return true; + } + } + + public boolean notificationGroupExists(String notificationGroupName) { + try { + return getService().notificationGroupExistsByName(notificationGroupName); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + // To be on the safe side, we'll return "true", to prevent duplicate notification + // groups from being created. + return true; + } + } + + /** @hide */ + public NotificationGroup[] getNotificationGroups() { + try { + return getService().getNotificationGroups(); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + /** @hide */ + public void addNotificationGroup(NotificationGroup group) { + try { + getService().addNotificationGroup(group); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + /** @hide */ + public void removeNotificationGroup(NotificationGroup group) { + try { + getService().removeNotificationGroup(group); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + /** @hide */ + public void updateNotificationGroup(NotificationGroup group) { + try { + getService().updateNotificationGroup(group); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } + + /** @hide */ + public NotificationGroup getNotificationGroupForPackage(String pkg) { + try { + return getService().getNotificationGroupForPackage(pkg); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + /** @hide */ + public NotificationGroup getNotificationGroup(UUID uuid) { + try { + return getService().getNotificationGroup(new ParcelUuid(uuid)); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return null; + } + + /** @hide */ + public ProfileGroup getActiveProfileGroup(String packageName) { + NotificationGroup notificationGroup = getNotificationGroupForPackage(packageName); + if(notificationGroup == null){ + ProfileGroup defaultGroup = getActiveProfile().getDefaultGroup(); + return defaultGroup; + } + return getActiveProfile().getProfileGroup(notificationGroup.getUuid()); + } + + /** @hide */ + public void resetAll() { + try { + getService().resetAll(); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } catch (SecurityException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + } +} diff --git a/core/java/android/app/StreamSettings.java b/core/java/android/app/StreamSettings.java new file mode 100644 index 0000000..2f3bf27 --- /dev/null +++ b/core/java/android/app/StreamSettings.java @@ -0,0 +1,130 @@ + +package android.app; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.IOException; + +/** @hide */ +public final class StreamSettings implements Parcelable{ + + private int mStreamId; + private int mValue; + private boolean mOverride; + private boolean mDirty; + + /** @hide */ + public static final Parcelable.Creator<StreamSettings> CREATOR = new Parcelable.Creator<StreamSettings>() { + public StreamSettings createFromParcel(Parcel in) { + return new StreamSettings(in); + } + + @Override + public StreamSettings[] newArray(int size) { + return new StreamSettings[size]; + } + }; + + + public StreamSettings(Parcel parcel) { + readFromParcel(parcel); + } + + public StreamSettings(int streamId) { + this(streamId, 0, false); + } + + public StreamSettings(int streamId, int value, boolean override) { + mStreamId = streamId; + mValue = value; + mOverride = override; + mDirty = false; + } + + public int getStreamId() { + return mStreamId; + } + + public int getValue() { + return mValue; + } + + public void setValue(int value) { + mValue = value; + mDirty = true; + } + + public void setOverride(boolean override) { + mOverride = override; + mDirty = true; + } + + public boolean isOverride() { + return mOverride; + } + + /** @hide */ + public boolean isDirty() { + return mDirty; + } + + /** @hide */ + public static StreamSettings fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + int event = xpp.next(); + StreamSettings streamDescriptor = new StreamSettings(0); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("streamDescriptor")) { + if (event == XmlPullParser.START_TAG) { + String name = xpp.getName(); + if (name.equals("streamId")) { + streamDescriptor.mStreamId = Integer.parseInt(xpp.nextText()); + } else if (name.equals("value")) { + streamDescriptor.mValue = Integer.parseInt(xpp.nextText()); + } else if (name.equals("override")) { + streamDescriptor.mOverride = Boolean.parseBoolean(xpp.nextText()); + } + } + event = xpp.next(); + } + return streamDescriptor; + } + + /** @hide */ + public void getXmlString(StringBuilder builder, Context context) { + builder.append("<streamDescriptor>\n<streamId>"); + builder.append(mStreamId); + builder.append("</streamId>\n<value>"); + builder.append(mValue); + builder.append("</value>\n<override>"); + builder.append(mOverride); + builder.append("</override>\n</streamDescriptor>\n"); + mDirty = false; + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mStreamId); + dest.writeInt(mOverride ? 1 : 0); + dest.writeInt(mValue); + dest.writeInt(mDirty ? 1 : 0); + } + + /** @hide */ + public void readFromParcel(Parcel in) { + mStreamId = in.readInt(); + mOverride = in.readInt() != 0; + mValue = in.readInt(); + mDirty = in.readInt() != 0; + } +} diff --git a/core/java/android/app/VibratorSettings.java b/core/java/android/app/VibratorSettings.java new file mode 100644 index 0000000..10e5ca2 --- /dev/null +++ b/core/java/android/app/VibratorSettings.java @@ -0,0 +1,150 @@ + +package android.app; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.media.AudioManager; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.IOException; + +/** @hide */ +public final class VibratorSettings implements Parcelable{ + + public static final int OFF = 0; + public static final int SILENT = 1; + public static final int ON = 2; + + private int mVibratorId; + private int mValue; + private boolean mOverride; + private boolean mDirty; + + /** @hide */ + public static final Parcelable.Creator<VibratorSettings> CREATOR = new Parcelable.Creator<VibratorSettings>() { + public VibratorSettings createFromParcel(Parcel in) { + return new VibratorSettings(in); + } + + @Override + public VibratorSettings[] newArray(int size) { + return new VibratorSettings[size]; + } + }; + + + public VibratorSettings(Parcel parcel) { + readFromParcel(parcel); + } + + public VibratorSettings(int vibratorId) { + this(vibratorId, 0, false); + } + + public VibratorSettings(int vibratorId, int value, boolean override) { + mVibratorId = vibratorId; + mValue = value; + mOverride = override; + mDirty = false; + } + + public int getVibratorId() { + return mVibratorId; + } + + public int getValue() { + return mValue; + } + + public void setValue(int value) { + mValue = value; + mDirty = true; + } + + public void setOverride(boolean override) { + mOverride = override; + mDirty = true; + } + + public boolean isOverride() { + return mOverride; + } + + /** @hide */ + public boolean isDirty() { + return mDirty; + } + + /** @hide */ + public static VibratorSettings fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + int event = xpp.next(); + VibratorSettings vibratorDescriptor = new VibratorSettings(0); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("vibratorDescriptor")) { + if (event == XmlPullParser.START_TAG) { + String name = xpp.getName(); + if (name.equals("vibratorId")) { + vibratorDescriptor.mVibratorId = Integer.parseInt(xpp.nextText()); + } else if (name.equals("value")) { + vibratorDescriptor.mValue = Integer.parseInt(xpp.nextText()); + } else if (name.equals("override")) { + vibratorDescriptor.mOverride = Boolean.parseBoolean(xpp.nextText()); + } + } + event = xpp.next(); + } + return vibratorDescriptor; + } + + /** @hide */ + public void getXmlString(StringBuilder builder, Context context) { + builder.append("<vibratorDescriptor>\n<vibratorId>"); + builder.append(mVibratorId); + builder.append("</vibratorId>\n<value>"); + builder.append(mValue); + builder.append("</value>\n<override>"); + builder.append(mOverride); + builder.append("</override>\n</vibratorDescriptor>\n"); + mDirty = false; + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mVibratorId); + dest.writeInt(mOverride ? 1 : 0); + dest.writeInt(mValue); + dest.writeInt(mDirty ? 1 : 0); + } + + /** @hide */ + public void readFromParcel(Parcel in) { + mVibratorId = in.readInt(); + mOverride = in.readInt() != 0; + mValue = in.readInt(); + mDirty = in.readInt() != 0; + } + + /** @hide */ + public void processOverride(Context context) { + AudioManager amgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + switch (mValue) { + case OFF: + amgr.setVibrateSetting(mVibratorId, AudioManager.VIBRATE_SETTING_OFF); + break; + case SILENT: + amgr.setVibrateSetting(mVibratorId, AudioManager.VIBRATE_SETTING_ONLY_SILENT); + default: + amgr.setVibrateSetting(mVibratorId, AudioManager.VIBRATE_SETTING_ON); + break; + } + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7aa2507..f0c6ce8 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1890,6 +1890,18 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a + * {@link android.app.ProfileManager} for setting + * notification profiles. + * + * @see #getSystemService + * @see android.app.ProfileManager + * + * @hide + */ + public static final String PROFILE_SERVICE = "profile"; + + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.view.accessibility.AccessibilityManager} for giving the user * feedback for UI events through the registered event listeners. * diff --git a/core/java/android/net/wimax/WimaxHelper.java b/core/java/android/net/wimax/WimaxHelper.java new file mode 100644 index 0000000..f6c7a40 --- /dev/null +++ b/core/java/android/net/wimax/WimaxHelper.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2011 The CyanogenMod 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 android.net.wimax; + +import dalvik.system.DexClassLoader; + +import android.content.Context; +import android.content.ContextWrapper; +import android.os.Handler; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; +import android.provider.Settings; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * {@hide} + */ +public class WimaxHelper { + + private static final String TAG = "WimaxHelper"; + + private static final String WIMAX_CONTROLLER_CLASSNAME = "com.htc.net.wimax.WimaxController"; + private static final String WIMAX_MANAGER_CLASSNAME = "android.net.fourG.wimax.Wimax4GManager"; + + private static DexClassLoader sWimaxClassLoader; + private static String sWimaxManagerClassname, sIsWimaxEnabledMethodname, + sSetWimaxEnabledMethodname, sGetWimaxStateMethodname; + + public static boolean isWimaxSupported(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_wimaxEnabled); + } + + public static DexClassLoader getWimaxClassLoader(Context context) { + if (isWimaxSupported(context)) { + if (sWimaxClassLoader == null) { + sWimaxManagerClassname = context.getResources().getString( + com.android.internal.R.string.config_wimaxManagerClassname); + + // WimaxController::getWimaxState == Wimax4GManager::get4GState. + // However, Wimax4GManager also implements a different getWimaxState + // method, which returns a WimaxState object describing the connection + // state, not the enabled state. Other methods are similarly renamed. + if (sWimaxManagerClassname.equals(WIMAX_CONTROLLER_CLASSNAME)) { + sIsWimaxEnabledMethodname = "isWimaxEnabled"; + sSetWimaxEnabledMethodname = "setWimaxEnabled"; + sGetWimaxStateMethodname = "getWimaxState"; + } else if (sWimaxManagerClassname.equals(WIMAX_MANAGER_CLASSNAME)) { + sIsWimaxEnabledMethodname = "is4GEnabled"; + sSetWimaxEnabledMethodname = "set4GEnabled"; + sGetWimaxStateMethodname = "get4GState"; + } + + String wimaxJarLocation = context.getResources().getString( + com.android.internal.R.string.config_wimaxServiceJarLocation); + String wimaxLibLocation = context.getResources().getString( + com.android.internal.R.string.config_wimaxNativeLibLocation); + sWimaxClassLoader = new DexClassLoader(wimaxJarLocation, + new ContextWrapper(context).getCacheDir().getAbsolutePath(), + wimaxLibLocation,ClassLoader.getSystemClassLoader()); + } + return sWimaxClassLoader; + } + return null; + } + + public static Object createWimaxService(Context context, Handler handler) { + Object controller = null; + + try { + DexClassLoader wimaxClassLoader = getWimaxClassLoader(context); + if (sWimaxManagerClassname.equals(WIMAX_CONTROLLER_CLASSNAME)) { + // Load supersonic's and speedy's WimaxController. + IBinder b = ServiceManager.getService(WimaxManagerConstants.WIMAX_SERVICE); + if (b != null) { + Class<?> klass = wimaxClassLoader.loadClass("com.htc.net.wimax.IWimaxController$Stub"); + if (klass != null) { + Method asInterface = klass.getMethod("asInterface", IBinder.class); + Object wc = asInterface.invoke(null, b); + if (wc != null) { + klass = wimaxClassLoader.loadClass(WIMAX_CONTROLLER_CLASSNAME); + if (klass != null) { + Constructor<?> ctor = klass.getDeclaredConstructors()[1]; + controller = ctor.newInstance(wc, handler); + } + } + } + } + } else if (sWimaxManagerClassname.equals(WIMAX_MANAGER_CLASSNAME)) { + // Load crespo4g's (and epicmtd's) Wimax4GManager. + // Note that crespo4g's implementation grabs WIMAX_SERVICE internally, so + // it doesn't need to be passed in. Other implementations (may) require + // WIMAX_SERVICE to be grabbed externally, so check Wimax4GManager::<init>. + Class<?> klass = wimaxClassLoader.loadClass(WIMAX_MANAGER_CLASSNAME); + if (klass != null) { + Constructor<?> ctor = klass.getDeclaredConstructors()[0]; + controller = ctor.newInstance(); + } + } + } catch (Exception e) { + Log.e(TAG, "Unable to create WimaxController instance", e); + } + + return controller; + } + + public static boolean isWimaxEnabled(Context context) { + boolean ret = false; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method m = wimaxService.getClass().getMethod(sIsWimaxEnabledMethodname); + ret = (Boolean) m.invoke(wimaxService); + } catch (Exception e) { + Log.e(TAG, "Unable to get WiMAX enabled state!", e); + } + return ret; + } + + public static boolean setWimaxEnabled(Context context, boolean enabled) { + boolean ret = false; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method m = wimaxService.getClass().getMethod(sSetWimaxEnabledMethodname, boolean.class); + ret = (Boolean) m.invoke(wimaxService, enabled); + if (ret) + Settings.Secure.putInt(context.getContentResolver(), + Settings.Secure.WIMAX_ON, (Boolean) enabled ? 1 : 0); + } catch (Exception e) { + Log.e(TAG, "Unable to set WiMAX state!", e); + } + return ret; + } + + public static int getWimaxState(Context context) { + int ret = 0; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method m = wimaxService.getClass().getMethod(sGetWimaxStateMethodname); + ret = (Integer) m.invoke(wimaxService); + } catch (Exception e) { + Log.e(TAG, "Unable to get WiMAX state!", e); + } + return ret; + } + + public static boolean wimaxRescan(Context context) { + boolean ret = false; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method wimaxRescan = wimaxService.getClass().getMethod("wimaxRescan"); + if (wimaxRescan != null) { + wimaxRescan.invoke(wimaxService); + ret = true; + } + } catch (Exception e) { + Log.e(TAG, "Unable to perform WiMAX rescan!", e); + } + return ret; + } + + private static Object getWimaxInfo(Context context) { + Object wimaxInfo = null; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method getConnectionInfo = wimaxService.getClass().getMethod("getConnectionInfo"); + wimaxInfo = getConnectionInfo.invoke(wimaxService); + } catch (Exception e) { + Log.e(TAG, "Unable to get a WimaxInfo object!", e); + } + return wimaxInfo; + } +} diff --git a/core/java/android/net/wimax/WimaxManagerConstants.java b/core/java/android/net/wimax/WimaxManagerConstants.java index b4aaf5b..a8239ba 100644 --- a/core/java/android/net/wimax/WimaxManagerConstants.java +++ b/core/java/android/net/wimax/WimaxManagerConstants.java @@ -67,11 +67,21 @@ public class WimaxManagerConstants public static final int NET_4G_STATE_DISABLED = 1; /** + * Indicatates Wimax is disabling. + */ + public static final int NET_4G_STATE_DISABLING = 0; + + /** * Indicatates Wimax is enabled. */ public static final int NET_4G_STATE_ENABLED = 3; /** + * Indicatates Wimax is enabling. + */ + public static final int NET_4G_STATE_ENABLING = 2; + + /** * Indicatates Wimax status is known. */ public static final int NET_4G_STATE_UNKNOWN = 4; @@ -101,4 +111,9 @@ public class WimaxManagerConstants */ public static final int WIMAX_STATE_DISCONNECTED = 9; + /** + * Constants for HTC/SQN WiMAX implementation + */ + public static final String WIMAX_ENABLED_CHANGED_ACTION = "com.htc.net.wimax.WIMAX_ENABLED_CHANGED"; + public static final String CURRENT_WIMAX_ENABLED_STATE = "curWimaxEnabledState"; } diff --git a/core/java/android/preference/ListPreferenceMultiSelect.java b/core/java/android/preference/ListPreferenceMultiSelect.java new file mode 100644 index 0000000..83edc72 --- /dev/null +++ b/core/java/android/preference/ListPreferenceMultiSelect.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 The CyanogenMod 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 android.preference; + +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.DialogInterface; +import android.text.TextUtils; +import android.util.AttributeSet; + +/** + * This Preference type is required for the Power Widget functionality. It should + * not be used for any other multi select lists, use the Android MultiselectListPreference + * instead + * @hide + */ +public class ListPreferenceMultiSelect extends ListPreference { + + private static final String SEPARATOR = "OV=I=XseparatorX=I=VO"; + + private boolean[] mClickedDialogEntryIndices; + + public ListPreferenceMultiSelect(Context context) { + super(context); + } + + public ListPreferenceMultiSelect(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onPrepareDialogBuilder(Builder builder) { + CharSequence[] entries = getEntries(); + CharSequence[] entryValues = getEntryValues(); + + if (entries == null || entryValues == null || entries.length != entryValues.length) { + throw new IllegalStateException( + this.getClass().getSimpleName() + + " requires an entries array and an entryValues array which are both the same length"); + } + + mClickedDialogEntryIndices = new boolean[entryValues.length]; + restoreCheckedEntries(); + builder.setMultiChoiceItems(entries, mClickedDialogEntryIndices, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + mClickedDialogEntryIndices[which] = isChecked; + } + }); + } + + public static String[] parseStoredValue(CharSequence val) { + if (TextUtils.isEmpty(val)) { + return null; + } else { + return val.toString().split(SEPARATOR); + } + } + + private void restoreCheckedEntries() { + CharSequence[] entryValues = getEntryValues(); + + String[] vals = parseStoredValue(getValue()); + if (vals != null) { + for (String val : vals) { + for (int i = 0; i < entryValues.length; i++) { + CharSequence entry = entryValues[i]; + if (entry.equals(val)) { + mClickedDialogEntryIndices[i] = true; + break; + } + } + } + } + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + CharSequence[] entryValues = getEntryValues(); + + if (positiveResult && entryValues != null) { + StringBuilder value = new StringBuilder(); + for (int i = 0; i < entryValues.length; i++) { + if (mClickedDialogEntryIndices[i]) { + if (value.length() > 0) { + value.append(SEPARATOR); + } + value.append(entryValues[i]); + } + } + + String val = value.toString(); + if (callChangeListener(val)) { + setValue(val); + } + } + } +} diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java index caf55d7..b763022 100644 --- a/core/java/android/preference/VolumePreference.java +++ b/core/java/android/preference/VolumePreference.java @@ -146,6 +146,11 @@ public class VolumePreference extends SeekBarDialogPreference implements } } + /** @hide */ + protected boolean onVolumeChange(SeekBarVolumizer volumizer, int value) { + return true; + } + @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); @@ -305,10 +310,14 @@ public class VolumePreference extends SeekBarDialogPreference implements } void postSetVolume(int progress) { - // Do the volume changing separately to give responsive UI - mLastProgress = progress; - mHandler.removeCallbacks(this); - mHandler.post(this); + if (onVolumeChange(this, progress)) { + // Do the volume changing separately to give responsive UI + mLastProgress = progress; + mHandler.removeCallbacks(this); + mHandler.post(this); + } else { + mSeekBar.setProgress(mLastProgress); + } } public void onStartTrackingTouch(SeekBar seekBar) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2a3f916..99db53c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1688,6 +1688,148 @@ public final class Settings { public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; /** + * Indicates that custom light sensor settings has changed. + * The value is random and changes reloads light settings. + * + * @hide + */ + public static final String LIGHTS_CHANGED = "lights_changed"; + + /** + * Whether custom light sensor levels & values are enabled. The value is + * boolean (1 or 0). + * + * @hide + */ + public static final String LIGHT_SENSOR_CUSTOM = "light_sensor_custom"; + + /** + * Screen dim value to use if LIGHT_SENSOR_CUSTOM is set. The value is int. + * Default is android.os.BRIGHTNESS_DIM. + * + * @hide + */ + public static final String LIGHT_SCREEN_DIM = "light_screen_dim"; + + /** + * Custom light sensor levels. The value is a comma separated int array + * with length N. + * Example: "100,300,3000". + * + * @hide + */ + public static final String LIGHT_SENSOR_LEVELS = "light_sensor_levels"; + + /** + * Custom light sensor lcd values. The value is a comma separated int array + * with length N+1. + * Example: "10,50,100,255". + * + * @hide + */ + public static final String LIGHT_SENSOR_LCD_VALUES = "light_sensor_lcd_values"; + + /** + * Custom light sensor lcd values. The value is a comma separated int array + * with length N+1. + * Example: "10,50,100,255". + * + * @hide + */ + public static final String LIGHT_SENSOR_BUTTON_VALUES = "light_sensor_button_values"; + + /** + * Custom light sensor lcd values. The value is a comma separated int array + * with length N+1. + * Example: "10,50,100,255". + * + * @hide + */ + public static final String LIGHT_SENSOR_KEYBOARD_VALUES = "light_sensor_keyboard_values"; + + /** + * Whether light sensor is allowed to decrease when calculating automatic + * backlight. The value is boolean (1 or 0). + * + * @hide + */ + public static final String LIGHT_DECREASE = "light_decrease"; + + /** + * Light sensor hysteresis for decreasing backlight. The value is + * int (0-99) representing % (0-0.99 as float). Example: + * + * Levels Output + * 0 - 100 50 + * 100 - 200 100 + * 200 - Inf 255 + * + * Current sensor value is 150 which gives light value 100. Hysteresis is 50. + * Current level lower bound is 100 and previous lower bound is 0. + * Sensor value must drop below 100-(100-0)*(50/100)=50 for output to become 50 + * (corresponding to the 0 - 100 level). + * @hide + */ + public static final String LIGHT_HYSTERESIS = "light_hysteresis"; + + /** + * Whether light sensor used when calculating automatic backlight should + * be filtered through an moving average filter. + * The value is boolean (1 or 0). + * + * @hide + */ + public static final String LIGHT_FILTER = "light_filter"; + + /** + * Window length of filter used when calculating automatic backlight. + * One minute means that the average sensor value last minute is used. + * The value is integer (milliseconds) + * + * @hide + */ + public static final String LIGHT_FILTER_WINDOW = "light_filter_window"; + + /** + * Reset threshold of filter used when calculating automatic backlight. + * Sudden large jumps in sensor value resets the filter. This is used + * to make the filter respond quickly to large enough changes in input + * while still filtering small changes. Example: + * + * Current filter value (average) is 100 and sensor value is changing to + * 10, 150, 100, 30, 50. The filter is continously taking the average of + * the samples. Now the user goes outside and the value jumps over 1000. + * The difference between current average and new sample is larger than + * the reset threshold and filter is reset. It begins calculating a new + * average on samples around 1000 (say, 800, 1200, 1000, 1100 etc.) + * + * The value is integer (lux) + * + * @hide + */ + public static final String LIGHT_FILTER_RESET = "light_filter_reset"; + + /** + * Sample interval of filter used when calculating automatic backlight. + * The value is integer (milliseconds) + * + * @hide + */ + public static final String LIGHT_FILTER_INTERVAL = "light_filter_interval"; + + /** + * Whether to enable the electron beam animation when turning screen on + * + * @hide */ + public static final String ELECTRON_BEAM_ANIMATION_ON = "electron_beam_animation_on"; + + /** + * Whether to enable the electron beam animation when turning screen off + * + * @hide */ + public static final String ELECTRON_BEAM_ANIMATION_OFF = "electron_beam_animation_off"; + + /** * Control whether the process CPU usage meter should be shown. * * @deprecated Use {@link Global#SHOW_PROCESSES} instead @@ -1706,6 +1848,22 @@ public final class Settings { public static final String ALWAYS_FINISH_ACTIVITIES = Global.ALWAYS_FINISH_ACTIVITIES; /** + * Volume Overlay Mode, This is behaviour of the volume overlay panel + * Defaults to 0 - which is simple + * @hide + */ + public static final String MODE_VOLUME_OVERLAY = "mode_volume_overlay"; + + /** @hide */ + public static final int VOLUME_OVERLAY_SINGLE = 0; + /** @hide */ + public static final int VOLUME_OVERLAY_EXPANDABLE = 1; + /** @hide */ + public static final int VOLUME_OVERLAY_EXPANDED = 2; + /** @hide */ + public static final int VOLUME_OVERLAY_NONE = 3; + + /** * Determines which streams are affected by ringer mode changes. The * stream type's bit should be set to 1 if it should be muted when going * into an inaudible ringer mode. @@ -1782,6 +1940,12 @@ public final class Settings { public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco"; /** + * Whether to prevent loud volume levels when headset is first plugged in. + * @hide + */ + public static final String SAFE_HEADSET_VOLUME_RESTORE = "safe_headset_volume_restore"; + + /** * Master volume (float in the range 0.0f to 1.0f). * @hide */ @@ -1813,6 +1977,24 @@ public final class Settings { "notifications_use_ring_volume"; /** + * Whether the phone ringtone should be played in an increasing manner + * @hide + */ + public static final String INCREASING_RING = "increasing_ring"; + + /** + * Minimum volume index for increasing ring volume + * @hide + */ + public static final String INCREASING_RING_MIN_VOLUME = "increasing_ring_min_vol"; + + /** + * Time (in ms) between ringtone volume increases + * @hide + */ + public static final String INCREASING_RING_INTERVAL = "increasing_ring_interval"; + + /** * Whether silent mode should allow vibration feedback. This is used * internally in AudioService and the Sound settings activity to * coordinate decoupling of vibrate and silent modes. This setting @@ -2008,6 +2190,19 @@ public final class Settings { public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation"; /** + * Control the type of rotation which can be performed using the accelerometer + * if ACCELEROMETER_ROTATION is enabled. + * Value is a bitwise combination of + * 1 = 0 degrees (portrait) + * 2 = 90 degrees (left) + * 4 = 180 degrees (inverted portrait) + * 8 = 270 degrees (right) + * Setting to 0 is effectively orientation lock + * @hide + */ + public static final String ACCELEROMETER_ROTATION_ANGLES = "accelerometer_rotation_angles"; + + /** * Default screen rotation when no other policy applies. * When {@link #ACCELEROMETER_ROTATION} is zero and no on-screen Activity expresses a * preference, this rotation value will be used. Must be one of the @@ -2079,6 +2274,13 @@ public final class Settings { public static final String TTY_MODE = "tty_mode"; /** + * Whether noise suppression is enabled. The value is + * boolean (1 or 0). + * @hide + */ + public static final String NOISE_SUPPRESSION = "noise_suppression"; + + /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is * boolean (1 or 0). */ @@ -2105,6 +2307,109 @@ public final class Settings { public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; /** + * What color to use for the notification LED by default + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR = "notification_light_pulse_default_color"; + + /** + * How long to flash the notification LED by default + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON = "notification_light_pulse_default_led_on"; + + /** + * How long to wait between flashes for the notification LED by default + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF = "notification_light_pulse_default_led_off"; + + /** + * What color to use for the missed call notification LED + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_CALL_COLOR = "notification_light_pulse_call_color"; + + /** + * How long to flash the missed call notification LED + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_CALL_LED_ON = "notification_light_pulse_call_led_on"; + + /** + * How long to wait between flashes for the missed call notification LED + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_CALL_LED_OFF = "notification_light_pulse_call_led_off"; + + /** + * What color to use for the voicemail notification LED + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_VMAIL_COLOR = "notification_light_pulse_vmail_color"; + + /** + * How long to flash the voicemail notification LED + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_VMAIL_LED_ON = "notification_light_pulse_vmail_led_on"; + + /** + * How long to wait between flashes for the voicemail notification LED + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_VMAIL_LED_OFF = "notification_light_pulse_vmail_led_off"; + + /** + * Whether to use the custom LED values for the notification pulse LED. + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE = "notification_light_pulse_custom_enable"; + + /** + * Which custom LED values to use for the notification pulse LED. + * @hide + */ + public static final String NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES = "notification_light_pulse_custom_values"; + + /** + * Whether the battery light should be enabled (if hardware supports it) + * The value is boolean (1 or 0). + * @hide + */ + public static final String BATTERY_LIGHT_ENABLED = "battery_light_enabled"; + + /** + * Whether the battery LED should repeatedly flash when the battery is low + * on charge. The value is boolean (1 or 0). + * @hide + */ + public static final String BATTERY_LIGHT_PULSE = "battery_light_pulse"; + + /** + * What color to use for the battery LED while charging - low + * @hide + */ + public static final String BATTERY_LIGHT_LOW_COLOR = "battery_light_low_color"; + + /** + * What color to use for the battery LED while charging - medium + * @hide + */ + public static final String BATTERY_LIGHT_MEDIUM_COLOR = "battery_light_medium_color"; + + /** + * What color to use for the battery LED while charging - full + * @hide + */ + public static final String BATTERY_LIGHT_FULL_COLOR = "battery_light_full_color"; + + /** Sprint MWI Quirk: Show message wait indicator notifications + * @hide + */ + public static final String ENABLE_MWI_NOTIFICATION = "enable_mwi_notification"; + + /** * Show pointer location on screen? * 0 = no * 1 = yes @@ -2265,6 +2570,362 @@ public final class Settings { public static final String POINTER_SPEED = "pointer_speed"; /** + * Use the Notification Power Widget? (Who wouldn't!) + * + * @hide + */ + public static final String EXPANDED_VIEW_WIDGET = "expanded_view_widget"; + + /** + * Whether to hide the notification screen after clicking on a widget + * button + * + * @hide + */ + public static final String EXPANDED_HIDE_ONCHANGE = "expanded_hide_onchange"; + + /** + * Hide scroll bar in power widget + * + * @hide + */ + public static final String EXPANDED_HIDE_SCROLLBAR = "expanded_hide_scrollbar"; + + /** + * Hide indicator in status bar widget + * + * @hide + */ + public static final String EXPANDED_HIDE_INDICATOR = "expanded_hide_indicator"; + + /** + * Haptic feedback in power widget + * + * @hide + */ + public static final String EXPANDED_HAPTIC_FEEDBACK = "expanded_haptic_feedback"; + + /** + * Notification Indicator Color + * + * @hide + */ + public static final String EXPANDED_VIEW_WIDGET_COLOR = "expanded_widget_color"; + + /** + * Widget Buttons to Use + * + * @hide + */ + public static final String WIDGET_BUTTONS = "expanded_widget_buttons"; + + /** + * Widget Buttons to Use - Tablet + * + * @hide + */ + public static final String WIDGET_BUTTONS_TABLET = "expanded_widget_buttons_tablet"; + + /** + * Navigation controls to Use + * + * @hide + */ + public static final String NAV_BUTTONS = "nav_buttons"; + + /** + * Notification Power Widget - Custom Brightness Mode + * @hide + */ + public static final String EXPANDED_BRIGHTNESS_MODE = "expanded_brightness_mode"; + + /** + * Notification Power Widget - Custom Network Mode + * @hide + */ + public static final String EXPANDED_NETWORK_MODE = "expanded_network_mode"; + + /** + * Notification Power Widget - Custom LTE Toggle + * 1 - lte on, 0 - lte off + * @hide + */ + public static final String LTE_MODE = "lte_mode"; + + /** + * Notification Power Widget - Custom Screen Timeout + * @hide + */ + public static final String EXPANDED_SCREENTIMEOUT_MODE = "expanded_screentimeout_mode"; + + /** + * Notification Power Widget - Custom Ring Mode + * @hide + */ + public static final String EXPANDED_RING_MODE = "expanded_ring_mode"; + + /** + * Notification Power Widget - Custom Torch Mode + * @hide + */ + public static final String EXPANDED_FLASH_MODE = "expanded_flash_mode"; + + /** + * AutoHide CombinedBar on tablets. + * @hide + */ + public static final String COMBINED_BAR_AUTO_HIDE = "combined_bar_auto_hide"; + + /** + * Display style of AM/PM next to clock in status bar + * 0: Normal display (Eclair stock) + * 1: Small display (Froyo stock) + * 2: No display (Gingerbread/ICS stock) + * default: 2 + * @hide + */ + public static final String STATUS_BAR_AM_PM = "status_bar_am_pm"; + + /** + * Display style of the status bar battery information + * 0: Display the stock battery information + * 1: Display cm battery percentage implementation / dont show stock icon + * 2: Hide the battery information + * default: 0 + * @hide + */ + public static final String STATUS_BAR_BATTERY = "status_bar_battery"; + + /** + * Whether to show the clock in status bar + * of the stock battery icon + * 0: don't show the clock + * 1: show the clock + * default: 1 + * @hide + */ + public static final String STATUS_BAR_CLOCK = "status_bar_clock"; + + /** + * Whether to show the signal text or signal bars. + * default: 0 + * 0: show signal bars + * 1: show signal text numbers + * 2: show signal text numbers w/small dBm appended + * @hide + */ + public static final String STATUS_BAR_SIGNAL_TEXT = "status_bar_signal"; + + /** + * Whether to control brightness from status bar + * + * @hide + */ + public static final String STATUS_BAR_BRIGHTNESS_CONTROL = "status_bar_brightness_control"; + + /** + * Whether to show the IME switcher in the status bar + * @hide + */ + public static final String STATUS_BAR_IME_SWITCHER = "status_bar_ime_switcher"; + + /** + * Whether to use a separate delay for "slide to unlock" and security + * lock + * @hide + */ + public static final String SCREEN_LOCK_SLIDE_DELAY_TOGGLE = "screen_lock_slide_delay_toggle"; + + /** + * How many ms to delay before enabling the "slide to unlock" screen + * lock when the screen goes off due to timeout + * @hide + */ + public static final String SCREEN_LOCK_SLIDE_TIMEOUT_DELAY = "screen_lock_slide_timeout_delay"; + + /** + * How many ms to delay before enabling the "slide to unlock" screen + * lock when the screen is turned off by the user + * @hide + */ + public static final String SCREEN_LOCK_SLIDE_SCREENOFF_DELAY = "screen_lock_slide_screenoff_delay"; + + /** + * Whether to use the custom quick unlock screen control + * @hide + */ + public static final String LOCKSCREEN_QUICK_UNLOCK_CONTROL = "lockscreen_quick_unlock_control"; + + /** + * Boolean value whether to link ringtone and notification volumes + * + * @hide + */ + public static final String VOLUME_LINK_NOTIFICATION = "volume_link_notification"; + + /** + * Whether to unlock the menu key. The value is boolean (1 or 0). + * @hide + */ + public static final String MENU_UNLOCK_SCREEN = "menu_unlock_screen"; + + /** + * Whether to wake the screen with the volume keys, the value is boolean. + * @hide + */ + public static final String VOLUME_WAKE_SCREEN = "volume_wake_screen"; + + /** + * Whether or not volume button music controls should be enabled to seek media tracks + * @hide + */ + public static final String VOLBTN_MUSIC_CONTROLS = "volbtn_music_controls"; + + /** + * Whether national data roaming should be used. + * @hide + */ + public static final String MVNO_ROAMING = "mvno_roaming"; + + /** + * Whether to enable quiet hours. + * @hide + */ + public static final String QUIET_HOURS_ENABLED = "quiet_hours_enabled"; + + /** + * Sets when quiet hours starts. This is stored in minutes from the start of the day. + * @hide + */ + public static final String QUIET_HOURS_START = "quiet_hours_start"; + + /** + * Sets when quiet hours end. This is stored in minutes from the start of the day. + * @hide + */ + public static final String QUIET_HOURS_END = "quiet_hours_end"; + + /** + * Whether to remove the sound from outgoing notifications during quiet hours. + * @hide + */ + public static final String QUIET_HOURS_MUTE = "quiet_hours_mute"; + + /** + * Whether to disable haptic feedback during quiet hours. + * @hide + */ + public static final String QUIET_HOURS_HAPTIC = "quiet_hours_haptic"; + + /** + * Whether to remove the vibration from outgoing notifications during quiet hours. + * @hide + */ + public static final String QUIET_HOURS_STILL = "quiet_hours_still"; + + /** + * Whether to attempt to dim the LED color during quiet hours. + * @hide + */ + public static final String QUIET_HOURS_DIM = "quiet_hours_dim"; + + /** + * Sets the lockscreen background style + * @hide + */ + public static final String LOCKSCREEN_BACKGROUND = "lockscreen_background"; + + /** + * Show the weather on the lock screen + * @hide + */ + public static final String LOCKSCREEN_WEATHER = "lockscreen_weather"; + + /** + * Show the current weather location on the lock screen + * @hide + */ + public static final String WEATHER_SHOW_LOCATION = "weather_show_location"; + + /** + * Show the current weather location on the lock screen + * @hide + */ + public static final String WEATHER_SHOW_TIMESTAMP = "weather_show_timestamp"; + + /** + * Use the custom/manually configured weather location + * @hide + */ + public static final String WEATHER_USE_CUSTOM_LOCATION = "weather_use_custom_location"; + + /** + * Stores the custom/manually configured weather location + * @hide + */ + public static final String WEATHER_CUSTOM_LOCATION = "weather_custom_location"; + + /** + * Stores the weather update frequency + * @hide + */ + public static final String WEATHER_UPDATE_INTERVAL = "weather_update_interval"; + + /** + * Use Metric measurements (celcius, km/h) for weather data + * @hide + */ + public static final String WEATHER_USE_METRIC = "weather_use_metric"; + + /** + * Invert low/high temperature display + * @hide + */ + public static final String WEATHER_INVERT_LOWHIGH = "weather_invert_lowhigh"; + + /** + * Whether to show the next calendar event + * @hide + */ + public static final String LOCKSCREEN_CALENDAR = "lockscreen_calendar"; + + /** + * Whether to show the next calendar event's location + * @hide + */ + public static final String LOCKSCREEN_CALENDAR_SHOW_LOCATION = "lockscreen_calendar_show_location"; + + /** + * Whether to show the next calendar event's description + * @hide + */ + public static final String LOCKSCREEN_CALENDAR_SHOW_DESCRIPTION = "lockscreen_calendar_show_description"; + + /** + * Which calendars to look for events + * @hide + */ + public static final String LOCKSCREEN_CALENDARS = "lockscreen_calendars"; + + /** + * How far in the future to look for events + * @hide + */ + public static final String LOCKSCREEN_CALENDAR_LOOKAHEAD = "lockscreen_calendar_lookahead"; + + /** + * Whether to find only events with reminders + * @hide + */ + public static final String LOCKSCREEN_CALENDAR_REMINDERS_ONLY = "lockscreen_calendar_reminders_only"; + + /** + * Show the pending notification counts as overlays on the status bar + * @hide + */ + public static final String STATUS_BAR_NOTIF_COUNT = "status_bar_notif_count"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @@ -2315,10 +2976,13 @@ public final class Settings { AUTO_TIME_ZONE, // moved to global TIME_12_24, DATE_FORMAT, + ACCELEROMETER_ROTATION, + USER_ROTATION, DTMF_TONE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING, HEARING_AID, TTY_MODE, + NOISE_SUPPRESSION, SOUND_EFFECTS_ENABLED, HAPTIC_FEEDBACK_ENABLED, POWER_SOUNDS_ENABLED, // moved to global @@ -2326,10 +2990,18 @@ public final class Settings { LOCKSCREEN_SOUNDS_ENABLED, SHOW_WEB_SUGGESTIONS, NOTIFICATION_LIGHT_PULSE, + NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR, + NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON, + NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF, SIP_CALL_OPTIONS, SIP_RECEIVE_CALLS, POINTER_SPEED, - VIBRATE_WHEN_RINGING + QUIET_HOURS_ENABLED, + QUIET_HOURS_START, + QUIET_HOURS_END, + QUIET_HOURS_MUTE, + QUIET_HOURS_STILL, + QUIET_HOURS_DIM, }; // Settings moved to Settings.Secure @@ -3082,6 +3754,12 @@ public final class Settings { public static final String ADB_ENABLED = Global.ADB_ENABLED; /** + * The TCP/IP port to run ADB on, or -1 for USB + * @hide + */ + public static final String ADB_PORT = "adb_port"; + + /** * The hostname for this device * @hide */ @@ -3261,6 +3939,13 @@ public final class Settings { "lock_screen_owner_info_enabled"; /** + * Whether the unsecure widget screen will be shown before a secure + * lock screen + * @hide + */ + public static final String LOCK_BEFORE_UNLOCK = + "lock_before_unlock"; + /** * The Logging ID (a unique 64-bit value) as a hex string. * Used as a pseudonymous identifier for logging. * @deprecated This identifier is poorly initialized and has @@ -3679,6 +4364,12 @@ public final class Settings { Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS; /** + * Whether the Wimax should be on. Only the WiMAX service should touch this. + * @hide + */ + public static final String WIMAX_ON = "wimax_on"; + + /** * Whether background data usage is allowed. * * @deprecated As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, @@ -4292,6 +4983,13 @@ public final class Settings { public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled"; /** + * Whether to allow killing of the foreground app by long-pressing the Back button + * @hide + */ + public static final String KILL_APP_LONGPRESS_BACK = "kill_app_longpress_back"; + + + /** * Whether we keep the device on while the device is plugged in. * Supported values are: * <ul> diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index d7c7f46..2497e95 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -35,7 +35,10 @@ import android.media.ToneGenerator; import android.net.Uri; import android.os.Handler; import android.os.Message; +import android.os.RemoteException; import android.os.Vibrator; +import android.provider.Settings; +import android.provider.Settings.System; import android.util.Log; import android.view.WindowManager.LayoutParams; import android.widget.ImageView; @@ -99,12 +102,16 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private static final int STREAM_MASTER = -100; // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC + public static final String ACTION_VOLUME_OVERLAY_CHANGED + = "android.intent.action.VOLUME_OVERLAY_CHANGED"; + protected Context mContext; private AudioManager mAudioManager; protected AudioService mAudioService; private boolean mRingIsSilent; private boolean mShowCombinedVolumes; private boolean mVoiceCapable; + private int mCurrentOverlayStyle = -1; // True if we want to play tones on the system stream when the master stream is specified. private final boolean mPlayMasterStreamTones; @@ -326,12 +333,26 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds; + // get the users preference + int choosenStyle = Settings.System.getInt(context.getContentResolver(),Settings.System.MODE_VOLUME_OVERLAY, -1); + // by default -1 is expected - deal with choosing the right default + if (choosenStyle == -1) { + if (mVoiceCapable) { + choosenStyle = Settings.System.VOLUME_OVERLAY_SINGLE; + } else { + choosenStyle = Settings.System.VOLUME_OVERLAY_EXPANDABLE; + } + } + changeOverlayStyle(choosenStyle); + mMoreButton.setOnClickListener(this); + listenToRingerMode(); } private void listenToRingerMode() { final IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); + filter.addAction(ACTION_VOLUME_OVERLAY_CHANGED); mContext.registerReceiver(new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { @@ -340,18 +361,45 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { removeMessages(MSG_RINGER_MODE_CHANGED); sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED)); + } else if (ACTION_VOLUME_OVERLAY_CHANGED.equals(action)) { + int state = (Integer) intent.getExtra("state"); + changeOverlayStyle(state); } } }, filter); } - private boolean isMuted(int streamType) { - if (streamType == STREAM_MASTER) { - return mAudioManager.isMasterMute(); - } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) { - return (mAudioService.getRemoteStreamVolume() <= 0); - } else { - return mAudioManager.isStreamMute(streamType); + private void changeOverlayStyle(int newStyle) { + Log.i("VolumePanel", "changeOverlayStyle : " + newStyle); + // Don't change to the same style + if (newStyle == mCurrentOverlayStyle) return; + switch (newStyle) { + case Settings.System.VOLUME_OVERLAY_SINGLE : + mMoreButton.setVisibility(View.GONE); + mDivider.setVisibility(View.GONE); + mShowCombinedVolumes = false; + mCurrentOverlayStyle = Settings.System.VOLUME_OVERLAY_SINGLE; + break; + case Settings.System.VOLUME_OVERLAY_EXPANDABLE : + mMoreButton.setVisibility(View.VISIBLE); + mDivider.setVisibility(View.VISIBLE); + mShowCombinedVolumes = true; + mCurrentOverlayStyle = Settings.System.VOLUME_OVERLAY_EXPANDABLE; + break; + case Settings.System.VOLUME_OVERLAY_EXPANDED : + mMoreButton.setVisibility(View.GONE); + mDivider.setVisibility(View.GONE); + mShowCombinedVolumes = true; + if (mCurrentOverlayStyle == Settings.System.VOLUME_OVERLAY_NONE) { + addOtherVolumes(); + expand(); + } + mCurrentOverlayStyle = Settings.System.VOLUME_OVERLAY_EXPANDED; + break; + case Settings.System.VOLUME_OVERLAY_NONE : + mShowCombinedVolumes = false; + mCurrentOverlayStyle = Settings.System.VOLUME_OVERLAY_NONE; + break; } } @@ -385,6 +433,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } } + private boolean isMuted(int streamType) { + return mAudioManager.isStreamMute(streamType); + } + private void createSliders() { LayoutInflater inflater = (LayoutInflater) mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -406,6 +458,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie sc.iconRes = streamRes.iconRes; sc.iconMuteRes = streamRes.iconMuteRes; sc.icon.setImageResource(sc.iconRes); + sc.icon.setOnClickListener(this); sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar); int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO || streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0; @@ -442,6 +495,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if (!STREAMS[i].show || streamType == mActiveStreamType) { continue; } + // Skip ring volume for non-phone devices + if (!mVoiceCapable && streamType == AudioManager.STREAM_RING) { + continue; + } StreamControl sc = mStreamControls.get(streamType); mSliderGroup.addView(sc.group); updateSlider(sc); @@ -475,10 +532,22 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private void expand() { final int count = mSliderGroup.getChildCount(); for (int i = 0; i < count; i++) { - mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE); + if (mSliderGroup.getChildAt(i).getVisibility() != View.VISIBLE) { + mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE); + } + } + mMoreButton.setVisibility(View.GONE); + mDivider.setVisibility(View.GONE); + } + + private void hideSlider(int mActiveStreamType) { + final int count = mSliderGroup.getChildCount(); + for (int i = 0; i < count; i++) { + StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag(); + if (mActiveStreamType == sc.streamType) { + mSliderGroup.getChildAt(i).setVisibility(View.GONE); + } } - mMoreButton.setVisibility(View.INVISIBLE); - mDivider.setVisibility(View.INVISIBLE); } private void collapse() { @@ -578,8 +647,12 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { synchronized (this) { - if (mActiveStreamType != streamType) { - reorderSliders(streamType); + if (mActiveStreamType == -1 || streamType != mActiveStreamType) { + if (streamType != mActiveStreamType && + mCurrentOverlayStyle == Settings.System.VOLUME_OVERLAY_EXPANDABLE) { + hideSlider(mActiveStreamType); + } + reorderSliders(streamType); } onShowVolumeChanged(streamType, flags); } @@ -709,17 +782,32 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } else { sc.seekbarView.setEnabled(true); } + // If adjusting Ring volume and preference is to link it to Notification + if (streamType == mAudioManager.STREAM_RING && + System.getInt(mContext.getContentResolver(),System.VOLUME_LINK_NOTIFICATION, 1) == 1) { + StreamControl notifySc = mStreamControls.get(mAudioManager.STREAM_NOTIFICATION); + if (index > notifySc.seekbarView.getMax()) { + notifySc.seekbarView.setProgress(notifySc.seekbarView.getMax()); + } else { + notifySc.seekbarView.setProgress(index); + } + } } - if (!mDialog.isShowing()) { + // Only Show if style needs it + if (!mDialog.isShowing() && mCurrentOverlayStyle != Settings.System.VOLUME_OVERLAY_NONE) { int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType; // when the stream is for remote playback, use -1 to reset the stream type evaluation mAudioManager.forceVolumeControlStream(stream); mDialog.setContentView(mView); // Showing dialog - use collapsed state - if (mShowCombinedVolumes) { + if (mShowCombinedVolumes && mCurrentOverlayStyle != Settings.System.VOLUME_OVERLAY_EXPANDED) { collapse(); } + // If just changed the style and we need to expand + if (mCurrentOverlayStyle == Settings.System.VOLUME_OVERLAY_EXPANDED) { + expand(); + } mDialog.show(); } @@ -995,13 +1083,34 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie sendMessage(obtainMessage(MSG_TIMEOUT)); } - public void onProgressChanged(SeekBar seekBar, int progress, - boolean fromUser) { + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { final Object tag = seekBar.getTag(); if (fromUser && tag instanceof StreamControl) { StreamControl sc = (StreamControl) tag; if (getStreamVolume(sc.streamType) != progress) { setStreamVolume(sc.streamType, progress, 0); + // if audio is linked then adjust other one if change made by user + if (fromUser && System.getInt(mContext.getContentResolver(),System.VOLUME_LINK_NOTIFICATION, 1) == 1) { + if (sc.streamType == AudioManager.STREAM_RING) { + StreamControl notifySc = mStreamControls.get(AudioManager.STREAM_NOTIFICATION); + if (notifySc != null) { + if (progress > notifySc.seekbarView.getMax()) { + notifySc.seekbarView.setProgress(notifySc.seekbarView.getMax()); + } else { + notifySc.seekbarView.setProgress(progress); + } + } + } else if (sc.streamType == AudioManager.STREAM_NOTIFICATION) { + StreamControl phoneSc = mStreamControls.get(AudioManager.STREAM_RING); + if (phoneSc != null) { + if (progress > phoneSc.seekbarView.getMax()) { + phoneSc.seekbarView.setProgress(phoneSc.seekbarView.getMax()); + } else { + phoneSc.seekbarView.setProgress(progress); + } + } + } + } } } resetTimeout(); @@ -1027,6 +1136,12 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie public void onClick(View v) { if (v == mMoreButton) { expand(); + } else if (v instanceof ImageView) { + Intent volumeSettings = new Intent(android.provider.Settings.ACTION_SOUND_SETTINGS); + volumeSettings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + forceTimeout(); + mContext.startActivity(volumeSettings); + return; } resetTimeout(); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 26739b3..fd5449c 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -399,6 +399,7 @@ public interface WindowManagerPolicy { public void shutdown(boolean confirm); public void rebootSafeMode(boolean confirm); + public void reboot(); } /** diff --git a/core/java/com/android/internal/util/weather/HttpRetriever.java b/core/java/com/android/internal/util/weather/HttpRetriever.java new file mode 100644 index 0000000..a3417a0 --- /dev/null +++ b/core/java/com/android/internal/util/weather/HttpRetriever.java @@ -0,0 +1,152 @@ +/****************************************************************************** + * Class : HttpConnectHelper.java * + * Main Weather activity, in this demo apps i use API from yahoo, you can * + * use other weather web service which you prefer * + * * + * Version : v1.0 * + * Date : May 09, 2011 * + * Copyright (c)-2011 DatNQ some right reserved * + * You can distribute, modify or what ever you want but WITHOUT ANY WARRANTY * + * Be honest by keep credit of this file * + * * + * If you have any concern, feel free to contact with me via email, i will * + * check email in free time * + * Email: nguyendatnq@gmail.com * + * ---------------------------------------------------------------------------* + * Modification Logs: * + * KEYCHANGE DATE AUTHOR DESCRIPTION * + * ---------------------------------------------------------------------------* + * ------- May 09, 2011 DatNQ Create new * + ******************************************************************************/ + +/** + * Modification into Android-internal HttpRetreiver.java + * Copyright (C) 2012 The AOKP Project + */ + + +package com.android.internal.util.weather; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.util.EntityUtils; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; + +import android.util.Log; + +public class HttpRetriever { + + private final String TAG = getClass().getSimpleName(); + private DefaultHttpClient client = new DefaultHttpClient(); + private HttpURLConnection httpConnection; + + public String retrieve(String url) { + HttpGet get = new HttpGet(url); + try { + HttpResponse getResponse = client.execute(get); + HttpEntity getResponseEntity = getResponse.getEntity(); + if (getResponseEntity != null) { + String response = EntityUtils.toString(getResponseEntity); + return response; + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private void requestConnectServer(String strURL) throws IOException { + httpConnection = (HttpURLConnection) new URL(strURL).openConnection(); + httpConnection.connect(); + + if (httpConnection.getResponseCode() != HttpURLConnection.HTTP_OK) { + Log.e(TAG, "Something wrong with connection"); + httpConnection.disconnect(); + throw new IOException("Error in connection: " + httpConnection.getResponseCode()); + } + } + + private void requestDisconnect() { + if (httpConnection != null) { + httpConnection.disconnect(); + } + } + + public Document getDocumentFromURL(String strURL) throws IOException { + if (strURL == null) { + Log.e(TAG, "Invalid input URL"); + return null; + } + + // Connect to server, get data and close + requestConnectServer(strURL); + String strDocContent = getDataFromConnection(); + requestDisconnect(); + + if (strDocContent == null) { + Log.e(TAG, "Cannot get XML content"); + return null; + } + + int strContentSize = strDocContent.length(); + StringBuffer strBuff = new StringBuffer(); + strBuff.setLength(strContentSize + 1); + strBuff.append(strDocContent); + ByteArrayInputStream is = new ByteArrayInputStream(strDocContent.getBytes()); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db; + Document docData = null; + + try { + db = dbf.newDocumentBuilder(); + docData = db.parse(is); + } catch (Exception e) { + Log.e(TAG, "Parser data error"); + return null; + } + return docData; + } + + private String getDataFromConnection() throws IOException { + if (httpConnection == null) { + Log.e(TAG, "Connection is null"); + return null; + } + + String strValue = null; + InputStream inputStream = httpConnection.getInputStream(); + if (inputStream == null) { + Log.e(TAG, "Input stream error"); + return null; + } + + StringBuffer strBuf = new StringBuffer(); + BufferedReader buffReader = new BufferedReader(new InputStreamReader(inputStream)); + String strLine = ""; + + while ((strLine = buffReader.readLine()) != null) { + strBuf.append(strLine + "\n"); + strValue += strLine + "\n"; + } + + // Release resource to system + buffReader.close(); + inputStream.close(); + return strBuf.toString(); + } +} diff --git a/core/java/com/android/internal/util/weather/WeatherInfo.java b/core/java/com/android/internal/util/weather/WeatherInfo.java new file mode 100644 index 0000000..2b65785 --- /dev/null +++ b/core/java/com/android/internal/util/weather/WeatherInfo.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 The AOKP 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.internal.util.weather; + +import android.content.Context; +import com.android.internal.R; + +public class WeatherInfo { + + public static final String NODATA = "-"; + + public String city, forecast_date, condition, condition_code, temp, temp_unit, + humidity, wind, wind_dir, speed_unit, low, high; + public long last_sync; + + public WeatherInfo() { + this.city = NODATA; + this.forecast_date = NODATA; + this.condition = NODATA; + this.condition_code = NODATA; + this.temp = NODATA; + this.temp_unit = NODATA; + this.humidity = NODATA; + this.wind = NODATA; + this.wind_dir = NODATA; + this.speed_unit = NODATA; + this.low = NODATA; + this.high = NODATA; + this.last_sync = 0; + } + + public WeatherInfo(Context context, String city, String fdate, String condition, String condition_code, + String temp, String temp_unit, String humidity, + String wind, String wind_dir, String speed_unit, + String low, String high, long last_sync) { + this.city = city; + this.forecast_date = fdate; + this.condition = condition; + this.condition_code = condition_code; + this.humidity = humidity + "%"; + this.wind = calcDirection(context, wind_dir) + " " + trimSpeed(wind) + speed_unit; + this.speed_unit = speed_unit; + this.last_sync = last_sync; + // Only the current temperature gets the temp_unit added. + this.temp_unit = temp_unit; + this.temp = temp + "°" + temp_unit; + this.low = low + "°"; + this.high = high + "°"; + } + + /** + * find the optimal weather string (helper function for translation) + * + * @param conditionCode condition code from Yahoo (this is the main + * identifier which will be used to find a matching translation + * in the project's resources + * @param providedString + * @return either the defaultString (which should be Yahoo's weather + * condition text), or the translated version from resources + */ + public static String getTranslatedConditionString(Context context, int conditionCode, + String providedString) { + int resID = context.getResources().getIdentifier("weather_" + conditionCode, "string", + context.getPackageName()); + return (resID != 0) ? context.getResources().getString(resID) : providedString; + } + + private String calcDirection(Context context, String degrees) { + try { + int deg = Integer.parseInt(degrees); + if (deg >= 338 || deg <= 22) + return context.getResources().getString(R.string.weather_N); + else if (deg < 68) + return context.getResources().getString(R.string.weather_NE); + else if (deg < 113) + return context.getResources().getString(R.string.weather_E); + else if (deg < 158) + return context.getResources().getString(R.string.weather_SE); + else if (deg < 203) + return context.getResources().getString(R.string.weather_S); + else if (deg < 248) + return context.getResources().getString(R.string.weather_SW); + else if (deg < 293) + return context.getResources().getString(R.string.weather_W); + else if (deg < 338) + return context.getResources().getString(R.string.weather_NW); + else + return ""; + } catch (NumberFormatException e) { + return ""; + } + } + + private String trimSpeed(String speed) { + try { + return String.valueOf(Math.round(Float.parseFloat(speed))); + } catch (NumberFormatException e) { + return ""; + } + } +} diff --git a/core/java/com/android/internal/util/weather/WeatherXmlParser.java b/core/java/com/android/internal/util/weather/WeatherXmlParser.java new file mode 100644 index 0000000..a2986fc --- /dev/null +++ b/core/java/com/android/internal/util/weather/WeatherXmlParser.java @@ -0,0 +1,177 @@ +/****************************************************************************** + * Class : YahooWeatherHelper.java * + * Parser helper for Yahoo * + * * + * Version : v1.0 * + * Date : May 06, 2011 * + * Copyright (c)-2011 DatNQ some right reserved * + * You can distribute, modify or what ever you want but WITHOUT ANY WARRANTY * + * Be honest by keep credit of this file * + * * + * If you have any concern, feel free to contact with me via email, i will * + * check email in free time * + * Email: nguyendatnq@gmail.com * + * ---------------------------------------------------------------------------* + * Modification Logs: * + * KEYCHANGE DATE AUTHOR DESCRIPTION * + * ---------------------------------------------------------------------------* + * ------- May 06, 2011 DatNQ Create new * + ******************************************************************************/ +/* + * Modification into Android-internal WeatherXmlParser.java + * Copyright (C) 2012 The AOKP Project + */ + +package com.android.internal.util.weather; + +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import android.content.Context; +import android.util.Log; + +public class WeatherXmlParser { + + protected static final String TAG = "WeatherXmlParser"; + + /** Yahoo attributes */ + private static final String PARAM_YAHOO_LOCATION = "yweather:location"; + private static final String PARAM_YAHOO_UNIT = "yweather:units"; + private static final String PARAM_YAHOO_ATMOSPHERE = "yweather:atmosphere"; + private static final String PARAM_YAHOO_CONDITION = "yweather:condition"; + private static final String PARAM_YAHOO_WIND = "yweather:wind"; + private static final String PARAM_YAHOO_FORECAST = "yweather:forecast"; + + private static final String ATT_YAHOO_CITY = "city"; + private static final String ATT_YAHOO_TEMP = "temp"; + private static final String ATT_YAHOO_CODE = "code"; + private static final String ATT_YAHOO_TEMP_UNIT = "temperature"; + private static final String ATT_YAHOO_HUMIDITY = "humidity"; + private static final String ATT_YAHOO_TEXT = "text"; + private static final String ATT_YAHOO_DATE = "date"; + private static final String ATT_YAHOO_SPEED = "speed"; + private static final String ATT_YAHOO_DIRECTION = "direction"; + private static final String ATT_YAHOO_TODAY_HIGH = "high"; + private static final String ATT_YAHOO_TODAY_LOW = "low"; + + private Context mContext; + + public WeatherXmlParser(Context context) { + mContext = context; + } + + public WeatherInfo parseWeatherResponse(Document docWeather) { + if (docWeather == null) { + Log.e(TAG, "Invalid doc weather"); + return null; + } + + String strCity = null; + String strDate = null; + String strCondition = null; + String strCondition_code = null; + String strTemp = null; + String strTempUnit = null; + String strHumidity = null; + String strWindSpeed = null; + String strWindDir = null; + String strSpeedUnit = null; + String strHigh = null; + String strLow = null; + + try { + Element root = docWeather.getDocumentElement(); + root.normalize(); + + NamedNodeMap locationNode = root.getElementsByTagName(PARAM_YAHOO_LOCATION).item(0) + .getAttributes(); + if (locationNode != null) { + strCity = locationNode.getNamedItem(ATT_YAHOO_CITY).getNodeValue(); + } + + NamedNodeMap unitNode = root.getElementsByTagName(PARAM_YAHOO_UNIT).item(0) + .getAttributes(); + + if (locationNode != null) { + strTempUnit = unitNode.getNamedItem(ATT_YAHOO_TEMP_UNIT).getNodeValue(); + strSpeedUnit = unitNode.getNamedItem(ATT_YAHOO_SPEED).getNodeValue(); + } + + NamedNodeMap atmosNode = root.getElementsByTagName(PARAM_YAHOO_ATMOSPHERE).item(0) + .getAttributes(); + if (atmosNode != null) { + strHumidity = atmosNode.getNamedItem(ATT_YAHOO_HUMIDITY).getNodeValue(); + } + + NamedNodeMap conditionNode = root.getElementsByTagName(PARAM_YAHOO_CONDITION).item(0) + .getAttributes(); + if (conditionNode != null) { + strCondition = conditionNode.getNamedItem(ATT_YAHOO_TEXT).getNodeValue(); + strCondition_code = conditionNode.getNamedItem(ATT_YAHOO_CODE).getNodeValue(); + strCondition = WeatherInfo.getTranslatedConditionString(mContext, Integer.parseInt(strCondition_code), strCondition); + strTemp = conditionNode.getNamedItem(ATT_YAHOO_TEMP).getNodeValue(); + strDate = conditionNode.getNamedItem(ATT_YAHOO_DATE).getNodeValue(); + } + + NamedNodeMap temNode = root.getElementsByTagName(PARAM_YAHOO_WIND).item(0) + .getAttributes(); + if (temNode != null) { + strWindSpeed = temNode.getNamedItem(ATT_YAHOO_SPEED).getNodeValue(); + strWindDir = temNode.getNamedItem(ATT_YAHOO_DIRECTION).getNodeValue(); + } + + NamedNodeMap fcNode = root.getElementsByTagName(PARAM_YAHOO_FORECAST).item(0).getAttributes(); + if (fcNode != null) { + strHigh = fcNode.getNamedItem(ATT_YAHOO_TODAY_HIGH).getNodeValue(); + strLow = fcNode.getNamedItem(ATT_YAHOO_TODAY_LOW).getNodeValue(); + } + } catch (Exception e) { + Log.e(TAG, "Something wrong with parser data: " + e.toString()); + return null; + } + + /* Weather info */ + WeatherInfo yahooWeatherInfo = new WeatherInfo(mContext, strCity, strDate, strCondition, strCondition_code, strTemp, + strTempUnit, strHumidity, strWindSpeed, strWindDir, strSpeedUnit, strLow, strHigh, System.currentTimeMillis()); + + Log.d(TAG, "Weather updated for " + strCity + ": " + strDate + ", " + strCondition + "(" + strCondition_code + + "), " + strTemp + strTempUnit + ", " + strHumidity + "% humidity, " + ", wind: " + strWindDir + " at " + + strWindSpeed + strSpeedUnit + ", low: " + strLow + strTempUnit + " high: " + strHigh + strTempUnit); + + return yahooWeatherInfo; + } + + public String parsePlaceFinderResponse(String response) { + try { + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(new InputSource(new StringReader(response))); + + NodeList resultNodes = doc.getElementsByTagName("Result"); + + Node resultNode = resultNodes.item(0); + NodeList attrsList = resultNode.getChildNodes(); + + for (int i = 0; i < attrsList.getLength(); i++) { + Node node = attrsList.item(i); + Node firstChild = node.getFirstChild(); + if ("woeid".equalsIgnoreCase(node.getNodeName()) && firstChild != null) { + return firstChild.getNodeValue(); + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + } + return null; + } +} diff --git a/core/java/com/android/internal/util/weather/YahooPlaceFinder.java b/core/java/com/android/internal/util/weather/YahooPlaceFinder.java new file mode 100644 index 0000000..fb8efe4 --- /dev/null +++ b/core/java/com/android/internal/util/weather/YahooPlaceFinder.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 The AOKP 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.internal.util.weather; + +import android.content.Context; + +public class YahooPlaceFinder { + + private static final String YAHOO_API_BASE_REV_URL = "http://where.yahooapis.com/geocode?appid=jYkTZp64&q=%1$s,+%2$s&gflags=R"; + private static final String YAHOO_API_BASE_URL = "http://where.yahooapis.com/geocode?appid=jYkTZp64&q=%1$s"; + + public static String reverseGeoCode(Context c, double latitude, double longitude) { + + String url = String.format(YAHOO_API_BASE_REV_URL, String.valueOf(latitude), + String.valueOf(longitude)); + String response = new HttpRetriever().retrieve(url); + return new WeatherXmlParser(c).parsePlaceFinderResponse(response); + + } + + public static String GeoCode(Context c, String location) { + String url = String.format(YAHOO_API_BASE_URL, location).replace(' ', '+'); + String response = new HttpRetriever().retrieve(url); + return new WeatherXmlParser(c).parsePlaceFinderResponse(response); + } + +} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 75fef24..8f419e4 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2012 The CyanogenMod Project (Calendar) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +24,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -31,10 +34,13 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.storage.IMountService; +import android.provider.CalendarContract; import android.provider.Settings; import android.security.KeyStore; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.text.format.DateFormat; +import android.text.format.Time; import android.util.Log; import android.view.IWindowManager; import android.view.View; @@ -47,7 +53,10 @@ import com.google.android.collect.Lists; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; +import java.util.TimeZone; /** * Utilities for the lock pattern and its settings. @@ -1038,6 +1047,166 @@ public class LockPatternUtils { return nextAlarm; } + /** + * @return A formatted string of the next calendar event with a reminder + * (for showing on the lock screen), or null if there is no next event + * within a certain look-ahead time. + */ + public String[] getNextCalendarAlarm(long lookahead, String[] calendars, + boolean remindersOnly) { + long now = System.currentTimeMillis(); + long later = now + lookahead; + + StringBuilder where = new StringBuilder(); + if (remindersOnly) { + where.append(CalendarContract.Events.HAS_ALARM + "=1"); + } + if (calendars != null && calendars.length > 0) { + if (remindersOnly) { + where.append(" AND "); + } + where.append(CalendarContract.Events.CALENDAR_ID + " in ("); + for (int i = 0; i < calendars.length; i++) { + where.append(calendars[i]); + if (i != calendars.length - 1) { + where.append(","); + } + } + where.append(") "); + } + + // Projection array + String[] projection = new String[] { + CalendarContract.Events.TITLE, + CalendarContract.Instances.BEGIN, + CalendarContract.Events.DESCRIPTION, + CalendarContract.Events.EVENT_LOCATION, + CalendarContract.Events.ALL_DAY + }; + + // The indices for the projection array + int TITLE_INDEX = 0; + int BEGIN_TIME_INDEX = 1; + int DESCRIPTION_INDEX = 2; + int LOCATION_INDEX = 3; + int ALL_DAY_INDEX = 4; + + Uri uri = Uri.withAppendedPath(CalendarContract.Instances.CONTENT_URI, + String.format("%d/%d", now, later)); + String[] nextCalendarAlarm = new String[2]; + Cursor cursor = null; + + try { + cursor = mContentResolver.query(uri, projection, + where.toString(), null, "begin ASC"); + + if (cursor != null && cursor.moveToFirst()) { + + String title = cursor.getString(TITLE_INDEX); + long begin = cursor.getLong(BEGIN_TIME_INDEX); + String description = cursor.getString(DESCRIPTION_INDEX); + String location = cursor.getString(LOCATION_INDEX); + boolean allDay = cursor.getInt(ALL_DAY_INDEX) != 0; + + // Check the next event in the case of all day event. As UTC is used for all day + // events, the next event may be the one that actually starts sooner + if (allDay && !cursor.isLast()) { + cursor.moveToNext(); + long nextBegin = cursor.getLong(BEGIN_TIME_INDEX); + if (nextBegin < begin + TimeZone.getDefault().getOffset(begin)) { + title = cursor.getString(TITLE_INDEX); + begin = nextBegin; + description = cursor.getString(DESCRIPTION_INDEX); + location = cursor.getString(LOCATION_INDEX); + allDay = cursor.getInt(ALL_DAY_INDEX) != 0; + } + } + + // Set the event title as the first array item + nextCalendarAlarm[0] = title.toString(); + + // Start building the event details string + // Starting with the date + Date start = new Date(begin); + StringBuilder sb = new StringBuilder(); + + if (allDay) { + SimpleDateFormat sdf = new SimpleDateFormat( + mContext.getString(R.string.abbrev_wday_month_day_no_year)); + // Calendar stores all-day events in UTC -- setting the time zone ensures + // the correct date is shown. + sdf.setTimeZone(TimeZone.getTimeZone(Time.TIMEZONE_UTC)); + sb.append(sdf.format(start)); + } else { + sb.append(DateFormat.format("E", start)); + sb.append(" "); + sb.append(DateFormat.getTimeFormat(mContext).format(start)); + } + + // Add the event location if it should be shown + int showLocation = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.LOCKSCREEN_CALENDAR_SHOW_LOCATION, 0); + if (showLocation != 0 && !TextUtils.isEmpty(location)) { + switch(showLocation) { + case 1: + // Show first line + int end = location.indexOf('\n'); + if(end == -1) { + sb.append(": " + location); + } else { + sb.append(": " + location.substring(0, end)); + } + break; + case 2: + // Show all + sb.append(": " + location); + break; + } + } + + // Add the event description if it should be shown + int showDescription = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.LOCKSCREEN_CALENDAR_SHOW_DESCRIPTION, 0); + if (showDescription != 0 && !TextUtils.isEmpty(description)) { + + // Show the appropriate separator + if (showLocation == 0) { + sb.append(": "); + } else { + sb.append(" - "); + } + + switch(showDescription) { + case 1: + // Show first line + int end = description.indexOf('\n'); + if(end == -1) { + sb.append(description); + } else { + sb.append(description.substring(0, end)); + } + break; + case 2: + // Show all + sb.append(description); + break; + } + } + + // Set the time, location and description as the second array item + nextCalendarAlarm[1] = sb.toString(); + } + } catch (Exception e) { + // Do nothing + } finally { + if (cursor != null) { + cursor.close(); + } + } + + return nextCalendarAlarm; + } + private boolean getBoolean(String secureSettingKey, boolean defaultValue) { try { return getLockSettings().getBoolean(secureSettingKey, defaultValue, @@ -1346,4 +1515,13 @@ public class LockPatternUtils { return false; } + /** + * @hide + * Set the lock-before-unlock option (show widgets before the secure + * unlock screen). See config_enableLockBeforeUnlockScreen + */ + public void setLockBeforeUnlock(boolean enabled) { + setBoolean(Settings.Secure.LOCK_BEFORE_UNLOCK, enabled); + } + } diff --git a/core/res/res/drawable-hdpi/ic_lock_idle_calendar.png b/core/res/res/drawable-hdpi/ic_lock_idle_calendar.png Binary files differnew file mode 100644 index 0000000..05b68b1 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lock_idle_calendar.png diff --git a/core/res/res/drawable-hdpi/ic_lock_profile.png b/core/res/res/drawable-hdpi/ic_lock_profile.png Binary files differnew file mode 100644 index 0000000..7fc4cec --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lock_profile.png diff --git a/core/res/res/drawable-hdpi/ic_lock_reboot.png b/core/res/res/drawable-hdpi/ic_lock_reboot.png Binary files differnew file mode 100644 index 0000000..ca00936 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lock_reboot.png diff --git a/core/res/res/drawable-hdpi/ic_lock_screenshot.png b/core/res/res/drawable-hdpi/ic_lock_screenshot.png Binary files differnew file mode 100644 index 0000000..5ef76f7 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lock_screenshot.png diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_chrome_activated.png b/core/res/res/drawable-hdpi/ic_lockscreen_chrome_activated.png Binary files differnew file mode 100644 index 0000000..75357a0 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lockscreen_chrome_activated.png diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_chrome_normal.png b/core/res/res/drawable-hdpi/ic_lockscreen_chrome_normal.png Binary files differnew file mode 100644 index 0000000..f6f29d1 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lockscreen_chrome_normal.png diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_google_activated.png b/core/res/res/drawable-hdpi/ic_lockscreen_google_activated.png Binary files differindex 2c4847c..7c105e6 100644 --- a/core/res/res/drawable-hdpi/ic_lockscreen_google_activated.png +++ b/core/res/res/drawable-hdpi/ic_lockscreen_google_activated.png diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_google_normal.png b/core/res/res/drawable-hdpi/ic_lockscreen_google_normal.png Binary files differindex 656f3ba..5daf9ed 100644 --- a/core/res/res/drawable-hdpi/ic_lockscreen_google_normal.png +++ b/core/res/res/drawable-hdpi/ic_lockscreen_google_normal.png diff --git a/core/res/res/drawable-hdpi/weather_0.png b/core/res/res/drawable-hdpi/weather_0.png Binary files differnew file mode 100644 index 0000000..9e539b6 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_0.png diff --git a/core/res/res/drawable-hdpi/weather_1.png b/core/res/res/drawable-hdpi/weather_1.png Binary files differnew file mode 100644 index 0000000..9e539b6 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_1.png diff --git a/core/res/res/drawable-hdpi/weather_10.png b/core/res/res/drawable-hdpi/weather_10.png Binary files differnew file mode 100644 index 0000000..daf991e --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_10.png diff --git a/core/res/res/drawable-hdpi/weather_11.png b/core/res/res/drawable-hdpi/weather_11.png Binary files differnew file mode 100644 index 0000000..91a74a5 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_11.png diff --git a/core/res/res/drawable-hdpi/weather_12.png b/core/res/res/drawable-hdpi/weather_12.png Binary files differnew file mode 100644 index 0000000..91a74a5 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_12.png diff --git a/core/res/res/drawable-hdpi/weather_13.png b/core/res/res/drawable-hdpi/weather_13.png Binary files differnew file mode 100644 index 0000000..b2f41a5 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_13.png diff --git a/core/res/res/drawable-hdpi/weather_14.png b/core/res/res/drawable-hdpi/weather_14.png Binary files differnew file mode 100644 index 0000000..b2f41a5 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_14.png diff --git a/core/res/res/drawable-hdpi/weather_15.png b/core/res/res/drawable-hdpi/weather_15.png Binary files differnew file mode 100644 index 0000000..2e92b35 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_15.png diff --git a/core/res/res/drawable-hdpi/weather_16.png b/core/res/res/drawable-hdpi/weather_16.png Binary files differnew file mode 100644 index 0000000..bf4564c --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_16.png diff --git a/core/res/res/drawable-hdpi/weather_17.png b/core/res/res/drawable-hdpi/weather_17.png Binary files differnew file mode 100644 index 0000000..f7948c1 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_17.png diff --git a/core/res/res/drawable-hdpi/weather_18.png b/core/res/res/drawable-hdpi/weather_18.png Binary files differnew file mode 100644 index 0000000..802a80f --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_18.png diff --git a/core/res/res/drawable-hdpi/weather_19.png b/core/res/res/drawable-hdpi/weather_19.png Binary files differnew file mode 100644 index 0000000..b67ba5e --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_19.png diff --git a/core/res/res/drawable-hdpi/weather_2.png b/core/res/res/drawable-hdpi/weather_2.png Binary files differnew file mode 100644 index 0000000..9e539b6 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_2.png diff --git a/core/res/res/drawable-hdpi/weather_20.png b/core/res/res/drawable-hdpi/weather_20.png Binary files differnew file mode 100644 index 0000000..fd40f33 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_20.png diff --git a/core/res/res/drawable-hdpi/weather_21.png b/core/res/res/drawable-hdpi/weather_21.png Binary files differnew file mode 100644 index 0000000..8349b21 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_21.png diff --git a/core/res/res/drawable-hdpi/weather_22.png b/core/res/res/drawable-hdpi/weather_22.png Binary files differnew file mode 100644 index 0000000..b67ba5e --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_22.png diff --git a/core/res/res/drawable-hdpi/weather_23.png b/core/res/res/drawable-hdpi/weather_23.png Binary files differnew file mode 100644 index 0000000..ca015be --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_23.png diff --git a/core/res/res/drawable-hdpi/weather_24.png b/core/res/res/drawable-hdpi/weather_24.png Binary files differnew file mode 100644 index 0000000..ca015be --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_24.png diff --git a/core/res/res/drawable-hdpi/weather_25.png b/core/res/res/drawable-hdpi/weather_25.png Binary files differnew file mode 100644 index 0000000..32cb0d8 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_25.png diff --git a/core/res/res/drawable-hdpi/weather_26.png b/core/res/res/drawable-hdpi/weather_26.png Binary files differnew file mode 100644 index 0000000..ce9761a --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_26.png diff --git a/core/res/res/drawable-hdpi/weather_27.png b/core/res/res/drawable-hdpi/weather_27.png Binary files differnew file mode 100644 index 0000000..67433a7 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_27.png diff --git a/core/res/res/drawable-hdpi/weather_28.png b/core/res/res/drawable-hdpi/weather_28.png Binary files differnew file mode 100644 index 0000000..43f3df8 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_28.png diff --git a/core/res/res/drawable-hdpi/weather_29.png b/core/res/res/drawable-hdpi/weather_29.png Binary files differnew file mode 100644 index 0000000..67433a7 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_29.png diff --git a/core/res/res/drawable-hdpi/weather_3.png b/core/res/res/drawable-hdpi/weather_3.png Binary files differnew file mode 100644 index 0000000..708a2e6 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_3.png diff --git a/core/res/res/drawable-hdpi/weather_30.png b/core/res/res/drawable-hdpi/weather_30.png Binary files differnew file mode 100644 index 0000000..43f3df8 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_30.png diff --git a/core/res/res/drawable-hdpi/weather_31.png b/core/res/res/drawable-hdpi/weather_31.png Binary files differnew file mode 100644 index 0000000..d545d77 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_31.png diff --git a/core/res/res/drawable-hdpi/weather_32.png b/core/res/res/drawable-hdpi/weather_32.png Binary files differnew file mode 100644 index 0000000..c55e4b3 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_32.png diff --git a/core/res/res/drawable-hdpi/weather_33.png b/core/res/res/drawable-hdpi/weather_33.png Binary files differnew file mode 100644 index 0000000..d545d77 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_33.png diff --git a/core/res/res/drawable-hdpi/weather_34.png b/core/res/res/drawable-hdpi/weather_34.png Binary files differnew file mode 100644 index 0000000..c55e4b3 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_34.png diff --git a/core/res/res/drawable-hdpi/weather_35.png b/core/res/res/drawable-hdpi/weather_35.png Binary files differnew file mode 100644 index 0000000..3cd1b81 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_35.png diff --git a/core/res/res/drawable-hdpi/weather_36.png b/core/res/res/drawable-hdpi/weather_36.png Binary files differnew file mode 100644 index 0000000..06cb774 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_36.png diff --git a/core/res/res/drawable-hdpi/weather_37.png b/core/res/res/drawable-hdpi/weather_37.png Binary files differnew file mode 100644 index 0000000..8b2c4d1 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_37.png diff --git a/core/res/res/drawable-hdpi/weather_38.png b/core/res/res/drawable-hdpi/weather_38.png Binary files differnew file mode 100644 index 0000000..f0509ef --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_38.png diff --git a/core/res/res/drawable-hdpi/weather_39.png b/core/res/res/drawable-hdpi/weather_39.png Binary files differnew file mode 100644 index 0000000..8b2c4d1 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_39.png diff --git a/core/res/res/drawable-hdpi/weather_4.png b/core/res/res/drawable-hdpi/weather_4.png Binary files differnew file mode 100644 index 0000000..708a2e6 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_4.png diff --git a/core/res/res/drawable-hdpi/weather_40.png b/core/res/res/drawable-hdpi/weather_40.png Binary files differnew file mode 100644 index 0000000..fb3c06d --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_40.png diff --git a/core/res/res/drawable-hdpi/weather_41.png b/core/res/res/drawable-hdpi/weather_41.png Binary files differnew file mode 100644 index 0000000..bf4564c --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_41.png diff --git a/core/res/res/drawable-hdpi/weather_42.png b/core/res/res/drawable-hdpi/weather_42.png Binary files differnew file mode 100644 index 0000000..7b81366 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_42.png diff --git a/core/res/res/drawable-hdpi/weather_43.png b/core/res/res/drawable-hdpi/weather_43.png Binary files differnew file mode 100644 index 0000000..bf4564c --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_43.png diff --git a/core/res/res/drawable-hdpi/weather_44.png b/core/res/res/drawable-hdpi/weather_44.png Binary files differnew file mode 100644 index 0000000..67433a7 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_44.png diff --git a/core/res/res/drawable-hdpi/weather_45.png b/core/res/res/drawable-hdpi/weather_45.png Binary files differnew file mode 100644 index 0000000..708a2e6 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_45.png diff --git a/core/res/res/drawable-hdpi/weather_46.png b/core/res/res/drawable-hdpi/weather_46.png Binary files differnew file mode 100644 index 0000000..b2f41a5 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_46.png diff --git a/core/res/res/drawable-hdpi/weather_47.png b/core/res/res/drawable-hdpi/weather_47.png Binary files differnew file mode 100644 index 0000000..f0509ef --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_47.png diff --git a/core/res/res/drawable-hdpi/weather_5.png b/core/res/res/drawable-hdpi/weather_5.png Binary files differnew file mode 100644 index 0000000..802a80f --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_5.png diff --git a/core/res/res/drawable-hdpi/weather_6.png b/core/res/res/drawable-hdpi/weather_6.png Binary files differnew file mode 100644 index 0000000..802a80f --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_6.png diff --git a/core/res/res/drawable-hdpi/weather_7.png b/core/res/res/drawable-hdpi/weather_7.png Binary files differnew file mode 100644 index 0000000..802a80f --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_7.png diff --git a/core/res/res/drawable-hdpi/weather_8.png b/core/res/res/drawable-hdpi/weather_8.png Binary files differnew file mode 100644 index 0000000..e36a680 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_8.png diff --git a/core/res/res/drawable-hdpi/weather_9.png b/core/res/res/drawable-hdpi/weather_9.png Binary files differnew file mode 100644 index 0000000..7651b3e --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_9.png diff --git a/core/res/res/drawable-hdpi/weather_na.png b/core/res/res/drawable-hdpi/weather_na.png Binary files differnew file mode 100644 index 0000000..4a43283 --- /dev/null +++ b/core/res/res/drawable-hdpi/weather_na.png diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_calendar.png b/core/res/res/drawable-mdpi/ic_lock_idle_calendar.png Binary files differnew file mode 100644 index 0000000..2834c8f --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lock_idle_calendar.png diff --git a/core/res/res/drawable-mdpi/ic_lock_profile.png b/core/res/res/drawable-mdpi/ic_lock_profile.png Binary files differnew file mode 100644 index 0000000..d47ba16 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lock_profile.png diff --git a/core/res/res/drawable-mdpi/ic_lock_reboot.png b/core/res/res/drawable-mdpi/ic_lock_reboot.png Binary files differnew file mode 100644 index 0000000..2b125b9 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lock_reboot.png diff --git a/core/res/res/drawable-mdpi/ic_lock_screenshot.png b/core/res/res/drawable-mdpi/ic_lock_screenshot.png Binary files differnew file mode 100644 index 0000000..9996e72 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lock_screenshot.png diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_chrome_activated.png b/core/res/res/drawable-mdpi/ic_lockscreen_chrome_activated.png Binary files differnew file mode 100644 index 0000000..52a4221e --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lockscreen_chrome_activated.png diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_chrome_normal.png b/core/res/res/drawable-mdpi/ic_lockscreen_chrome_normal.png Binary files differnew file mode 100644 index 0000000..1abda2f --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lockscreen_chrome_normal.png diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_google_activated.png b/core/res/res/drawable-mdpi/ic_lockscreen_google_activated.png Binary files differindex 32a68e0..35a3ad5 100644 --- a/core/res/res/drawable-mdpi/ic_lockscreen_google_activated.png +++ b/core/res/res/drawable-mdpi/ic_lockscreen_google_activated.png diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_google_normal.png b/core/res/res/drawable-mdpi/ic_lockscreen_google_normal.png Binary files differindex 2f7efcf..292b0ce 100644 --- a/core/res/res/drawable-mdpi/ic_lockscreen_google_normal.png +++ b/core/res/res/drawable-mdpi/ic_lockscreen_google_normal.png diff --git a/core/res/res/drawable-mdpi/weather_0.png b/core/res/res/drawable-mdpi/weather_0.png Binary files differnew file mode 100644 index 0000000..1553c55 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_0.png diff --git a/core/res/res/drawable-mdpi/weather_1.png b/core/res/res/drawable-mdpi/weather_1.png Binary files differnew file mode 100644 index 0000000..1553c55 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_1.png diff --git a/core/res/res/drawable-mdpi/weather_10.png b/core/res/res/drawable-mdpi/weather_10.png Binary files differnew file mode 100644 index 0000000..c44b8f7 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_10.png diff --git a/core/res/res/drawable-mdpi/weather_11.png b/core/res/res/drawable-mdpi/weather_11.png Binary files differnew file mode 100644 index 0000000..ee01b1cf --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_11.png diff --git a/core/res/res/drawable-mdpi/weather_12.png b/core/res/res/drawable-mdpi/weather_12.png Binary files differnew file mode 100644 index 0000000..ee01b1cf --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_12.png diff --git a/core/res/res/drawable-mdpi/weather_13.png b/core/res/res/drawable-mdpi/weather_13.png Binary files differnew file mode 100644 index 0000000..c922139 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_13.png diff --git a/core/res/res/drawable-mdpi/weather_14.png b/core/res/res/drawable-mdpi/weather_14.png Binary files differnew file mode 100644 index 0000000..c922139 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_14.png diff --git a/core/res/res/drawable-mdpi/weather_15.png b/core/res/res/drawable-mdpi/weather_15.png Binary files differnew file mode 100644 index 0000000..27dbc3f --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_15.png diff --git a/core/res/res/drawable-mdpi/weather_16.png b/core/res/res/drawable-mdpi/weather_16.png Binary files differnew file mode 100644 index 0000000..d575139 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_16.png diff --git a/core/res/res/drawable-mdpi/weather_17.png b/core/res/res/drawable-mdpi/weather_17.png Binary files differnew file mode 100644 index 0000000..7ac7828 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_17.png diff --git a/core/res/res/drawable-mdpi/weather_18.png b/core/res/res/drawable-mdpi/weather_18.png Binary files differnew file mode 100644 index 0000000..a1ae654 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_18.png diff --git a/core/res/res/drawable-mdpi/weather_19.png b/core/res/res/drawable-mdpi/weather_19.png Binary files differnew file mode 100644 index 0000000..b750e26 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_19.png diff --git a/core/res/res/drawable-mdpi/weather_2.png b/core/res/res/drawable-mdpi/weather_2.png Binary files differnew file mode 100644 index 0000000..1553c55 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_2.png diff --git a/core/res/res/drawable-mdpi/weather_20.png b/core/res/res/drawable-mdpi/weather_20.png Binary files differnew file mode 100644 index 0000000..8790ddd --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_20.png diff --git a/core/res/res/drawable-mdpi/weather_21.png b/core/res/res/drawable-mdpi/weather_21.png Binary files differnew file mode 100644 index 0000000..242ab7a --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_21.png diff --git a/core/res/res/drawable-mdpi/weather_22.png b/core/res/res/drawable-mdpi/weather_22.png Binary files differnew file mode 100644 index 0000000..b750e26 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_22.png diff --git a/core/res/res/drawable-mdpi/weather_23.png b/core/res/res/drawable-mdpi/weather_23.png Binary files differnew file mode 100644 index 0000000..ddefbf1 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_23.png diff --git a/core/res/res/drawable-mdpi/weather_24.png b/core/res/res/drawable-mdpi/weather_24.png Binary files differnew file mode 100644 index 0000000..ddefbf1 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_24.png diff --git a/core/res/res/drawable-mdpi/weather_25.png b/core/res/res/drawable-mdpi/weather_25.png Binary files differnew file mode 100644 index 0000000..766d094 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_25.png diff --git a/core/res/res/drawable-mdpi/weather_26.png b/core/res/res/drawable-mdpi/weather_26.png Binary files differnew file mode 100644 index 0000000..1445f9e --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_26.png diff --git a/core/res/res/drawable-mdpi/weather_27.png b/core/res/res/drawable-mdpi/weather_27.png Binary files differnew file mode 100644 index 0000000..3835d5b --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_27.png diff --git a/core/res/res/drawable-mdpi/weather_28.png b/core/res/res/drawable-mdpi/weather_28.png Binary files differnew file mode 100644 index 0000000..a362aaf --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_28.png diff --git a/core/res/res/drawable-mdpi/weather_29.png b/core/res/res/drawable-mdpi/weather_29.png Binary files differnew file mode 100644 index 0000000..3835d5b --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_29.png diff --git a/core/res/res/drawable-mdpi/weather_3.png b/core/res/res/drawable-mdpi/weather_3.png Binary files differnew file mode 100644 index 0000000..0fd0158 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_3.png diff --git a/core/res/res/drawable-mdpi/weather_30.png b/core/res/res/drawable-mdpi/weather_30.png Binary files differnew file mode 100644 index 0000000..a362aaf --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_30.png diff --git a/core/res/res/drawable-mdpi/weather_31.png b/core/res/res/drawable-mdpi/weather_31.png Binary files differnew file mode 100644 index 0000000..886b845 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_31.png diff --git a/core/res/res/drawable-mdpi/weather_32.png b/core/res/res/drawable-mdpi/weather_32.png Binary files differnew file mode 100644 index 0000000..4e6827a --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_32.png diff --git a/core/res/res/drawable-mdpi/weather_33.png b/core/res/res/drawable-mdpi/weather_33.png Binary files differnew file mode 100644 index 0000000..886b845 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_33.png diff --git a/core/res/res/drawable-mdpi/weather_34.png b/core/res/res/drawable-mdpi/weather_34.png Binary files differnew file mode 100644 index 0000000..4e6827a --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_34.png diff --git a/core/res/res/drawable-mdpi/weather_35.png b/core/res/res/drawable-mdpi/weather_35.png Binary files differnew file mode 100644 index 0000000..b8ba7c4 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_35.png diff --git a/core/res/res/drawable-mdpi/weather_36.png b/core/res/res/drawable-mdpi/weather_36.png Binary files differnew file mode 100644 index 0000000..005295a --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_36.png diff --git a/core/res/res/drawable-mdpi/weather_37.png b/core/res/res/drawable-mdpi/weather_37.png Binary files differnew file mode 100644 index 0000000..290e2a2 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_37.png diff --git a/core/res/res/drawable-mdpi/weather_38.png b/core/res/res/drawable-mdpi/weather_38.png Binary files differnew file mode 100644 index 0000000..92785f5 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_38.png diff --git a/core/res/res/drawable-mdpi/weather_39.png b/core/res/res/drawable-mdpi/weather_39.png Binary files differnew file mode 100644 index 0000000..290e2a2 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_39.png diff --git a/core/res/res/drawable-mdpi/weather_4.png b/core/res/res/drawable-mdpi/weather_4.png Binary files differnew file mode 100644 index 0000000..0fd0158 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_4.png diff --git a/core/res/res/drawable-mdpi/weather_40.png b/core/res/res/drawable-mdpi/weather_40.png Binary files differnew file mode 100644 index 0000000..25fbd20 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_40.png diff --git a/core/res/res/drawable-mdpi/weather_41.png b/core/res/res/drawable-mdpi/weather_41.png Binary files differnew file mode 100644 index 0000000..d575139 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_41.png diff --git a/core/res/res/drawable-mdpi/weather_42.png b/core/res/res/drawable-mdpi/weather_42.png Binary files differnew file mode 100644 index 0000000..f259805 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_42.png diff --git a/core/res/res/drawable-mdpi/weather_43.png b/core/res/res/drawable-mdpi/weather_43.png Binary files differnew file mode 100644 index 0000000..d575139 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_43.png diff --git a/core/res/res/drawable-mdpi/weather_44.png b/core/res/res/drawable-mdpi/weather_44.png Binary files differnew file mode 100644 index 0000000..3835d5b --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_44.png diff --git a/core/res/res/drawable-mdpi/weather_45.png b/core/res/res/drawable-mdpi/weather_45.png Binary files differnew file mode 100644 index 0000000..0fd0158 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_45.png diff --git a/core/res/res/drawable-mdpi/weather_46.png b/core/res/res/drawable-mdpi/weather_46.png Binary files differnew file mode 100644 index 0000000..c922139 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_46.png diff --git a/core/res/res/drawable-mdpi/weather_47.png b/core/res/res/drawable-mdpi/weather_47.png Binary files differnew file mode 100644 index 0000000..92785f5 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_47.png diff --git a/core/res/res/drawable-mdpi/weather_5.png b/core/res/res/drawable-mdpi/weather_5.png Binary files differnew file mode 100644 index 0000000..a1ae654 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_5.png diff --git a/core/res/res/drawable-mdpi/weather_6.png b/core/res/res/drawable-mdpi/weather_6.png Binary files differnew file mode 100644 index 0000000..a1ae654 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_6.png diff --git a/core/res/res/drawable-mdpi/weather_7.png b/core/res/res/drawable-mdpi/weather_7.png Binary files differnew file mode 100644 index 0000000..a1ae654 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_7.png diff --git a/core/res/res/drawable-mdpi/weather_8.png b/core/res/res/drawable-mdpi/weather_8.png Binary files differnew file mode 100644 index 0000000..37a339e --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_8.png diff --git a/core/res/res/drawable-mdpi/weather_9.png b/core/res/res/drawable-mdpi/weather_9.png Binary files differnew file mode 100644 index 0000000..0a9c8c3 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_9.png diff --git a/core/res/res/drawable-mdpi/weather_na.png b/core/res/res/drawable-mdpi/weather_na.png Binary files differnew file mode 100644 index 0000000..48c2879 --- /dev/null +++ b/core/res/res/drawable-mdpi/weather_na.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_idle_calendar.png b/core/res/res/drawable-xhdpi/ic_lock_idle_calendar.png Binary files differnew file mode 100644 index 0000000..5ae7782 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lock_idle_calendar.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_profile.png b/core/res/res/drawable-xhdpi/ic_lock_profile.png Binary files differnew file mode 100644 index 0000000..4c9472c --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lock_profile.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_reboot.png b/core/res/res/drawable-xhdpi/ic_lock_reboot.png Binary files differnew file mode 100644 index 0000000..653970f --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lock_reboot.png diff --git a/core/res/res/drawable-xhdpi/ic_lock_screenshot.png b/core/res/res/drawable-xhdpi/ic_lock_screenshot.png Binary files differnew file mode 100644 index 0000000..7e4c0ee --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lock_screenshot.png diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_chrome_activated.png b/core/res/res/drawable-xhdpi/ic_lockscreen_chrome_activated.png Binary files differnew file mode 100644 index 0000000..ea90ef5 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lockscreen_chrome_activated.png diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_chrome_normal.png b/core/res/res/drawable-xhdpi/ic_lockscreen_chrome_normal.png Binary files differnew file mode 100644 index 0000000..e49b962 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lockscreen_chrome_normal.png diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_google_activated.png b/core/res/res/drawable-xhdpi/ic_lockscreen_google_activated.png Binary files differindex d643f83..de9eec8 100644 --- a/core/res/res/drawable-xhdpi/ic_lockscreen_google_activated.png +++ b/core/res/res/drawable-xhdpi/ic_lockscreen_google_activated.png diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_google_normal.png b/core/res/res/drawable-xhdpi/ic_lockscreen_google_normal.png Binary files differindex 9a9bf68..f45db74 100644 --- a/core/res/res/drawable-xhdpi/ic_lockscreen_google_normal.png +++ b/core/res/res/drawable-xhdpi/ic_lockscreen_google_normal.png diff --git a/core/res/res/drawable-xhdpi/weather_0.png b/core/res/res/drawable-xhdpi/weather_0.png Binary files differnew file mode 100644 index 0000000..335af4b --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_0.png diff --git a/core/res/res/drawable-xhdpi/weather_1.png b/core/res/res/drawable-xhdpi/weather_1.png Binary files differnew file mode 100644 index 0000000..335af4b --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_1.png diff --git a/core/res/res/drawable-xhdpi/weather_10.png b/core/res/res/drawable-xhdpi/weather_10.png Binary files differnew file mode 100644 index 0000000..9432a13 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_10.png diff --git a/core/res/res/drawable-xhdpi/weather_11.png b/core/res/res/drawable-xhdpi/weather_11.png Binary files differnew file mode 100644 index 0000000..3f7c5ad --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_11.png diff --git a/core/res/res/drawable-xhdpi/weather_12.png b/core/res/res/drawable-xhdpi/weather_12.png Binary files differnew file mode 100644 index 0000000..3f7c5ad --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_12.png diff --git a/core/res/res/drawable-xhdpi/weather_13.png b/core/res/res/drawable-xhdpi/weather_13.png Binary files differnew file mode 100644 index 0000000..3f9e500 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_13.png diff --git a/core/res/res/drawable-xhdpi/weather_14.png b/core/res/res/drawable-xhdpi/weather_14.png Binary files differnew file mode 100644 index 0000000..3f9e500 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_14.png diff --git a/core/res/res/drawable-xhdpi/weather_15.png b/core/res/res/drawable-xhdpi/weather_15.png Binary files differnew file mode 100644 index 0000000..2ad1e46 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_15.png diff --git a/core/res/res/drawable-xhdpi/weather_16.png b/core/res/res/drawable-xhdpi/weather_16.png Binary files differnew file mode 100644 index 0000000..4bad358 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_16.png diff --git a/core/res/res/drawable-xhdpi/weather_17.png b/core/res/res/drawable-xhdpi/weather_17.png Binary files differnew file mode 100644 index 0000000..6fced6c --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_17.png diff --git a/core/res/res/drawable-xhdpi/weather_18.png b/core/res/res/drawable-xhdpi/weather_18.png Binary files differnew file mode 100644 index 0000000..aae6d11 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_18.png diff --git a/core/res/res/drawable-xhdpi/weather_19.png b/core/res/res/drawable-xhdpi/weather_19.png Binary files differnew file mode 100644 index 0000000..d8e5e82 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_19.png diff --git a/core/res/res/drawable-xhdpi/weather_2.png b/core/res/res/drawable-xhdpi/weather_2.png Binary files differnew file mode 100644 index 0000000..335af4b --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_2.png diff --git a/core/res/res/drawable-xhdpi/weather_20.png b/core/res/res/drawable-xhdpi/weather_20.png Binary files differnew file mode 100644 index 0000000..73cbc39 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_20.png diff --git a/core/res/res/drawable-xhdpi/weather_21.png b/core/res/res/drawable-xhdpi/weather_21.png Binary files differnew file mode 100644 index 0000000..800f484 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_21.png diff --git a/core/res/res/drawable-xhdpi/weather_22.png b/core/res/res/drawable-xhdpi/weather_22.png Binary files differnew file mode 100644 index 0000000..d8e5e82 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_22.png diff --git a/core/res/res/drawable-xhdpi/weather_23.png b/core/res/res/drawable-xhdpi/weather_23.png Binary files differnew file mode 100644 index 0000000..be446c2 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_23.png diff --git a/core/res/res/drawable-xhdpi/weather_24.png b/core/res/res/drawable-xhdpi/weather_24.png Binary files differnew file mode 100644 index 0000000..be446c2 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_24.png diff --git a/core/res/res/drawable-xhdpi/weather_25.png b/core/res/res/drawable-xhdpi/weather_25.png Binary files differnew file mode 100644 index 0000000..63f019a --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_25.png diff --git a/core/res/res/drawable-xhdpi/weather_26.png b/core/res/res/drawable-xhdpi/weather_26.png Binary files differnew file mode 100644 index 0000000..3eb5734 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_26.png diff --git a/core/res/res/drawable-xhdpi/weather_27.png b/core/res/res/drawable-xhdpi/weather_27.png Binary files differnew file mode 100644 index 0000000..abf4096 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_27.png diff --git a/core/res/res/drawable-xhdpi/weather_28.png b/core/res/res/drawable-xhdpi/weather_28.png Binary files differnew file mode 100644 index 0000000..0cbae30 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_28.png diff --git a/core/res/res/drawable-xhdpi/weather_29.png b/core/res/res/drawable-xhdpi/weather_29.png Binary files differnew file mode 100644 index 0000000..abf4096 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_29.png diff --git a/core/res/res/drawable-xhdpi/weather_3.png b/core/res/res/drawable-xhdpi/weather_3.png Binary files differnew file mode 100644 index 0000000..7dda34c --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_3.png diff --git a/core/res/res/drawable-xhdpi/weather_30.png b/core/res/res/drawable-xhdpi/weather_30.png Binary files differnew file mode 100644 index 0000000..0cbae30 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_30.png diff --git a/core/res/res/drawable-xhdpi/weather_31.png b/core/res/res/drawable-xhdpi/weather_31.png Binary files differnew file mode 100644 index 0000000..16f2d9f --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_31.png diff --git a/core/res/res/drawable-xhdpi/weather_32.png b/core/res/res/drawable-xhdpi/weather_32.png Binary files differnew file mode 100644 index 0000000..409373d --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_32.png diff --git a/core/res/res/drawable-xhdpi/weather_33.png b/core/res/res/drawable-xhdpi/weather_33.png Binary files differnew file mode 100644 index 0000000..16f2d9f --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_33.png diff --git a/core/res/res/drawable-xhdpi/weather_34.png b/core/res/res/drawable-xhdpi/weather_34.png Binary files differnew file mode 100644 index 0000000..409373d --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_34.png diff --git a/core/res/res/drawable-xhdpi/weather_35.png b/core/res/res/drawable-xhdpi/weather_35.png Binary files differnew file mode 100644 index 0000000..445b884 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_35.png diff --git a/core/res/res/drawable-xhdpi/weather_36.png b/core/res/res/drawable-xhdpi/weather_36.png Binary files differnew file mode 100644 index 0000000..a424ad6 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_36.png diff --git a/core/res/res/drawable-xhdpi/weather_37.png b/core/res/res/drawable-xhdpi/weather_37.png Binary files differnew file mode 100644 index 0000000..e98c8b8 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_37.png diff --git a/core/res/res/drawable-xhdpi/weather_38.png b/core/res/res/drawable-xhdpi/weather_38.png Binary files differnew file mode 100644 index 0000000..b47249c --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_38.png diff --git a/core/res/res/drawable-xhdpi/weather_39.png b/core/res/res/drawable-xhdpi/weather_39.png Binary files differnew file mode 100644 index 0000000..e98c8b8 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_39.png diff --git a/core/res/res/drawable-xhdpi/weather_4.png b/core/res/res/drawable-xhdpi/weather_4.png Binary files differnew file mode 100644 index 0000000..7dda34c --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_4.png diff --git a/core/res/res/drawable-xhdpi/weather_40.png b/core/res/res/drawable-xhdpi/weather_40.png Binary files differnew file mode 100644 index 0000000..1505fea --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_40.png diff --git a/core/res/res/drawable-xhdpi/weather_41.png b/core/res/res/drawable-xhdpi/weather_41.png Binary files differnew file mode 100644 index 0000000..4bad358 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_41.png diff --git a/core/res/res/drawable-xhdpi/weather_42.png b/core/res/res/drawable-xhdpi/weather_42.png Binary files differnew file mode 100644 index 0000000..196026d --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_42.png diff --git a/core/res/res/drawable-xhdpi/weather_43.png b/core/res/res/drawable-xhdpi/weather_43.png Binary files differnew file mode 100644 index 0000000..4bad358 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_43.png diff --git a/core/res/res/drawable-xhdpi/weather_44.png b/core/res/res/drawable-xhdpi/weather_44.png Binary files differnew file mode 100644 index 0000000..abf4096 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_44.png diff --git a/core/res/res/drawable-xhdpi/weather_45.png b/core/res/res/drawable-xhdpi/weather_45.png Binary files differnew file mode 100644 index 0000000..7dda34c --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_45.png diff --git a/core/res/res/drawable-xhdpi/weather_46.png b/core/res/res/drawable-xhdpi/weather_46.png Binary files differnew file mode 100644 index 0000000..3f9e500 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_46.png diff --git a/core/res/res/drawable-xhdpi/weather_47.png b/core/res/res/drawable-xhdpi/weather_47.png Binary files differnew file mode 100644 index 0000000..b47249c --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_47.png diff --git a/core/res/res/drawable-xhdpi/weather_5.png b/core/res/res/drawable-xhdpi/weather_5.png Binary files differnew file mode 100644 index 0000000..aae6d11 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_5.png diff --git a/core/res/res/drawable-xhdpi/weather_6.png b/core/res/res/drawable-xhdpi/weather_6.png Binary files differnew file mode 100644 index 0000000..aae6d11 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_6.png diff --git a/core/res/res/drawable-xhdpi/weather_7.png b/core/res/res/drawable-xhdpi/weather_7.png Binary files differnew file mode 100644 index 0000000..aae6d11 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_7.png diff --git a/core/res/res/drawable-xhdpi/weather_8.png b/core/res/res/drawable-xhdpi/weather_8.png Binary files differnew file mode 100644 index 0000000..1a7e699 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_8.png diff --git a/core/res/res/drawable-xhdpi/weather_9.png b/core/res/res/drawable-xhdpi/weather_9.png Binary files differnew file mode 100644 index 0000000..0da4419 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_9.png diff --git a/core/res/res/drawable-xhdpi/weather_na.png b/core/res/res/drawable-xhdpi/weather_na.png Binary files differnew file mode 100644 index 0000000..8125ce2 --- /dev/null +++ b/core/res/res/drawable-xhdpi/weather_na.png diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml index c6ddd1b..c1ea69c 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml @@ -2,6 +2,7 @@ <!-- ** ** Copyright 2010, The Android Open Source Project +** Copyright 2012, The CyanogenMod Project (Weather, Calendar) ** ** Licensed under the Apache License, Version 2.0 (the "License") ** you may not use this file except in compliance with the License. @@ -38,12 +39,108 @@ android:visibility="gone" /> + <!-- CyanogenMod Weather Panel --> + <RelativeLayout + android:id="@+id/weather_panel" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:orientation="horizontal" > + + <ImageView + android:id="@+id/weather_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:paddingLeft="8dip" + android:paddingRight="8dip" + android:src="@android:drawable/ic_dialog_alert" /> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toLeftOf="@id/weather_image" + android:orientation="horizontal" + android:padding="4dip" > + + <TextView + android:id="@+id/weather_city" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:textStyle="bold" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textColor="?android:attr/textColorPrimary"/> + + <TextView + android:id="@+id/weather_condition" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_city" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + <TextView + android:id="@+id/update_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_condition" + android:layout_alignParentRight="true" + android:gravity="right" + android:textSize="6sp" + android:textColor="?android:attr/textColorSecondary" /> + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/weather_temps_panel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/weather_image" + android:orientation="horizontal" + android:layout_centerVertical="true" > + + <TextView + android:id="@+id/weather_temp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textSize="20sp" + android:textColor="?android:attr/textColorPrimary" /> + + <View + android:id="@+id/weather_divider" + android:layout_below="@id/weather_temp" + android:layout_width="44dip" + android:layout_height="1dip" + android:background="@android:drawable/divider_horizontal_dark" /> + + <TextView + android:id="@+id/weather_low_high" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_divider" + android:layout_centerHorizontal="true" + android:paddingTop="2dip" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + </RelativeLayout> + + </RelativeLayout> + <com.android.internal.widget.DigitalClock android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - > + android:layout_marginBottom="8dip" > <!-- Because we can't have multi-tone fonts, we render two TextViews, one on top of the other. Hence the redundant layout... --> @@ -120,4 +217,69 @@ android:textColor="@color/lockscreen_owner_info" android:visibility="invisible" /> + + <!-- CyanogenMod Calendar Panel --> + <LinearLayout + android:id="@+id/calendar_panel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minHeight="28dip" + android:layout_gravity="center_horizontal" + android:gravity="center_vertical" + android:layout_marginTop="10dip" + android:paddingLeft="12dip" + android:paddingRight="12dip" > + + <ImageView + android:layout_width="36dp" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:gravity="center" + android:src="@drawable/ic_lock_idle_calendar" /> + + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:paddingTop="6dip" + android:paddingBottom="6dip" + android:background="@android:drawable/divider_horizontal_dark" /> + + <RelativeLayout + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:paddingTop="6dip" + android:paddingBottom="6dip" + android:paddingLeft="4dip" > + + <TextView + android:id="@+id/calendar_event_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textSize="14sp" + android:textColor="?android:attr/textColorPrimary" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> + + <TextView + android:id="@+id/calendar_event_details" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/calendar_event_title" + android:layout_alignLeft="@id/calendar_event_title" + android:textSize="12sp" + android:singleLine="true" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:scrollHorizontally="true" + android:focusable="true" + android:focusableInTouchMode="true" + android:fadingEdge="horizontal" + android:textColor="?android:attr/textColorSecondary" /> + + </RelativeLayout> + + </LinearLayout> + </LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml index 765dc95..5929529 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml @@ -2,6 +2,7 @@ <!-- ** ** Copyright 2010, The Android Open Source Project +** Copyright 2012, The CyanogenMod Project (Weather, Calendar) ** ** Licensed under the Apache License, Version 2.0 (the "License") ** you may not use this file except in compliance with the License. @@ -40,11 +41,108 @@ android:visibility="gone" /> + <!-- CyanogenMod Weather Panel --> + <RelativeLayout + android:id="@+id/weather_panel" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:orientation="horizontal" > + + <ImageView + android:id="@+id/weather_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:paddingLeft="8dip" + android:paddingRight="8dip" + android:src="@android:drawable/ic_dialog_alert" /> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toLeftOf="@id/weather_image" + android:orientation="horizontal" + android:padding="4dip" > + + <TextView + android:id="@+id/weather_city" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:textStyle="bold" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textColor="?android:attr/textColorPrimary"/> + + <TextView + android:id="@+id/weather_condition" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_city" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + <TextView + android:id="@+id/update_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_condition" + android:layout_alignParentRight="true" + android:gravity="right" + android:textSize="6sp" + android:textColor="?android:attr/textColorSecondary" /> + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/weather_temps_panel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/weather_image" + android:orientation="horizontal" + android:layout_centerVertical="true" > + + <TextView + android:id="@+id/weather_temp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textSize="20sp" + android:textColor="?android:attr/textColorPrimary" /> + + <View + android:id="@+id/weather_divider" + android:layout_below="@id/weather_temp" + android:layout_width="44dip" + android:layout_height="1dip" + android:background="@android:drawable/divider_horizontal_dark" /> + + <TextView + android:id="@+id/weather_low_high" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_divider" + android:layout_centerHorizontal="true" + android:paddingTop="2dip" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + </RelativeLayout> + + </RelativeLayout> + <com.android.internal.widget.DigitalClock android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="8dip" - android:layout_marginBottom="8dip"> + android:layout_marginBottom="8dip" > <!-- Because we can't have multi-tone fonts, we render two TextViews, one on top of the other. Hence the redundant layout... --> @@ -120,4 +218,68 @@ android:visibility="invisible" android:textColor="@color/lockscreen_owner_info" /> + + <!-- CyanogenMod Calendar Panel --> + <LinearLayout + android:id="@+id/calendar_panel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dip" + android:minHeight="28dip" + android:gravity="center_vertical" + android:paddingLeft="12dip" + android:paddingRight="12dip" > + + <ImageView + android:layout_width="36dp" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:gravity="center" + android:src="@drawable/ic_lock_idle_calendar" /> + + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:paddingTop="6dip" + android:paddingBottom="6dip" + android:background="@android:drawable/divider_horizontal_dark" /> + + <RelativeLayout + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:paddingTop="6dip" + android:paddingBottom="6dip" + android:paddingLeft="4dip" > + + <TextView + android:id="@+id/calendar_event_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textSize="14sp" + android:textColor="?android:attr/textColorPrimary" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> + + <TextView + android:id="@+id/calendar_event_details" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/calendar_event_title" + android:layout_alignLeft="@id/calendar_event_title" + android:textSize="12sp" + android:singleLine="true" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:scrollHorizontally="true" + android:focusable="true" + android:focusableInTouchMode="true" + android:fadingEdge="horizontal" + android:textColor="?android:attr/textColorSecondary" /> + + </RelativeLayout> + + </LinearLayout> + </LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml index 54381ee..143e24d 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml @@ -2,6 +2,7 @@ <!-- ** ** Copyright 2009, The Android Open Source Project +** Copyright 2012, The CyanogenMod Project (Weather, Calendar) ** ** Licensed under the Apache License, Version 2.0 (the "License") ** you may not use this file except in compliance with the License. @@ -26,6 +27,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/root" android:gravity="center_horizontal"> <com.android.internal.widget.DigitalClock android:id="@+id/time" @@ -102,6 +104,105 @@ android:drawablePadding="4dip" /> + <!-- CyanogenMod Weather Panel --> + <RelativeLayout + android:id="@+id/weather_panel" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingTop="4dip" > + + <ImageView + android:id="@+id/weather_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:paddingLeft="8dip" + android:paddingRight="8dip" + android:src="@android:drawable/ic_dialog_alert" /> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toLeftOf="@id/weather_image" + android:orientation="horizontal" + android:padding="4dip" > + + <TextView + android:id="@+id/weather_city" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:textStyle="bold" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textColor="?android:attr/textColorPrimary"/> + + <TextView + android:id="@+id/weather_condition" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_city" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + <TextView + android:id="@+id/update_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="right" + android:layout_below="@id/weather_condition" + android:layout_alignParentRight="true" + android:textSize="6sp" + android:textColor="?android:attr/textColorSecondary" /> + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/weather_temps_panel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/weather_image" + android:orientation="horizontal" + android:layout_centerVertical="true" > + + <TextView + android:id="@+id/weather_temp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textSize="20sp" + android:textColor="?android:attr/textColorPrimary" /> + + <View + android:id="@+id/weather_divider" + android:layout_below="@id/weather_temp" + android:layout_width="44dip" + android:layout_height="1dip" + android:background="@android:drawable/divider_horizontal_dark" /> + + <TextView + android:id="@+id/weather_low_high" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_divider" + android:layout_centerHorizontal="true" + android:paddingTop="2dip" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + </RelativeLayout> + + </RelativeLayout> + <Space android:layout_gravity="fill" /> <!-- emergency call button shown when sim is PUKd and tab_selector is hidden --> @@ -123,6 +224,68 @@ android:layout_width="match_parent" android:layout_height="302dip"> + <!-- CyanogenMod Calendar Panel --> + <LinearLayout + android:id="@+id/calendar_panel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="28dip" + android:gravity="center_vertical" + android:paddingLeft="12dip" + android:paddingRight="12dip" > + + <ImageView + android:layout_width="36dp" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:gravity="center" + android:src="@drawable/ic_lock_idle_calendar" /> + + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:paddingTop="6dip" + android:paddingBottom="6dip" + android:background="@android:drawable/divider_horizontal_dark" /> + + <RelativeLayout + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:paddingTop="6dip" + android:paddingBottom="6dip" + android:paddingLeft="4dip" > + + <TextView + android:id="@+id/calendar_event_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textSize="14sp" + android:textColor="?android:attr/textColorPrimary" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> + + <TextView + android:id="@+id/calendar_event_details" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/calendar_event_title" + android:layout_alignLeft="@id/calendar_event_title" + android:textSize="12sp" + android:singleLine="true" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:scrollHorizontally="true" + android:focusable="true" + android:focusableInTouchMode="true" + android:fadingEdge="horizontal" + android:textColor="?android:attr/textColorSecondary" /> + + </RelativeLayout> + + </LinearLayout> + <com.android.internal.widget.multiwaveview.GlowPadView android:id="@+id/unlock_widget" android:orientation="horizontal" @@ -189,7 +352,7 @@ layout="@layout/keyguard_transport_control" android:layout_row="0" android:layout_column="0" - android:layout_rowSpan="4" + android:layout_rowSpan="5" android:layout_columnSpan="1" android:layout_gravity="fill" android:layout_width="0dip" diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml index 7ef9d8b..b151239 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml @@ -30,8 +30,9 @@ <!-- Column 0 --> <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginTop="80dip" + android:layout_marginTop="40dip" android:layout_marginBottom="8dip" + android:layout_columnSpan="3" android:layout_gravity="end"> <!-- Because we can't have multi-tone fonts, we render two TextViews, one on @@ -64,7 +65,7 @@ <TextView android:id="@+id/date" - android:layout_width="0dip" + android:layout_columnSpan="3" android:layout_gravity="fill_horizontal" android:gravity="end" android:layout_marginTop="6dip" @@ -76,6 +77,7 @@ <TextView android:id="@+id/alarm_status" + android:layout_columnSpan="3" android:singleLine="true" android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceMedium" @@ -87,7 +89,7 @@ <TextView android:id="@+id/status1" - android:layout_width="0dip" + android:layout_columnSpan="3" android:layout_gravity="fill_horizontal" android:gravity="end" android:layout_marginTop="4dip" @@ -98,11 +100,110 @@ android:drawablePadding="4dip" /> + <!-- CyanogenMod Weather Panel --> + <RelativeLayout + android:id="@+id/weather_panel" + android:layout_columnSpan="3" + android:gravity="right" + android:layout_gravity="fill_horizontal" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <ImageView + android:id="@+id/weather_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:paddingLeft="8dip" + android:paddingRight="8dip" + android:src="@android:drawable/ic_dialog_alert" /> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toLeftOf="@id/weather_image" + android:orientation="horizontal" + android:padding="4dip" > + + <TextView + android:id="@+id/weather_city" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:textStyle="bold" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textColor="?android:attr/textColorPrimary"/> + + <TextView + android:id="@+id/weather_condition" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_city" + android:layout_alignParentRight="true" + android:gravity="right" + android:singleLine="true" + android:ellipsize="marquee" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + <TextView + android:id="@+id/update_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_condition" + android:layout_alignParentRight="true" + android:gravity="right" + android:textSize="6sp" + android:textColor="?android:attr/textColorSecondary" /> + + </RelativeLayout> + + <RelativeLayout + android:id="@+id/weather_temps_panel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/weather_image" + android:orientation="horizontal" + android:layout_centerVertical="true" > + + <TextView + android:id="@+id/weather_temp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textSize="20sp" + android:textColor="?android:attr/textColorPrimary" /> + + <View + android:id="@+id/weather_divider" + android:layout_below="@id/weather_temp" + android:layout_width="44dip" + android:layout_height="1dip" + android:background="@android:drawable/divider_horizontal_dark" /> + + <TextView + android:id="@+id/weather_low_high" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/weather_divider" + android:layout_centerHorizontal="true" + android:paddingTop="2dip" + android:textSize="12sp" + android:textColor="?android:attr/textColorPrimary" /> + + </RelativeLayout> + + </RelativeLayout> + <Space android:layout_gravity="fill" /> <TextView android:id="@+id/carrier" - android:layout_width="0dip" + android:layout_columnSpan="3" android:layout_gravity="fill_horizontal" android:layout_marginBottom="12dip" android:gravity="end" @@ -131,7 +232,7 @@ <!-- Column 2 --> <com.android.internal.widget.multiwaveview.GlowPadView android:id="@+id/unlock_widget" - android:layout_width="302dip" + android:layout_width="200dip" android:layout_height="match_parent" android:layout_rowSpan="7" android:gravity="start|center_vertical" @@ -157,7 +258,7 @@ android:layout_row="0" android:layout_column="0" android:layout_rowSpan="5" - android:layout_columnSpan="1" + android:layout_columnSpan="3" android:layout_gravity="fill" android:layout_width="0dip" android:layout_height="0dip" diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 1e966f7..0238050 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -419,4 +419,28 @@ <item>TUV</item><!-- 8 --> <item>WXYZ</item><!-- 9 --> </string-array> + + <!-- Defines the shutdown options shown in the reboot dialog. --> + <array name="shutdown_reboot_options" translatable="false"> + <item>@string/reboot_reboot</item> + <item>@string/reboot_recovery</item> + <item>@string/reboot_bootloader</item> + </array> + + <!-- Do not translate. Defines the shutdown actions passed to the kernel. + The first item should be empty for regular reboot. --> + <string-array name="shutdown_reboot_actions" translatable="false"> + <item></item> + <item>recovery</item> + <item>bootloader</item> + </string-array> + + <!-- Do not translate. Defines the mapping of notification package names + from the actual triggering package to the user selectable package. + E.g. GTalk notifications come via Google Services Framework + Format: [triggering package]|[user package] --> + <string-array name="notification_light_package_mapping" translatable="false"> + <item>com.google.android.gsf|com.google.android.talk</item> + </string-array> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3b7d73a..ddd3d28 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -514,6 +514,12 @@ <!-- Is the notification LED intrusive? Used to decide if there should be a disable option --> <bool name="config_intrusiveNotificationLed">false</bool> + <!-- Is the battery LED intrusive? Used to decide if there should be a disable option --> + <bool name="config_intrusiveBatteryLed">false</bool> + + <!-- Does the battery LED support multiple colors? Used to decide if the user can change the colors --> + <bool name="config_multiColorBatteryLed">false</bool> + <!-- Default value for LED off time when the battery is low on charge in miliseconds --> <integer name="config_notificationsBatteryLedOff">2875</integer> @@ -1000,4 +1006,19 @@ provisioning on some carriers, working around a bug (7305641) where if the preferred is used we don't try the others. --> <bool name="config_dontPreferApn">false</bool> + + <!-- Values greater or equal to 0 will enable electronbeam screen-on + animation with the specified delay (in milliseconds), -1 will disable the animation --> + <integer name="config_screenOnAnimation">-1</integer> + + <!-- True will enable the electron beam screen-off animation. --> + <bool name="config_screenOffAnimation">true</bool> + + <!-- Timeout in MS for how long you have to long-press the back key to + kill the foreground app. --> + <integer name="config_backKillTimeout">2000</integer> + + <!-- Device has a h/w rotation lock switch --> + <bool name="config_hasRotationLockSwitch">false</bool> + </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a5dae7e..2d6d27a 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1,28 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <!-- This file defines the base public resources exported by the - platform, which must always exist. --> + platform, which must always exist. --> <!-- *************************************************************** - *************************************************************** - IMPORTANT NOTE FOR ANYONE MODIFYING THIS FILE - READ THIS BEFORE YOU MAKE ANY CHANGES + *************************************************************** + IMPORTANT NOTE FOR ANYONE MODIFYING THIS FILE + READ THIS BEFORE YOU MAKE ANY CHANGES - This file defines the binary compatibility for resources. As such, - you must be very careful when making changes here, or you will - completely break backwards compatibility with old applications. + This file defines the binary compatibility for resources. As such, + you must be very careful when making changes here, or you will + completely break backwards compatibility with old applications. - To avoid breaking compatibility, all new resources must be placed - at the end of the list of resources of the same type. Placing a resource - in the middle of type will cause all following resources to be - assigned new resource numbers, breaking compatibility. + To avoid breaking compatibility, all new resources must be placed + at the end of the list of resources of the same type. Placing a resource + in the middle of type will cause all following resources to be + assigned new resource numbers, breaking compatibility. - *************************************************************** - *************************************************************** --> + *************************************************************** + *************************************************************** --> <resources> <!-- =============================================================== - Resources for version 1 of the platform. - =============================================================== --> + Resources for version 1 of the platform. + =============================================================== --> <eat-comment /> <public type="attr" name="theme" id="0x01010000" /> @@ -927,15 +927,15 @@ <public type="anim" name="decelerate_interpolator" id="0x010a0006" /> <!-- =============================================================== - Resources added in version 2 of the platform. - =============================================================== --> + Resources added in version 2 of the platform. + =============================================================== --> <eat-comment /> <public type="attr" name="marqueeRepeatLimit" id="0x0101021d" /> <!-- =============================================================== - Resources added in version 3 of the platform (Cupcake). - =============================================================== --> + Resources added in version 3 of the platform (Cupcake). + =============================================================== --> <eat-comment /> <public type="attr" name="windowNoDisplay" id="0x0101021e" /> @@ -1015,44 +1015,44 @@ <public type="attr" name="imeExtractExitAnimation" id="0x01010269" /> <!-- The part of the UI shown by an - {@link android.inputmethodservice.InputMethodService} that contains the - views for interacting with the user in extraction mode. --> + {@link android.inputmethodservice.InputMethodService} that contains the + views for interacting with the user in extraction mode. --> <public type="id" name="extractArea" id="0x0102001c" /> <!-- The part of the UI shown by an - {@link android.inputmethodservice.InputMethodService} that contains the - views for displaying candidates for what the user has entered. --> + {@link android.inputmethodservice.InputMethodService} that contains the + views for displaying candidates for what the user has entered. --> <public type="id" name="candidatesArea" id="0x0102001d" /> <!-- The part of the UI shown by an - {@link android.inputmethodservice.InputMethodService} that contains the - views for entering text using the screen. --> + {@link android.inputmethodservice.InputMethodService} that contains the + views for entering text using the screen. --> <public type="id" name="inputArea" id="0x0102001e" /> <!-- Context menu ID for the "Select All" menu item to select all text - in a text view. --> + in a text view. --> <public type="id" name="selectAll" id="0x0102001f" /> <!-- Context menu ID for the "Cut" menu item to copy and delete the currently - selected (or all) text in a text view to the clipboard. --> + selected (or all) text in a text view to the clipboard. --> <public type="id" name="cut" id="0x01020020" /> <!-- Context menu ID for the "Copy" menu item to copy the currently - selected (or all) text in a text view to the clipboard. --> + selected (or all) text in a text view to the clipboard. --> <public type="id" name="copy" id="0x01020021" /> <!-- Context menu ID for the "Paste" menu item to copy the current contents - of the clipboard into the text view. --> + of the clipboard into the text view. --> <public type="id" name="paste" id="0x01020022" /> <!-- Context menu ID for the "Copy URL" menu item to copy the currently - selected URL from the text view to the clipboard. --> + selected URL from the text view to the clipboard. --> <public type="id" name="copyUrl" id="0x01020023" /> <!-- Context menu ID for the "Input Method" menu item to being up the - input method picker dialog, allowing the user to switch to another - input method. --> + input method picker dialog, allowing the user to switch to another + input method. --> <public type="id" name="switchInputMethod" id="0x01020024" /> <!-- View ID of the text editor inside of an extracted text layout. --> <public type="id" name="inputExtractEditText" id="0x01020025" /> <!-- View ID of the {@link android.inputmethodservice.KeyboardView} within - an input method's input area. --> + an input method's input area. --> <public type="id" name="keyboardView" id="0x01020026" /> <!-- View ID of a {@link android.view.View} to close a popup keyboard --> <public type="id" name="closeButton" id="0x01020027" /> @@ -1088,8 +1088,8 @@ <public type="integer" name="config_longAnimTime" id="0x010e0002" /> <!-- =============================================================== - Resources added in version 4 of the platform (Donut). - =============================================================== --> + Resources added in version 4 of the platform (Donut). + =============================================================== --> <eat-comment /> <public type="attr" name="tension" id="0x0101026a" /> @@ -1145,8 +1145,8 @@ <public type="anim" name="linear_interpolator" id="0x010a000b" /> <!-- =============================================================== - Resources added in version 5 of the platform (Eclair). - =============================================================== --> + Resources added in version 5 of the platform (Eclair). + =============================================================== --> <eat-comment /> <public type="attr" name="required" id="0x0101028e" /> @@ -1191,16 +1191,16 @@ <public type="style" name="TextAppearance.SearchResult.Subtitle" id="0x01030064" /> <!-- Semi-transparent background that can be used when placing a dark - themed UI on top of some arbitrary background (such as the - wallpaper). This darkens the background sufficiently that the UI - can be seen. --> + themed UI on top of some arbitrary background (such as the + wallpaper). This darkens the background sufficiently that the UI + can be seen. --> <public type="drawable" name="screen_background_dark_transparent" id="0x010800a9" /> <public type="drawable" name="screen_background_light_transparent" id="0x010800aa" /> <public type="drawable" name="stat_notify_sdcard_prepare" id="0x010800ab" /> <!-- =============================================================== - Resources added in version 6 of the platform (Eclair 2.0.1). - =============================================================== --> + Resources added in version 6 of the platform (Eclair 2.0.1). + =============================================================== --> <eat-comment /> <public type="attr" name="quickContactBadgeStyleWindowSmall" id="0x010102ae" /> @@ -1211,8 +1211,8 @@ <public type="attr" name="quickContactBadgeStyleSmallWindowLarge" id="0x010102b3" /> <!-- =============================================================== - Resources added in version 7 of the platform (Eclair MR1). - =============================================================== --> + Resources added in version 7 of the platform (Eclair MR1). + =============================================================== --> <eat-comment /> <public type="attr" name="author" id="0x010102b4" /> @@ -1220,15 +1220,15 @@ <!-- =============================================================== - Resources added in version 8 of the platform (Eclair MR2). - =============================================================== --> + Resources added in version 8 of the platform (Eclair MR2). + =============================================================== --> <eat-comment /> <public type="attr" name="expandableListViewWhiteStyle" id="0x010102b6" /> <!-- =============================================================== - Resources added in version 8 of the platform (Froyo / 2.2) - =============================================================== --> + Resources added in version 8 of the platform (Froyo / 2.2) + =============================================================== --> <eat-comment /> <public type="attr" name="installLocation" id="0x010102b7" /> <public type="attr" name="vmSafeMode" id="0x010102b8" /> @@ -1243,8 +1243,8 @@ <public type="anim" name="cycle_interpolator" id="0x010a000c" /> <!-- =============================================================== - Resources added in version 9 of the platform (Gingerbread / 2.3) - =============================================================== --> + Resources added in version 9 of the platform (Gingerbread / 2.3) + =============================================================== --> <eat-comment /> <public type="attr" name="logo" id="0x010102be" /> <public type="attr" name="xlargeScreens" id="0x010102bf" /> @@ -1275,8 +1275,8 @@ <public type="style" name="TextAppearance.StatusBar.EventContent.Title" id="0x01030068" /> <!-- =============================================================== - Resources added in version 11 of the platform (Honeycomb / 3.0). - =============================================================== --> + Resources added in version 11 of the platform (Honeycomb / 3.0). + =============================================================== --> <eat-comment /> <public type="attr" name="allContactsName" id="0x010102cc" /> @@ -1454,22 +1454,22 @@ <!-- An interpolator where the change starts backward then flings forward. --> <public type="interpolator" name="anticipate" id="0x010c0007" /> <!-- An interpolator where the change flings forward and overshoots the last - value then comes back. --> + value then comes back. --> <public type="interpolator" name="overshoot" id="0x010c0008" /> <!-- An interpolator where the change starts backward then flings forward and - overshoots the target value and finally goes back to the final value. --> + overshoots the target value and finally goes back to the final value. --> <public type="interpolator" name="anticipate_overshoot" id="0x010c0009" /> <!-- An interpolator where the change bounces at the end. --> <public type="interpolator" name="bounce" id="0x010c000a" /> <!-- An interpolator where the rate of change is constant. --> <public type="interpolator" name="linear" id="0x010c000b" /> <!-- Repeats the animation for one cycle. The rate of change follows a - sinusoidal pattern. --> + sinusoidal pattern. --> <public type="interpolator" name="cycle" id="0x010c000c" /> <public type="id" name="home" id="0x0102002c" /> <!-- Context menu ID for the "Select text..." menu item to switch to text - selection context mode in text views. --> + selection context mode in text views. --> <public type="id" name="selectTextMode" id="0x0102002d" /> <public type="dimen" name="dialog_min_width_major" id="0x01050003" /> @@ -1478,23 +1478,23 @@ <public type="dimen" name="notification_large_icon_height" id="0x01050006" /> <!-- Standard content view for a {@link android.app.ListFragment}. - If you are implementing a subclass of ListFragment with your - own customized content, you can include this layout in that - content to still retain all of the standard functionality of - the base class. --> + If you are implementing a subclass of ListFragment with your + own customized content, you can include this layout in that + content to still retain all of the standard functionality of + the base class. --> <public type="layout" name="list_content" id="0x01090014" /> <!-- A simple ListView item layout which can contain text and support (single or multiple) item selection. --> <public type="layout" name="simple_selectable_list_item" id="0x01090015" /> <!-- A version of {@link #simple_list_item_1} that is able to change its - background state to indicate when it is activated (that is checked by - a ListView). --> + background state to indicate when it is activated (that is checked by + a ListView). --> <public type="layout" name="simple_list_item_activated_1" id="0x01090016" /> <!-- A version of {@link #simple_list_item_2} that is able to change its - background state to indicate when it is activated (that is checked by - a ListView). --> + background state to indicate when it is activated (that is checked by + a ListView). --> <public type="layout" name="simple_list_item_activated_2" id="0x01090017" /> <public type="drawable" name="dialog_holo_dark_frame" id="0x010800b2" /> @@ -1642,15 +1642,15 @@ <public type="mipmap" name="sym_def_app_icon" id="0x010d0000" /> <!-- =============================================================== - Resources added in version 12 of the platform (Honeycomb MR 1 / 3.1) - =============================================================== --> + Resources added in version 12 of the platform (Honeycomb MR 1 / 3.1) + =============================================================== --> <eat-comment /> <public type="attr" name="textCursorDrawable" id="0x01010362" /> <public type="attr" name="resizeMode" id="0x01010363" /> <!-- =============================================================== - Resources added in version 13 of the platform (Honeycomb MR 2 / 3.2) - =============================================================== --> + Resources added in version 13 of the platform (Honeycomb MR 2 / 3.2) + =============================================================== --> <eat-comment /> <public type="attr" name="requiresSmallestWidthDp" id="0x01010364" /> <public type="attr" name="compatibleWidthLimitDp" id="0x01010365" /> @@ -1699,8 +1699,8 @@ <public type="style" name="TextAppearance.Holo.DialogWindowTitle" id="0x01030117" /> <!-- =============================================================== - Resources added in version 14 of the platform (Ice Cream Sandwich / 4.0) - =============================================================== --> + Resources added in version 14 of the platform (Ice Cream Sandwich / 4.0) + =============================================================== --> <eat-comment /> <public type="attr" name="state_hovered" id="0x01010367" /> <public type="attr" name="state_drag_can_accept" id="0x01010368" /> @@ -1973,8 +1973,8 @@ <public type="color" name="holo_blue_bright" id="0x0106001b" /> <!-- =============================================================== - Resources added in version 16 of the platform (Jelly Bean) - =============================================================== --> + Resources added in version 16 of the platform (Jelly Bean) + =============================================================== --> <eat-comment /> <public type="attr" name="parentActivityName" id="0x010103a7" /> <public type="attr" name="isolatedProcess" id="0x010103a9" /> @@ -2028,4 +2028,45 @@ <public type="style" name="Widget.DeviceDefault.CheckedTextView" id="0x010301db" /> <public type="style" name="Widget.DeviceDefault.Light.CheckedTextView" id="0x010301dc" /> + <java-symbol type="string" name="weather_E" /> + <java-symbol type="string" name="weather_N" /> + <java-symbol type="string" name="weather_NE" /> + <java-symbol type="string" name="weather_NW" /> + <java-symbol type="string" name="weather_S" /> + <java-symbol type="string" name="weather_SE" /> + <java-symbol type="string" name="weather_SW" /> + <java-symbol type="string" name="weather_W" /> + + <java-symbol type="drawable" name="ic_lock_profile" /> + <java-symbol type="drawable" name="ic_lock_reboot" /> + <java-symbol type="drawable" name="ic_lock_screenshot" /> + <java-symbol type="drawable" name="weather_na" /> + <java-symbol type="id" name="calendar_event_details" /> + <java-symbol type="id" name="calendar_event_title" /> + <java-symbol type="id" name="calendar_panel" /> + <java-symbol type="id" name="update_time" /> + <java-symbol type="id" name="weather_city" /> + <java-symbol type="id" name="weather_condition" /> + <java-symbol type="id" name="weather_image" /> + <java-symbol type="id" name="weather_low_high" /> + <java-symbol type="id" name="weather_panel" /> + <java-symbol type="id" name="weather_temp" /> + <java-symbol type="id" name="weather_temps_panel" /> + <java-symbol type="string" name="global_action_choose_profile" /> + <java-symbol type="string" name="global_action_reboot" /> + <java-symbol type="string" name="global_actions" /> + <java-symbol type="string" name="global_action_screenshot" /> + <java-symbol type="string" name="weather_no_data" /> + <java-symbol type="string" name="weather_refreshing" /> + <java-symbol type="string" name="weather_tap_to_refresh" /> + + <java-symbol type="array" name="notification_light_package_mapping" /> + <java-symbol type="array" name="shutdown_reboot_actions" /> + <java-symbol type="array" name="shutdown_reboot_options" /> + <java-symbol type="bool" name="config_multiColorBatteryLed" /> + <java-symbol type="string" name="reboot_progress" /> + <java-symbol type="string" name="reboot_system" /> + <java-symbol type="string" name="wildcardProfile" /> + <java-symbol type="xml" name="profile_default" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 9932d1e..c6285c8 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -68,6 +68,23 @@ could not be performed because FDN is enabled. This will be displayed in a toast. --> <string name="mmiFdnError">Operation is restricted to fixed dialing numbers only.</string> + <!-- Names of default profiles. --> + <string name="profileNameDefault">Default</string> + <string name="profileNameWork">Work</string> + <string name="profileNameHome">Home</string> + <string name="profileNameSilent">Silent</string> + <string name="profileNameNight">Night</string> + + <!-- Names of application groups. --> + <string name="profileGroupPhone">Phone</string> + <string name="profileGroupCalendar">Calendar</string> + <string name="profileGroupGmail">Gmail</string> + <string name="profileGroupEmail">Email</string> + <string name="profileGroupSMS">SMS</string> + + <!-- Name of wildcard profile. --> + <string name="wildcardProfile">Other</string> + <!-- Displayed when a phone feature such as call barring was activated. --> <string name="serviceEnabled">Service was enabled.</string> <!-- Displayed in front of the list of a set of service classes @@ -292,6 +309,33 @@ <string name="screen_lock">Screen lock</string> <!-- Button to turn off the phone, within the Phone Options dialog --> <string name="power_off">Power off</string> + + <!-- Button to reboot the phone, within the Phone Options dialog --> + <string name="reboot_system" product="tablet">Reboot tablet</string> + <string name="reboot_system" product="default">Reboot phone</string> + <!-- Reboot Progress Dialog. This is shown if the user chooses to reboot the phone. --> + + <!-- label for item that screenshots in phone options dialog --> + <string name="global_action_screenshot">Screenshot</string> + + <!-- Button to reboot the phone, within the Reboot Options dialog --> + <string name="reboot_reboot">Reboot</string> + <!-- Button to reboot the phone into recovery, within the Reboot Options dialog --> + <string name="reboot_recovery">Recovery</string> + <!-- Button to reboot the phone into bootloader, within the Reboot Options dialog --> + <string name="reboot_bootloader">Bootloader</string> + <!-- Button to reboot the phone into bootmenu, within the Reboot Options dialog --> + <string name="reboot_bootmenu">Bootmenu</string> + <!-- Button to reboot the phone into fastboot, within the Reboot Options dialog --> + <string name="reboot_fastboot">Fastboot</string> + <!-- Button to reboot the phone into download, within the Reboot Options dialog --> + <string name="reboot_download">Download</string> + + <!-- Reboot Progress Dialog. This is shown if the user chooses to reboot the phone. --> + <string name="reboot_progress">Rebooting\u2026</string> + <!-- Reboot Confirmation Dialog. When the user chooses to reboot the phone, there will be a confirmation dialog. This is the message. --> + <string name="reboot_confirm">Your phone will reboot.</string> + <!-- Spoken description for ringer silent option. [CHAR LIMIT=NONE] --> <string name="silent_mode_silent">Ringer off</string> <!-- Spoken description for ringer vibrate option. [CHAR LIMIT=NONE] --> @@ -356,7 +400,6 @@ current device state, to send as an e-mail message. It will take a little time from starting the bug report until it is ready to be sent; please be patient.</string> - <!-- label for item that enables silent mode in phone options dialog --> <string name="global_action_toggle_silent_mode">Silent mode</string> @@ -3990,4 +4033,77 @@ <!-- Default name of the owner user [CHAR LIMIT=20] --> <string name="owner_name" msgid="3879126011135546571">Owner</string> + <!-- label for item that reboots the phone in phone options dialog --> + <string name="global_action_reboot">Reboot</string> + + <!-- label for item that opens the profile choosing dialog --> + <string name="global_action_choose_profile">Profile</string> + + <!-- Lock screen Weather - Weather codes --> + <string name="weather_0">Tornado</string> + <string name="weather_1">Tropical Storm</string> + <string name="weather_2">Hurricane</string> + <string name="weather_3">Severe Thunderstorms</string> + <string name="weather_4">Thunderstorms</string> + <string name="weather_5">Mixed Rain and Snow</string> + <string name="weather_6">Mixed Rain and Sleet</string> + <string name="weather_7">Mixed Snow and Sleet</string> + <string name="weather_8">Freezing Drizzle</string> + <string name="weather_9">Drizzle</string> + <string name="weather_10">Freezing Rain</string> + <string name="weather_11">Showers</string> + <string name="weather_12">Showers</string> + <string name="weather_13">Snow Flurries</string> + <string name="weather_14">Light Snow Showers</string> + <string name="weather_15">Blowing Snow</string> + <string name="weather_16">Snow</string> + <string name="weather_17">Hail</string> + <string name="weather_18">Sleet</string> + <string name="weather_19">Dust</string> + <string name="weather_20">Foggy</string> + <string name="weather_21">Haze</string> + <string name="weather_22">Smoky</string> + <string name="weather_23">Blustery</string> + <string name="weather_24">Windy</string> + <string name="weather_25">Cold</string> + <string name="weather_26">Cloudy</string> + <string name="weather_27">Mostly Cloudy</string> <!-- Night --> + <string name="weather_28">Mostly Cloudy</string> <!-- Day --> + <string name="weather_29">Partly Cloudy</string> <!-- Night --> + <string name="weather_30">Partly Cloudy</string> <!-- Day --> + <string name="weather_31">Clear</string> <!-- Night --> + <string name="weather_32">Sunny</string> + <string name="weather_33">Fair</string> <!-- Night --> + <string name="weather_34">Fair</string> <!-- Day --> + <string name="weather_35">Mixed Rain and Hail</string> + <string name="weather_36">Hot</string> + <string name="weather_37">Isolated Thunderstorms</string> + <string name="weather_38">Scattered Thunderstorms</string> + <string name="weather_39">Scattered Thunderstorms</string> + <string name="weather_40">Scattered Showers</string> + <string name="weather_41">Heavy Snow</string> + <string name="weather_42">Scattered Snow Showers</string> + <string name="weather_43">Heavy Snow</string> + <string name="weather_44">Partly Cloudy</string> + <string name="weather_45">Thundershowers</string> + <string name="weather_46">Snow Showers</string> + <string name="weather_47">Isolated Thundershowers</string> + <!-- Forecast unavailable --> + <string name="weather_3200"></string> + + <!-- Lock screen Weather - Wind directions --> + <string name="weather_N">N</string> + <string name="weather_NE">NE</string> + <string name="weather_E">E</string> + <string name="weather_SE">SE</string> + <string name="weather_S">S</string> + <string name="weather_SW">SW</string> + <string name="weather_W">W</string> + <string name="weather_NW">NW</string> + + <!-- Lock screen Weather - error messages --> + <string name="weather_no_data">No data</string> + <string name="weather_tap_to_refresh">Tap to refresh</string> + <string name="weather_refreshing">Refreshing</string> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 68a0289..aae7c1d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1745,6 +1745,7 @@ <!-- From Settings --> <java-symbol type="array" name="config_mobile_hotspot_provision_app" /> <java-symbol type="bool" name="config_intrusiveNotificationLed" /> + <java-symbol type="bool" name="config_intrusiveBatteryLed" /> <java-symbol type="dimen" name="preference_fragment_padding_bottom" /> <java-symbol type="dimen" name="preference_fragment_padding_side" /> <java-symbol type="drawable" name="expander_ic_maximized" /> diff --git a/core/res/res/xml/profile_default.xml b/core/res/res/xml/profile_default.xml new file mode 100644 index 0000000..824a73c --- /dev/null +++ b/core/res/res/xml/profile_default.xml @@ -0,0 +1,267 @@ +<profiles> + <profile nameres="profileNameDefault"> + <profileGroup uuid="d393035d-ea71-4f2a-bdbd-b65f6bf298f1"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="66bc93e4-775a-4ac2-9da1-752178fcdcaf"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="d2ebb02a-5205-47c5-8e39-f55823d4082a"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="db3318cd-1964-4732-b913-1f83d73a3dea"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="da4f1e2d-0e50-4789-acd4-5ca2b53a981b"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="a126d48a-aaef-47c4-baed-7f0e44aeffe5" default="true"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + </profile> + <profile nameres="profileNameWork"> + <profileGroup uuid="d393035d-ea71-4f2a-bdbd-b65f6bf298f1"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>OVERRIDE</soundMode> + <ringerMode>OVERRIDE</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>OVERRIDE</lightsMode> + </profileGroup> + <profileGroup uuid="66bc93e4-775a-4ac2-9da1-752178fcdcaf"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>OVERRIDE</lightsMode> + </profileGroup> + <profileGroup uuid="d2ebb02a-5205-47c5-8e39-f55823d4082a"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="db3318cd-1964-4732-b913-1f83d73a3dea"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>SUPPRESS</vibrateMode> + <lightsMode>SUPPRESS</lightsMode> + </profileGroup> + <profileGroup uuid="da4f1e2d-0e50-4789-acd4-5ca2b53a981b"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>OVERRIDE</soundMode> + <ringerMode>OVERRIDE</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="a126d48a-aaef-47c4-baed-7f0e44aeffe5" default="true"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + </profile> + <profile nameres="profileNameHome"> + <profileGroup uuid="d393035d-ea71-4f2a-bdbd-b65f6bf298f1"> + <sound>content://media/external/audio/media/11</sound> + <ringer>content://media/external/audio/media/5</ringer> + <soundMode>OVERRIDE</soundMode> + <ringerMode>OVERRIDE</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="66bc93e4-775a-4ac2-9da1-752178fcdcaf"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>OVERRIDE</soundMode> + <ringerMode>OVERRIDE</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>OVERRIDE</lightsMode> + </profileGroup> + <profileGroup uuid="d2ebb02a-5205-47c5-8e39-f55823d4082a"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>OVERRIDE</soundMode> + <ringerMode>OVERRIDE</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>OVERRIDE</lightsMode> + </profileGroup> + <profileGroup uuid="db3318cd-1964-4732-b913-1f83d73a3dea"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="da4f1e2d-0e50-4789-acd4-5ca2b53a981b"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>OVERRIDE</soundMode> + <ringerMode>OVERRIDE</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="a126d48a-aaef-47c4-baed-7f0e44aeffe5" default="true"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>DEFAULT</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + </profile> + <profile nameres="profileNameSilent"> + <profileGroup uuid="d393035d-ea71-4f2a-bdbd-b65f6bf298f1"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>SUPPRESS</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="66bc93e4-775a-4ac2-9da1-752178fcdcaf"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="d2ebb02a-5205-47c5-8e39-f55823d4082a"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="db3318cd-1964-4732-b913-1f83d73a3dea"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="da4f1e2d-0e50-4789-acd4-5ca2b53a981b"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="a126d48a-aaef-47c4-baed-7f0e44aeffe5" default="true"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + </profile> + <profile nameres="profileNameNight"> + <profileGroup uuid="d393035d-ea71-4f2a-bdbd-b65f6bf298f1"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>OVERRIDE</soundMode> + <ringerMode>OVERRIDE</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="66bc93e4-775a-4ac2-9da1-752178fcdcaf"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>SUPPRESS</lightsMode> + </profileGroup> + <profileGroup uuid="d2ebb02a-5205-47c5-8e39-f55823d4082a"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>SUPPRESS</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="db3318cd-1964-4732-b913-1f83d73a3dea"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>SUPPRESS</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + <profileGroup uuid="da4f1e2d-0e50-4789-acd4-5ca2b53a981b"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>SUPPRESS</ringerMode> + <vibrateMode>OVERRIDE</vibrateMode> + <lightsMode>SUPPRESS</lightsMode> + </profileGroup> + <profileGroup uuid="a126d48a-aaef-47c4-baed-7f0e44aeffe5" default="true"> + <sound>content://settings/system/notification_sound</sound> + <ringer>content://settings/system/ringtone</ringer> + <soundMode>SUPPRESS</soundMode> + <ringerMode>DEFAULT</ringerMode> + <vibrateMode>DEFAULT</vibrateMode> + <lightsMode>DEFAULT</lightsMode> + </profileGroup> + </profile> + <notificationGroup nameres="profileGroupPhone" uuid="d393035d-ea71-4f2a-bdbd-b65f6bf298f1"> + <package>com.android.phone</package> + </notificationGroup> + <notificationGroup nameres="profileGroupCalendar" uuid="66bc93e4-775a-4ac2-9da1-752178fcdcaf"> + <package>com.android.calendar</package> + </notificationGroup> + <notificationGroup nameres="profileGroupGmail" uuid="d2ebb02a-5205-47c5-8e39-f55823d4082a"> + <package>com.google.android.gm</package> + </notificationGroup> + <notificationGroup nameres="profileGroupEmail" uuid="db3318cd-1964-4732-b913-1f83d73a3dea"> + <package>com.android.email</package> + </notificationGroup> + <notificationGroup nameres="profileGroupSMS" uuid="da4f1e2d-0e50-4789-acd4-5ca2b53a981b"> + <package>com.android.mms</package> + </notificationGroup> +</profiles> diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 035b282..f55a0bb 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -19,6 +19,8 @@ package android.media; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; +import android.app.ProfileGroup; +import android.app.ProfileManager; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; @@ -51,6 +53,7 @@ public class AudioManager { private final boolean mUseMasterVolume; private final boolean mUseVolumeKeySounds; private static String TAG = "AudioManager"; + private final ProfileManager mProfileManager; /** * Broadcast intent, a hint for applications that audio is about to become @@ -422,6 +425,7 @@ public class AudioManager { com.android.internal.R.bool.config_useMasterVolume); mUseVolumeKeySounds = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useVolumeKeySounds); + mProfileManager = (ProfileManager) context.getSystemService(Context.PROFILE_SERVICE); } private static IAudioService getService() @@ -1000,6 +1004,26 @@ public class AudioManager { * current ringer mode that can be queried via {@link #getRingerMode()}. */ public boolean shouldVibrate(int vibrateType) { + String packageName = mContext.getPackageName(); + // Don't apply profiles for "android" context, as these could + // come from the NotificationManager, and originate from a real package. + if (!packageName.equals("android")) { + ProfileGroup profileGroup = mProfileManager.getActiveProfileGroup(packageName); + if (profileGroup != null) { + Log.v(TAG, "shouldVibrate, group: " + profileGroup.getUuid() + + " mode: " + profileGroup.getVibrateMode()); + switch (profileGroup.getVibrateMode()) { + case OVERRIDE : + return true; + case SUPPRESS : + return false; + case DEFAULT : + // Drop through + } + } + } else { + Log.v(TAG, "Not applying override for 'android' package"); + } IAudioService service = getService(); try { return service.shouldVibrate(vibrateType); diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 5e18bfa..b5a672a 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -22,6 +22,8 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Activity; import android.content.ContentUris; +import android.app.ProfileGroup; +import android.app.ProfileManager; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.database.Cursor; @@ -55,29 +57,29 @@ public class RingtoneManager { // Make sure these are in sync with attrs.xml: // <attr name="ringtoneType"> - + /** * Type that refers to sounds that are used for the phone ringer. */ public static final int TYPE_RINGTONE = 1; - + /** * Type that refers to sounds that are used for notifications. */ public static final int TYPE_NOTIFICATION = 2; - + /** * Type that refers to sounds that are used for the alarm. */ public static final int TYPE_ALARM = 4; - + /** * All types of sounds. */ public static final int TYPE_ALL = TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM; // </attr> - + /** * Activity Action: Shows a ringtone picker. * <p> @@ -131,7 +133,7 @@ public class RingtoneManager { */ public static final String EXTRA_RINGTONE_EXISTING_URI = "android.intent.extra.ringtone.EXISTING_URI"; - + /** * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the * ringtone to play when the user attempts to preview the "Default" @@ -144,7 +146,7 @@ public class RingtoneManager { */ public static final String EXTRA_RINGTONE_DEFAULT_URI = "android.intent.extra.ringtone.DEFAULT_URI"; - + /** * Given to the ringtone picker as an int. Specifies which ringtone type(s) should be * shown in the picker. One or more of {@link #TYPE_RINGTONE}, @@ -194,7 +196,7 @@ public class RingtoneManager { "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"", MediaStore.Audio.Media.TITLE_KEY }; - + /** * The column index (in the cursor returned by {@link #getCursor()} for the * row ID. @@ -215,11 +217,11 @@ public class RingtoneManager { private Activity mActivity; private Context mContext; - + private Cursor mCursor; private int mType = TYPE_RINGTONE; - + /** * If a column (item from this list) exists in the Cursor, its value must * be true (value of 1) for the row to be returned. @@ -230,7 +232,7 @@ public class RingtoneManager { private Ringtone mPreviousRingtone; private boolean mIncludeDrm; - + /** * Constructs a RingtoneManager. This constructor is recommended as its * constructed instance manages cursor(s). @@ -322,7 +324,7 @@ public class RingtoneManager { mPreviousRingtone.stop(); } } - + /** * Returns whether DRM ringtones will be included. * @@ -365,7 +367,7 @@ public class RingtoneManager { final Cursor internalCursor = getInternalRingtones(); final Cursor drmCursor = mIncludeDrm ? getDrmRingtones() : null; final Cursor mediaCursor = getMediaRingtones(); - + return mCursor = new SortCursor(new Cursor[] { internalCursor, drmCursor, mediaCursor }, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); } @@ -401,12 +403,12 @@ public class RingtoneManager { return getUriFromCursor(mCursor); } - + private static Uri getUriFromCursor(Cursor cursor) { return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor .getLong(ID_COLUMN_INDEX)); } - + /** * Gets the position of a {@link Uri} within this {@link RingtoneManager}. * @@ -527,7 +529,7 @@ public class RingtoneManager { columns.add(MediaStore.Audio.AudioColumns.IS_ALARM); } } - + /** * Constructs a where clause that consists of at least one column being 1 * (true). This is used to find all matching sounds for the given sound @@ -539,7 +541,7 @@ public class RingtoneManager { private static String constructBooleanTrueWhereClause(List<String> columns, boolean includeDrm) { if (columns == null) return null; - + StringBuilder sb = new StringBuilder(); sb.append("("); @@ -565,7 +567,7 @@ public class RingtoneManager { return sb.toString(); } - + private Cursor query(Uri uri, String[] projection, String selection, @@ -578,7 +580,7 @@ public class RingtoneManager { sortOrder); } } - + /** * Returns a {@link Ringtone} for a given sound URI. * <p> @@ -606,11 +608,26 @@ public class RingtoneManager { * @see #getRingtone(Context, Uri) */ private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) { + ProfileManager pm = (ProfileManager)context.getSystemService(context.PROFILE_SERVICE); + ProfileGroup profileGroup = pm.getActiveProfileGroup(context.getPackageName()); + try { - final Ringtone r = new Ringtone(context, true); + Ringtone r = new Ringtone(context, true); if (streamType >= 0) { r.setStreamType(streamType); } + + if (profileGroup != null) { + switch (profileGroup.getRingerMode()) { + case OVERRIDE : + r.setUri(profileGroup.getRingerOverride()); + return r; + case SUPPRESS : + r = null; + return r; + } + } + r.setUri(ringtoneUri); return r; } catch (Exception ex) { @@ -619,7 +636,7 @@ public class RingtoneManager { return null; } - + /** * Gets the current default sound's {@link Uri}. This will give the actual * sound {@link Uri}, instead of using this, most clients can use @@ -638,7 +655,7 @@ public class RingtoneManager { final String uriString = Settings.System.getString(context.getContentResolver(), setting); return uriString != null ? Uri.parse(uriString) : null; } - + /** * Sets the {@link Uri} of the default sound for a given sound type. * @@ -655,7 +672,7 @@ public class RingtoneManager { Settings.System.putString(context.getContentResolver(), setting, ringtoneUri != null ? ringtoneUri.toString() : null); } - + private static String getSettingForType(int type) { if ((type & TYPE_RINGTONE) != 0) { return Settings.System.RINGTONE; @@ -667,7 +684,7 @@ public class RingtoneManager { return null; } } - + /** * Returns whether the given {@link Uri} is one of the default ringtones. * @@ -677,7 +694,7 @@ public class RingtoneManager { public static boolean isDefault(Uri ringtoneUri) { return getDefaultType(ringtoneUri) != -1; } - + /** * Returns the type of a default {@link Uri}. * @@ -700,7 +717,7 @@ public class RingtoneManager { return -1; } } - + /** * Returns the {@link Uri} for the default ringtone of a particular type. * Rather than returning the actual ringtone's sound {@link Uri}, this will diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index d1f8ef1..6c439cc 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010-2012 CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +26,8 @@ import com.android.internal.R; import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.app.Dialog; +import android.app.Profile; +import android.app.ProfileManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -71,6 +74,17 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.List; +import java.util.UUID; + +/** + * Needed for takeScreenshot + */ +import android.content.ServiceConnection; +import android.content.ComponentName; +import android.os.IBinder; +import android.os.Messenger; +import android.os.RemoteException; + /** * Helper to show the global actions dialog. Each item is an {@link Action} that @@ -85,6 +99,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac private final Context mContext; private final WindowManagerFuncs mWindowManagerFuncs; + private final AudioManager mAudioManager; private final IDreamManager mDreamManager; @@ -103,6 +118,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac private boolean mHasTelephony; private boolean mHasVibrator; + private Profile mChosenProfile; + + /** * @param context everything needs a context :( */ @@ -242,6 +260,22 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac mWindowManagerFuncs.shutdown(true); } + public boolean showDuringKeyguard() { + return true; + } + + public boolean showBeforeProvisioning() { + return true; + } + }); + + // next: reboot + mItems.add( + new SinglePressAction(R.drawable.ic_lock_reboot, R.string.global_action_reboot) { + public void onPress() { + mWindowManagerFuncs.reboot(); + } + public boolean onLongPress() { mWindowManagerFuncs.rebootSafeMode(true); return true; @@ -256,6 +290,43 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } }); + + // next: profile + mItems.add( + new ProfileChooseAction() { + public void onPress() { + createProfileDialog(); + } + + public boolean onLongPress() { + return true; + } + + public boolean showDuringKeyguard() { + return false; + } + + public boolean showBeforeProvisioning() { + return false; + } + }); + + // next: screenshot + mItems.add( + new SinglePressAction(R.drawable.ic_lock_screenshot, R.string.global_action_screenshot) { + public void onPress() { + takeScreenshot(); + } + + public boolean showDuringKeyguard() { + return true; + } + + public boolean showBeforeProvisioning() { + return true; + } + }); + // next: airplane mode mItems.add(mAirplaneModeOn); @@ -385,11 +456,141 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } } + private void createProfileDialog(){ + final ProfileManager profileManager = (ProfileManager)mContext.getSystemService(Context.PROFILE_SERVICE); + + final Profile[] profiles = profileManager.getProfiles(); + UUID activeProfile = profileManager.getActiveProfile().getUuid(); + final CharSequence[] names = new CharSequence[profiles.length]; + + int i=0; + int checkedItem = 0; + + for(Profile profile : profiles) { + if(profile.getUuid().equals(activeProfile)) { + checkedItem = i; + mChosenProfile = profile; + } + names[i++] = profile.getName(); + } + + final AlertDialog.Builder ab = new AlertDialog.Builder(mContext); + + AlertDialog dialog = ab + .setTitle(R.string.global_action_choose_profile) + .setSingleChoiceItems(names, checkedItem, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (which < 0) + return; + mChosenProfile = profiles[which]; + } + }) + .setPositiveButton(com.android.internal.R.string.yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + profileManager.setActiveProfile(mChosenProfile.getUuid()); + } + }) + .setNegativeButton(com.android.internal.R.string.no, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }).create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + dialog.show(); + } + + /** + * functions needed for taking screenhots. + * This leverages the built in ICS screenshot functionality + */ + final Object mScreenshotLock = new Object(); + ServiceConnection mScreenshotConnection = null; + + final Runnable mScreenshotTimeout = new Runnable() { + @Override public void run() { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + } + } + } + }; + + private void takeScreenshot() { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + return; + } + ComponentName cn = new ComponentName("com.android.systemui", + "com.android.systemui.screenshot.TakeScreenshotService"); + Intent intent = new Intent(); + intent.setComponent(cn); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != this) { + return; + } + Messenger messenger = new Messenger(service); + Message msg = Message.obtain(null, 1); + final ServiceConnection myConn = this; + Handler h = new Handler(mHandler.getLooper()) { + @Override + public void handleMessage(Message msg) { + synchronized (mScreenshotLock) { + if (mScreenshotConnection == myConn) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + mHandler.removeCallbacks(mScreenshotTimeout); + } + } + } + }; + msg.replyTo = new Messenger(h); + msg.arg1 = msg.arg2 = 0; + + /* remove for the time being + if (mStatusBar != null && mStatusBar.isVisibleLw()) + msg.arg1 = 1; + if (mNavigationBar != null && mNavigationBar.isVisibleLw()) + msg.arg2 = 1; + */ + + /* wait for the dialog box to close */ + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + } + + /* take the screenshot */ + try { + messenger.send(msg); + } catch (RemoteException e) { + } + } + } + @Override + public void onServiceDisconnected(ComponentName name) {} + }; + if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) { + mScreenshotConnection = conn; + mHandler.postDelayed(mScreenshotTimeout, 10000); + } + } + } + private void prepareDialog() { refreshSilentMode(); mAirplaneModeOn.updateState(mAirplaneState); mAdapter.notifyDataSetChanged(); mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + + mDialog.setTitle(R.string.global_actions); + if (SHOW_SILENT_TOGGLE) { IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); mContext.registerReceiver(mRingerModeReceiver, filter); @@ -587,6 +788,41 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } /** + * A single press action maintains no state, just responds to a press + * and takes an action. + */ + private abstract class ProfileChooseAction implements Action { + private ProfileManager mProfileManager; + + protected ProfileChooseAction() { + mProfileManager = (ProfileManager)mContext.getSystemService(Context.PROFILE_SERVICE); + } + + public boolean isEnabled() { + return true; + } + + abstract public void onPress(); + + public View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { + View v = (convertView != null) ? + convertView : + inflater.inflate(R.layout.global_actions_item, parent, false); + + ImageView icon = (ImageView) v.findViewById(R.id.icon); + TextView messageView = (TextView) v.findViewById(R.id.message); + TextView statusView = (TextView) v.findViewById(R.id.status); + statusView.setVisibility(View.VISIBLE); + statusView.setText(mProfileManager.getActiveProfile().getName()); + + icon.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_lock_profile)); + messageView.setText(R.string.global_action_choose_profile); + + return v; + } + } + + /** * A toggle action knows whether it is on or off, and displays an icon * and status message accordingly. */ diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index d9c07f8..a48e411 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -324,6 +324,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mFocusedWindow; IApplicationToken mFocusedApp; + // Behavior of volume wake + boolean mVolumeWakeScreen; + + // Behavior of volbtn music controls + boolean mVolBtnMusicControls; + boolean mIsLongPress; + private static final class PointerLocationInputEventReceiver extends InputEventReceiver { private final PointerLocationView mView; @@ -526,6 +533,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.VOLUME_WAKE_SCREEN), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.VOLBTN_MUSIC_CONTROLS), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.ACCELEROMETER_ROTATION), false, this, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( @@ -675,6 +688,53 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + /** + * When a volumeup-key longpress expires, skip songs based on key press + */ + Runnable mVolumeUpLongPress = new Runnable() { + public void run() { + // set the long press flag to true + mIsLongPress = true; + + // Shamelessly copied from Kmobs LockScreen controls, works for Pandora, etc... + sendMediaButtonEvent(KeyEvent.KEYCODE_MEDIA_NEXT); + }; + }; + + /** + * When a volumedown-key longpress expires, skip songs based on key press + */ + Runnable mVolumeDownLongPress = new Runnable() { + public void run() { + // set the long press flag to true + mIsLongPress = true; + + // Shamelessly copied from Kmobs LockScreen controls, works for Pandora, etc... + sendMediaButtonEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS); + }; + }; + + private void sendMediaButtonEvent(int code) { + long eventtime = SystemClock.uptimeMillis(); + Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); + KeyEvent keyEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN, code, 0); + keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + mContext.sendOrderedBroadcast(keyIntent, null); + keyEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP); + keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + mContext.sendOrderedBroadcast(keyIntent, null); + } + + void handleVolumeLongPress(int keycode) { + mHandler.postDelayed(keycode == KeyEvent.KEYCODE_VOLUME_UP ? mVolumeUpLongPress : + mVolumeDownLongPress, ViewConfiguration.getLongPressTimeout()); + } + + void handleVolumeLongPressAbort() { + mHandler.removeCallbacks(mVolumeUpLongPress); + mHandler.removeCallbacks(mVolumeDownLongPress); + } + private void interceptScreenshotChord() { if (mScreenshotChordEnabled && mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) { @@ -1070,11 +1130,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT, UserHandle.USER_CURRENT); + mVolumeWakeScreen = (Settings.System.getIntForUser(resolver, + Settings.System.VOLUME_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1); + mVolBtnMusicControls = (Settings.System.getIntForUser(resolver, + Settings.System.VOLBTN_MUSIC_CONTROLS, 1, UserHandle.USER_CURRENT) == 1); // Configure rotation lock. int userRotation = Settings.System.getIntForUser(resolver, Settings.System.USER_ROTATION, Surface.ROTATION_0, UserHandle.USER_CURRENT); + if (mUserRotation != userRotation) { mUserRotation = userRotation; updateRotation = true; @@ -3340,7 +3405,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final boolean canceled = event.isCanceled(); - final int keyCode = event.getKeyCode(); + int keyCode = event.getKeyCode(); final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; @@ -3356,8 +3421,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (keyCode == KeyEvent.KEYCODE_POWER) { policyFlags |= WindowManagerPolicy.FLAG_WAKE; } - final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE - | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; + final boolean isWakeKey = (policyFlags + & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; if (DEBUG_INPUT) { Log.d(TAG, "interceptKeyTq keycode=" + keyCode @@ -3391,7 +3456,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (keyguardActive) { // If the keyguard is showing, let it wake the device when ready. mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode); - } else { + } else if ((keyCode != KeyEvent.KEYCODE_VOLUME_UP) && (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN)) { // Otherwise, wake the device ourselves. result |= ACTION_WAKE_UP; } @@ -3400,6 +3465,35 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Handle special keys. switch (keyCode) { + case KeyEvent.KEYCODE_ENDCALL: { + result &= ~ACTION_PASS_TO_USER; + if (down) { + ITelephony telephonyService = getTelephonyService(); + boolean hungUp = false; + if (telephonyService != null) { + try { + hungUp = telephonyService.endCall(); + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); + } + } + interceptPowerKeyDown(!isScreenOn || hungUp); + } else { + if (interceptPowerKeyUp(canceled)) { + if ((mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) { + if (goHome()) { + break; + } + } + if ((mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { + result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; + } + } + } + break; + } case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE: { @@ -3464,45 +3558,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { Log.w(TAG, "ITelephony threw RemoteException", ex); } } - - if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) { - // If music is playing but we decided not to pass the key to the - // application, handle the volume change here. - handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); - break; - } } - break; - } - - case KeyEvent.KEYCODE_ENDCALL: { - result &= ~ACTION_PASS_TO_USER; - if (down) { - ITelephony telephonyService = getTelephonyService(); - boolean hungUp = false; - if (telephonyService != null) { - try { - hungUp = telephonyService.endCall(); - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); - } - } - interceptPowerKeyDown(!isScreenOn || hungUp); - } else { - if (interceptPowerKeyUp(canceled)) { - if ((mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) { - if (goHome()) { + if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) { + if (mVolBtnMusicControls && down && (keyCode != KeyEvent.KEYCODE_VOLUME_MUTE)) { + mIsLongPress = false; + handleVolumeLongPress(keyCode); + break; + } else { + if (mVolBtnMusicControls && !down) { + handleVolumeLongPressAbort(); + if (mIsLongPress) { break; } } - if ((mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { - result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; + if (!isScreenOn && !mVolumeWakeScreen) { + handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); } } } - break; + if (isScreenOn || !mVolumeWakeScreen) { + break; + } else if (keyguardActive) { + keyCode = KeyEvent.KEYCODE_POWER; + mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode); + } else { + result |= ACTION_WAKE_UP; + break; + } } case KeyEvent.KEYCODE_POWER: { diff --git a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java index b6ffde0..d1651fc 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2012 The CyanogenMod Project (Weather, Calendar) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +30,12 @@ import libcore.util.MutableInt; import android.content.ContentResolver; import android.content.Context; +import android.content.res.Resources; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationManager; +import android.os.Handler; +import android.os.Message; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateFormat; @@ -36,8 +43,27 @@ import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; +import com.android.internal.R; +import com.android.internal.util.weather.HttpRetriever; +import com.android.internal.util.weather.WeatherInfo; +import com.android.internal.util.weather.WeatherXmlParser; +import com.android.internal.util.weather.YahooPlaceFinder; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.TransportControlView; + +import org.w3c.dom.Document; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; + +import libcore.util.MutableInt; + /*** * Manages a number of views inside of LockScreen layouts. See below for a list of widgets * @@ -71,6 +97,11 @@ class KeyguardStatusViewManager implements OnClickListener { private TextView mOwnerInfoView; private TextView mAlarmStatusView; private TransportControlView mTransportView; + private RelativeLayout mWeatherPanel, mWeatherTempsPanel; + private TextView mWeatherCity, mWeatherCondition, mWeatherLowHigh, mWeatherTemp, mWeatherUpdateTime; + private ImageView mWeatherImage; + private LinearLayout mCalendarPanel; + private TextView mCalendarEventTitle, mCalendarEventDetails; // Top-level container view for above views private View mContainer; @@ -185,6 +216,32 @@ class KeyguardStatusViewManager implements OnClickListener { mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen; mDigitalClock = (DigitalClock) findViewById(R.id.time); + // Weather panel + mWeatherPanel = (RelativeLayout) findViewById(R.id.weather_panel); + mWeatherCity = (TextView) findViewById(R.id.weather_city); + mWeatherCondition = (TextView) findViewById(R.id.weather_condition); + mWeatherImage = (ImageView) findViewById(R.id.weather_image); + mWeatherTemp = (TextView) findViewById(R.id.weather_temp); + mWeatherLowHigh = (TextView) findViewById(R.id.weather_low_high); + mWeatherUpdateTime = (TextView) findViewById(R.id.update_time); + mWeatherTempsPanel = (RelativeLayout) findViewById(R.id.weather_temps_panel); + + // Hide Weather panel view until we know we need to show it. + if (mWeatherPanel != null) { + mWeatherPanel.setVisibility(View.GONE); + mWeatherPanel.setOnClickListener(this); + } + + // Calendar panel + mCalendarPanel = (LinearLayout) findViewById(R.id.calendar_panel); + mCalendarEventTitle = (TextView) findViewById(R.id.calendar_event_title); + mCalendarEventDetails = (TextView) findViewById(R.id.calendar_event_details); + + // Hide calendar panel view until we know we need to show it. + if (mCalendarPanel != null) { + mCalendarPanel.setVisibility(View.GONE); + } + // Hide transport control view until we know we need to show it. if (mTransportView != null) { mTransportView.setVisibility(View.GONE); @@ -204,10 +261,12 @@ class KeyguardStatusViewManager implements OnClickListener { resetStatusInfo(); refreshDate(); updateOwnerInfo(); + refreshWeather(); + refreshCalendar(); // Required to get Marquee to work. final View scrollableViews[] = { mCarrierView, mDateView, mStatus1View, mOwnerInfoView, - mAlarmStatusView }; + mAlarmStatusView, mCalendarEventDetails, mWeatherCity, mWeatherCondition }; for (View v : scrollableViews) { if (v != null) { v.setSelected(true); @@ -215,6 +274,303 @@ class KeyguardStatusViewManager implements OnClickListener { } } + /* + * CyanogenMod Lock screen Weather related functionality + */ + private static final String URL_YAHOO_API_WEATHER = "http://weather.yahooapis.com/forecastrss?w=%s&u="; + private static WeatherInfo mWeatherInfo = new WeatherInfo(); + private static final int QUERY_WEATHER = 0; + private static final int UPDATE_WEATHER = 1; + + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case QUERY_WEATHER: + Thread queryWeather = new Thread(new Runnable() { + @Override + public void run() { + LocationManager locationManager = (LocationManager) getContext(). + getSystemService(Context.LOCATION_SERVICE); + final ContentResolver resolver = getContext().getContentResolver(); + boolean useCustomLoc = Settings.System.getInt(resolver, + Settings.System.WEATHER_USE_CUSTOM_LOCATION, 0) == 1; + String customLoc = Settings.System.getString(resolver, + Settings.System.WEATHER_CUSTOM_LOCATION); + String woeid = null; + + // custom location + if (customLoc != null && useCustomLoc) { + try { + woeid = YahooPlaceFinder.GeoCode(getContext().getApplicationContext(), customLoc); + if (DEBUG) + Log.d(TAG, "Yahoo location code for " + customLoc + " is " + woeid); + } catch (Exception e) { + Log.e(TAG, "ERROR: Could not get Location code"); + e.printStackTrace(); + } + // network location + } else { + Criteria crit = new Criteria(); + crit.setAccuracy(Criteria.ACCURACY_COARSE); + String bestProvider = locationManager.getBestProvider(crit, true); + Location loc = null; + if (bestProvider != null) { + loc = locationManager.getLastKnownLocation(bestProvider); + } else { + loc = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); + } + try { + woeid = YahooPlaceFinder.reverseGeoCode(getContext(), loc.getLatitude(), + loc.getLongitude()); + if (DEBUG) + Log.d(TAG, "Yahoo location code for current geolocation is " + woeid); + } catch (Exception e) { + Log.e(TAG, "ERROR: Could not get Location code"); + e.printStackTrace(); + } + } + Message msg = Message.obtain(); + msg.what = UPDATE_WEATHER; + msg.obj = woeid; + mHandler.sendMessage(msg); + } + }); + queryWeather.setPriority(Thread.MIN_PRIORITY); + queryWeather.start(); + break; + case UPDATE_WEATHER: + String woeid = (String) msg.obj; + if (woeid != null) { + if (DEBUG) { + Log.d(TAG, "Location code is " + woeid); + } + WeatherInfo w = null; + try { + w = parseXml(getDocument(woeid)); + } catch (Exception e) { + } + if (w == null) { + setNoWeatherData(); + } else { + setWeatherData(w); + mWeatherInfo = w; + } + } else { + if (mWeatherInfo.temp.equals(WeatherInfo.NODATA)) { + setNoWeatherData(); + } else { + setWeatherData(mWeatherInfo); + } + } + break; + } + } + }; + + /** + * Reload the weather forecast + */ + private void refreshWeather() { + final ContentResolver resolver = getContext().getContentResolver(); + boolean showWeather = Settings.System.getInt(resolver,Settings.System.LOCKSCREEN_WEATHER, 0) == 1; + + if (showWeather) { + final long interval = Settings.System.getLong(resolver, + Settings.System.WEATHER_UPDATE_INTERVAL, 60); // Default to hourly + boolean manualSync = (interval == 0); + if (!manualSync && (((System.currentTimeMillis() - mWeatherInfo.last_sync) / 60000) >= interval)) { + mHandler.sendEmptyMessage(QUERY_WEATHER); + } else if (manualSync && mWeatherInfo.last_sync == 0) { + setNoWeatherData(); + } else { + setWeatherData(mWeatherInfo); + } + } else { + // Hide the Weather panel view + if (mWeatherPanel != null) { + mWeatherPanel.setVisibility(View.GONE); + } + } + } + + /** + * Display the weather information + * @param w + */ + private void setWeatherData(WeatherInfo w) { + final ContentResolver resolver = getContext().getContentResolver(); + final Resources res = getContext().getResources(); + boolean showLocation = Settings.System.getInt(resolver, + Settings.System.WEATHER_SHOW_LOCATION, 1) == 1; + boolean showTimestamp = Settings.System.getInt(resolver, + Settings.System.WEATHER_SHOW_TIMESTAMP, 1) == 1; + boolean invertLowhigh = Settings.System.getInt(resolver, + Settings.System.WEATHER_INVERT_LOWHIGH, 0) == 1; + + if (mWeatherPanel != null) { + if (mWeatherImage != null) { + String conditionCode = w.condition_code; + String condition_filename = "weather_" + conditionCode; + int resID = res.getIdentifier(condition_filename, "drawable", + getContext().getPackageName()); + + if (DEBUG) + Log.d("Weather", "Condition:" + conditionCode + " ID:" + resID); + + if (resID != 0) { + mWeatherImage.setImageDrawable(res.getDrawable(resID)); + } else { + mWeatherImage.setImageResource(R.drawable.weather_na); + } + } + if (mWeatherCity != null) { + mWeatherCity.setText(w.city); + mWeatherCity.setVisibility(showLocation ? View.VISIBLE : View.GONE); + } + if (mWeatherCondition != null) { + mWeatherCondition.setText(w.condition); + mWeatherCondition.setVisibility(View.VISIBLE); + } + if (mWeatherUpdateTime != null) { + Date lastTime = new Date(mWeatherInfo.last_sync); + String date = DateFormat.getDateFormat(getContext()).format(lastTime); + String time = DateFormat.getTimeFormat(getContext()).format(lastTime); + mWeatherUpdateTime.setText(date + " " + time); + mWeatherUpdateTime.setVisibility(showTimestamp ? View.VISIBLE : View.GONE); + } + if (mWeatherTempsPanel != null && mWeatherTemp != null && mWeatherLowHigh != null) { + mWeatherTemp.setText(w.temp); + mWeatherLowHigh.setText(invertLowhigh ? w.high + " | " + w.low : w.low + " | " + w.high); + mWeatherTempsPanel.setVisibility(View.VISIBLE); + } + + // Show the Weather panel view + mWeatherPanel.setVisibility(View.VISIBLE); + } + } + + /** + * There is no data to display, display 'empty' fields and the + * 'Tap to reload' message + */ + private void setNoWeatherData() { + + if (mWeatherPanel != null) { + if (mWeatherImage != null) { + mWeatherImage.setImageResource(R.drawable.weather_na); + } + if (mWeatherCity != null) { + mWeatherCity.setText(R.string.weather_no_data); + mWeatherCity.setVisibility(View.VISIBLE); + } + if (mWeatherCondition != null) { + mWeatherCondition.setText(R.string.weather_tap_to_refresh); + } + if (mWeatherUpdateTime != null) { + mWeatherUpdateTime.setVisibility(View.GONE); + } + if (mWeatherTempsPanel != null ) { + mWeatherTempsPanel.setVisibility(View.GONE); + } + + // Show the Weather panel view + mWeatherPanel.setVisibility(View.VISIBLE); + } + } + + /** + * Get the weather forecast XML document for a specific location + * @param woeid + * @return + */ + private Document getDocument(String woeid) { + try { + boolean celcius = Settings.System.getInt(getContext().getContentResolver(), + Settings.System.WEATHER_USE_METRIC, 1) == 1; + String urlWithDegreeUnit; + + if (celcius) { + urlWithDegreeUnit = URL_YAHOO_API_WEATHER + "c"; + } else { + urlWithDegreeUnit = URL_YAHOO_API_WEATHER + "f"; + } + + return new HttpRetriever().getDocumentFromURL(String.format(urlWithDegreeUnit, woeid)); + } catch (IOException e) { + Log.e(TAG, "Error querying Yahoo weather"); + } + + return null; + } + + /** + * Parse the weather XML document + * @param wDoc + * @return + */ + private WeatherInfo parseXml(Document wDoc) { + try { + return new WeatherXmlParser(getContext()).parseWeatherResponse(wDoc); + } catch (Exception e) { + Log.e(TAG, "Error parsing Yahoo weather XML document"); + e.printStackTrace(); + } + return null; + } + + /* + * CyanogenMod Lock screen Calendar related functionality + */ + + private void refreshCalendar() { + if (mCalendarPanel != null) { + final ContentResolver resolver = getContext().getContentResolver(); + String[] nextCalendar = null; + boolean visible = false; // Assume we are not showing the view + + // Load the settings + boolean lockCalendar = (Settings.System.getInt(resolver, + Settings.System.LOCKSCREEN_CALENDAR, 0) == 1); + String[] calendars = parseStoredValue(Settings.System.getString( + resolver, Settings.System.LOCKSCREEN_CALENDARS)); + boolean lockCalendarRemindersOnly = (Settings.System.getInt(resolver, + Settings.System.LOCKSCREEN_CALENDAR_REMINDERS_ONLY, 0) == 1); + long lockCalendarLookahead = Settings.System.getLong(resolver, + Settings.System.LOCKSCREEN_CALENDAR_LOOKAHEAD, 10800000); + + if (lockCalendar) { + nextCalendar = mLockPatternUtils.getNextCalendarAlarm(lockCalendarLookahead, + calendars, lockCalendarRemindersOnly); + if (nextCalendar[0] != null && mCalendarEventTitle != null) { + mCalendarEventTitle.setText(nextCalendar[0].toString()); + if (nextCalendar[1] != null && mCalendarEventDetails != null) { + mCalendarEventDetails.setText(nextCalendar[1]); + } + visible = true; + } + } + + mCalendarPanel.setVisibility(visible ? View.VISIBLE : View.GONE); + } + } + + /** + * Split the MultiSelectListPreference string based on a separator of ',' and + * stripping off the start [ and the end ] + * @param val + * @return + */ + private static String[] parseStoredValue(String val) { + if (val == null || val.isEmpty()) + return null; + else { + // Strip off the start [ and the end ] before splitting + val = val.substring(1, val.length() -1); + return (val.split(",")); + } + } + private boolean inWidgetMode() { return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE; } @@ -657,6 +1013,16 @@ class KeyguardStatusViewManager implements OnClickListener { public void onClick(View v) { if (v == mEmergencyCallButton) { mCallback.takeEmergencyCallAction(); + } else if (v == mWeatherPanel) { + // Indicate we are refreshing + if (mWeatherCondition != null) { + mWeatherCondition.setText(R.string.weather_refreshing); + } + + mCallback.pokeWakelock(); + if (!mHandler.hasMessages(QUERY_WEATHER)) { + mHandler.sendEmptyMessage(QUERY_WEATHER); + } } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java index 3de1428..bf43199 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java @@ -25,6 +25,8 @@ import com.android.internal.widget.LockPatternUtils; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.Profile; +import android.app.ProfileManager; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -248,6 +250,8 @@ public class KeyguardViewMediator implements KeyguardViewCallback { private int mUnlockSoundId; private int mLockSoundStreamId; + private ProfileManager mProfileManager; + /** * The volume applied to the lock/unlock sounds. */ @@ -369,6 +373,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback { mWakeAndHandOff.setReferenceCounted(false); mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION)); + mProfileManager = (ProfileManager) context.getSystemService(Context.PROFILE_SERVICE); mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); @@ -434,11 +439,21 @@ public class KeyguardViewMediator implements KeyguardViewCallback { mScreenOn = false; if (DEBUG) Log.d(TAG, "onScreenTurnedOff(" + why + ")"); - // Lock immediately based on setting if secure (user has a pin/pattern/password). - // This also "locks" the device when not secure to provide easy access to the - // camera while preventing unwanted input. - final boolean lockImmediately = - mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure(); + // Prepare for handling Lock/Slide lock delay and timeout + boolean lockImmediately = false; + final ContentResolver cr = mContext.getContentResolver(); + boolean separateSlideLockTimeoutEnabled = Settings.System.getInt(cr, + Settings.System.SCREEN_LOCK_SLIDE_DELAY_TOGGLE, 0) == 1; + if (mLockPatternUtils.isSecure()) { + // Lock immediately based on setting if secure (user has a pin/pattern/password) + // This is retained as-is to ensue AOSP security integrity is maintained + lockImmediately = mLockPatternUtils.getPowerButtonInstantlyLocks(); + } else { + // Unless a separate slide lock timeout is enabled, this "locks" the device when + // not secure to provide easy access to the camera while preventing unwanted input + lockImmediately = separateSlideLockTimeoutEnabled ? false + : mLockPatternUtils.getPowerButtonInstantlyLocks(); + } if (mExitSecureCallback != null) { if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled"); @@ -457,7 +472,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback { // to enable it a little bit later (i.e, give the user a chance // to turn the screen back on within a certain window without // having to unlock the screen) - final ContentResolver cr = mContext.getContentResolver(); // From DisplaySettings long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT, @@ -468,17 +482,33 @@ public class KeyguardViewMediator implements KeyguardViewCallback { Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, KEYGUARD_LOCK_AFTER_DELAY_DEFAULT); + // From CyanogenMod specific Settings + int slideLockTimeoutDelay = (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT ? Settings.System + .getInt(cr, Settings.System.SCREEN_LOCK_SLIDE_TIMEOUT_DELAY, + KEYGUARD_LOCK_AFTER_DELAY_DEFAULT) : Settings.System.getInt(cr, + Settings.System.SCREEN_LOCK_SLIDE_SCREENOFF_DELAY, 0)); + // From DevicePolicyAdmin final long policyTimeout = mLockPatternUtils.getDevicePolicyManager() .getMaximumTimeToLock(null); + if (DEBUG) Log.d(TAG, "Security lock screen timeout delay is " + lockAfterTimeout + + " ms; slide lock screen timeout delay is " + + slideLockTimeoutDelay + + " ms; Separate slide lock delay settings considered: " + + separateSlideLockTimeoutEnabled + + "; Policy timeout is " + + policyTimeout + + " ms"); + long timeout; if (policyTimeout > 0) { // policy in effect. Make sure we don't go beyond policy limit. displayTimeout = Math.max(displayTimeout, 0); // ignore negative values timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout); } else { - timeout = lockAfterTimeout; + // Not sure lockAfterTimeout is needed any more but keeping it for AOSP compatibility + timeout = separateSlideLockTimeoutEnabled ? slideLockTimeoutDelay : lockAfterTimeout; } if (timeout <= 0) { @@ -708,6 +738,13 @@ public class KeyguardViewMediator implements KeyguardViewCallback { return; } + // if the current profile has disabled us, don't show + if (!lockedOrMissing + && mProfileManager.getActiveProfile().getScreenLockMode() == Profile.LockMode.DISABLE) { + if (DEBUG) Log.d(TAG, "doKeyguard: not showing because of profile override"); + return; + } + if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen"); showLocked(); } diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index dbffa97..537d69f 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -24,6 +24,9 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.graphics.Color; import android.os.BatteryManager; import android.os.Binder; import android.os.FileUtils; @@ -44,7 +47,7 @@ import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; - +import java.util.Calendar; /** * <p>BatteryService monitors the charging status, and charge level of the device @@ -140,10 +143,21 @@ public final class BatteryService extends Binder { private boolean mUpdatesStopped; private Led mLed; + private boolean mLightEnabled; + private boolean mLedPulseEnabled; + private int mBatteryLowARGB; + private int mBatteryMediumARGB; + private int mBatteryFullARGB; + private boolean mMultiColorLed; private boolean mSentLowBatteryBroadcast = false; private native void native_update(); + // Quiet hours support + private boolean mQuietHoursEnabled = false; + private int mQuietHoursStart = 0; + private int mQuietHoursEnd = 0; + private boolean mQuietHoursDim = true; public BatteryService(Context context, LightsService lights) { mContext = context; @@ -168,6 +182,9 @@ public final class BatteryService extends Binder { "DEVPATH=/devices/virtual/switch/invalid_charger"); } + SettingsObserver observer = new SettingsObserver(new Handler()); + observer.observe(); + // set initial status synchronized (mLock) { updateLocked(); @@ -672,6 +689,10 @@ public final class BatteryService extends Binder { } }; + private synchronized void updateLedPulse() { + mLed.updateLightsLocked(); + } + private final class Led { private final LightsService.Light mBatteryLight; @@ -684,12 +705,17 @@ public final class BatteryService extends Binder { public Led(Context context, LightsService lights) { mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY); - mBatteryLowARGB = context.getResources().getInteger( + mBatteryLowARGB = mContext.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryLowARGB); - mBatteryMediumARGB = context.getResources().getInteger( + mBatteryMediumARGB = mContext.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryMediumARGB); - mBatteryFullARGB = context.getResources().getInteger( + mBatteryFullARGB = mContext.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryFullARGB); + + // Does the Device support changing battery LED colors? + mMultiColorLed = context.getResources().getBoolean( + com.android.internal.R.bool.config_multiColorBatteryLed); + mBatteryLedOn = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryLedOn); mBatteryLedOff = context.getResources().getInteger( @@ -702,28 +728,143 @@ public final class BatteryService extends Binder { public void updateLightsLocked() { final int level = mBatteryLevel; final int status = mBatteryStatus; - if (level < mLowBatteryWarningLevel) { + + if (!mLightEnabled) { + // No lights if explicitly disabled + mBatteryLight.turnOff(); + } else if (inQuietHours() && mQuietHoursDim) { + if (mLedPulseEnabled && level < mLowBatteryWarningLevel && + status != BatteryManager.BATTERY_STATUS_CHARGING) { + // The battery is low, the device is not charging and the low battery pulse + // is enabled - ignore Quiet Hours + mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED, + mBatteryLedOn, mBatteryLedOff); + } else { + // No lights if in Quiet Hours and battery not low + mBatteryLight.turnOff(); + } + } else if (level < mLowBatteryWarningLevel) { if (status == BatteryManager.BATTERY_STATUS_CHARGING) { - // Solid red when battery is charging + // Battery is charging and low mBatteryLight.setColor(mBatteryLowARGB); - } else { - // Flash red when battery is low and not charging + } else if (mLedPulseEnabled) { + // Battery is low and not charging mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED, mBatteryLedOn, mBatteryLedOff); + } else { + // "Pulse low battery light" is disabled, no lights. + mBatteryLight.turnOff(); } } else if (status == BatteryManager.BATTERY_STATUS_CHARGING - || status == BatteryManager.BATTERY_STATUS_FULL) { + || status == BatteryManager.BATTERY_STATUS_FULL) { if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) { - // Solid green when full or charging and nearly full + // Battery is full or charging and nearly full mBatteryLight.setColor(mBatteryFullARGB); } else { - // Solid orange when charging and halfway full + // Battery is charging and halfway full mBatteryLight.setColor(mBatteryMediumARGB); } } else { - // No lights if not charging and not low + //No lights if not charging and not low mBatteryLight.turnOff(); } } } + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + + // Battery light enabled + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.BATTERY_LIGHT_ENABLED), false, this); + + // Low battery pulse + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.BATTERY_LIGHT_PULSE), false, this); + + // Light colors + if (mMultiColorLed) { + // Register observer if we have a multi color led + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.BATTERY_LIGHT_LOW_COLOR), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.BATTERY_LIGHT_MEDIUM_COLOR), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.BATTERY_LIGHT_FULL_COLOR), false, this); + } + + // Quiet Hours + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_ENABLED), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_START), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_END), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_DIM), false, this); + + update(); + } + + @Override public void onChange(boolean selfChange) { + update(); + } + + public void update() { + ContentResolver resolver = mContext.getContentResolver(); + Resources res = mContext.getResources(); + + // Battery light enabled + mLightEnabled = Settings.System.getInt(resolver, + Settings.System.BATTERY_LIGHT_ENABLED, 1) != 0; + + // Low battery pulse + mLedPulseEnabled = Settings.System.getInt(resolver, + Settings.System.BATTERY_LIGHT_PULSE, 1) != 0; + + // Light colors + mBatteryLowARGB = Settings.System.getInt(resolver, + Settings.System.BATTERY_LIGHT_LOW_COLOR, + res.getInteger(com.android.internal.R.integer.config_notificationsBatteryLowARGB)); + mBatteryMediumARGB = Settings.System.getInt(resolver, + Settings.System.BATTERY_LIGHT_MEDIUM_COLOR, + res.getInteger(com.android.internal.R.integer.config_notificationsBatteryMediumARGB)); + mBatteryFullARGB = Settings.System.getInt(resolver, + Settings.System.BATTERY_LIGHT_FULL_COLOR, + res.getInteger(com.android.internal.R.integer.config_notificationsBatteryFullARGB)); + + // Quiet Hours + mQuietHoursEnabled = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_ENABLED, 0) != 0; + mQuietHoursStart = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_START, 0); + mQuietHoursEnd = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_END, 0); + mQuietHoursDim = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_DIM, 0) != 0; + + updateLedPulse(); + } + } + + private boolean inQuietHours() { + if (mQuietHoursEnabled && (mQuietHoursStart != mQuietHoursEnd)) { + // Get the date in "quiet hours" format. + Calendar calendar = Calendar.getInstance(); + int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE); + if (mQuietHoursEnd < mQuietHoursStart) { + // Starts at night, ends in the morning. + return (minutes > mQuietHoursStart) || (minutes < mQuietHoursEnd); + } else { + return (minutes > mQuietHoursStart) && (minutes < mQuietHoursEnd); + } + } + return false; + } + } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index c9ff595..b11432d 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -397,6 +397,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.ENABLED_INPUT_METHODS), false, this); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUS_BAR_IME_SWITCHER), + false, new ContentObserver(mHandler) { + public void onChange(boolean selfChange) { + updateFromSettingsLocked(); + } + }); } @Override public void onChange(boolean selfChange) { @@ -844,8 +851,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mStatusBar = statusBar; statusBar.setIconVisibility("ime", false); updateImeWindowStatusLocked(); - mShowOngoingImeSwitcherForPhones = mRes.getBoolean( - com.android.internal.R.bool.show_ongoing_ime_switcher); if (mShowOngoingImeSwitcherForPhones) { mWindowManagerService.setOnHardKeyboardStatusChangeListener( mHardKeyboardListener); @@ -1597,6 +1602,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurMethodId = null; unbindCurrentMethodLocked(true, false); } + mShowOngoingImeSwitcherForPhones = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.STATUS_BAR_IME_SWITCHER, 1) == 1; } /* package */ void setInputMethodLocked(String id, int subtypeId) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index f3a38f0..af49135 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -27,7 +27,12 @@ import android.app.IActivityManager; import android.app.INotificationManager; import android.app.ITransientNotification; import android.app.Notification; +import android.app.NotificationGroup; +import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.Profile; +import android.app.ProfileGroup; +import android.app.ProfileManager; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -44,6 +49,7 @@ import android.media.IAudioService; import android.media.IRingtonePlayer; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -81,7 +87,10 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.Calendar; +import java.util.Map; import libcore.io.IoUtils; @@ -134,10 +143,13 @@ public class NotificationManagerService extends INotificationManager.Stub private IAudioService mAudioService; private Vibrator mVibrator; - // for enabling and disabling notification pulse behavior + // for enabling and disabling notification pulse behaviour private boolean mScreenOn = true; + private boolean mWasScreenOn = false; private boolean mInCall = false; private boolean mNotificationPulseEnabled; + private HashMap<String, NotificationLedValues> mNotificationPulseCustomLedValues; + private Map<String, String> mPackageNameMappings; private final ArrayList<NotificationRecord> mNotificationList = new ArrayList<NotificationRecord>(); @@ -147,6 +159,18 @@ public class NotificationManagerService extends INotificationManager.Stub private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); private NotificationRecord mLedNotification; + private boolean mQuietHoursEnabled = false; + // Minutes from midnight when quiet hours begin. + private int mQuietHoursStart = 0; + // Minutes from midnight when quiet hours end. + private int mQuietHoursEnd = 0; + // Don't play sounds. + private boolean mQuietHoursMute = true; + // Don't vibrate. + private boolean mQuietHoursStill = true; + // Dim LED if hardware supports it. + private boolean mQuietHoursDim = true; + // Notification control database. For now just contains disabled packages. private AtomicFile mPolicyFile; private HashSet<String> mBlockedPackages = new HashSet<String>(); @@ -402,6 +426,12 @@ public class NotificationManagerService extends INotificationManager.Stub } } + class NotificationLedValues { + public int color; + public int onMS; + public int offMS; + } + private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks = new StatusBarManagerService.NotificationCallbacks() { @@ -553,6 +583,8 @@ public class NotificationManagerService extends INotificationManager.Stub mScreenOn = true; } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; + mWasScreenOn = true; + updateLightsLocked(); } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( TelephonyManager.EXTRA_STATE_OFFHOOK)); @@ -569,8 +601,8 @@ public class NotificationManagerService extends INotificationManager.Stub } }; - class SettingsObserver extends ContentObserver { - SettingsObserver(Handler handler) { + class LEDSettingsObserver extends ContentObserver { + LEDSettingsObserver(Handler handler) { super(handler); } @@ -578,24 +610,96 @@ public class NotificationManagerService extends INotificationManager.Stub ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.NOTIFICATION_LIGHT_PULSE), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES), false, this); update(); } @Override public void onChange(boolean selfChange) { update(); + updateNotificationPulse(); } public void update() { ContentResolver resolver = mContext.getContentResolver(); - boolean pulseEnabled = Settings.System.getInt(resolver, - Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; - if (mNotificationPulseEnabled != pulseEnabled) { - mNotificationPulseEnabled = pulseEnabled; - updateNotificationPulse(); + // LED enabled + mNotificationPulseEnabled = Settings.System.getInt(resolver, + Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; + + // LED default color + mDefaultNotificationColor = Settings.System.getInt(resolver, + Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR, mDefaultNotificationColor); + + // LED default on MS + mDefaultNotificationLedOn = Settings.System.getInt(resolver, + Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON, mDefaultNotificationLedOn); + + // LED default off MS + mDefaultNotificationLedOff = Settings.System.getInt(resolver, + Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF, mDefaultNotificationLedOff); + + // LED custom notification colors + mNotificationPulseCustomLedValues.clear(); + if (Settings.System.getInt(resolver, + Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, 0) != 0) { + parseNotificationPulseCustomValuesString(Settings.System.getString(resolver, + Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES)); } } } + class QuietHoursSettingsObserver extends ContentObserver { + QuietHoursSettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_ENABLED), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_START), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_END), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_MUTE), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_STILL), false, this); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.QUIET_HOURS_DIM), false, this); + update(); + } + + @Override public void onChange(boolean selfChange) { + update(); + updateNotificationPulse(); + } + + public void update() { + ContentResolver resolver = mContext.getContentResolver(); + mQuietHoursEnabled = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_ENABLED, 0) != 0; + mQuietHoursStart = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_START, 0); + mQuietHoursEnd = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_END, 0); + mQuietHoursMute = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_MUTE, 0) != 0; + mQuietHoursStill = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_STILL, 0) != 0; + mQuietHoursDim = Settings.System.getInt(resolver, + Settings.System.QUIET_HOURS_DIM, 0) != 0; + } + } + NotificationManagerService(Context context, StatusBarManagerService statusBar, LightsService lights) { @@ -622,6 +726,15 @@ public class NotificationManagerService extends INotificationManager.Stub mDefaultNotificationLedOff = resources.getInteger( com.android.internal.R.integer.config_defaultNotificationLedOff); + mNotificationPulseCustomLedValues = new HashMap<String, NotificationLedValues>(); + + mPackageNameMappings = new HashMap<String, String>(); + for(String mapping : resources.getStringArray( + com.android.internal.R.array.notification_light_package_mapping)) { + String[] map = mapping.split("\\|"); + mPackageNameMappings.put(map[0], map[1]); + } + // Don't start allowing notifications until the setup wizard has run once. // After that, including subsequent boots, init with notifications turned on. // This works on the first boot because the setup wizard will toggle this @@ -649,8 +762,10 @@ public class NotificationManagerService extends INotificationManager.Stub IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mIntentReceiver, sdFilter); - SettingsObserver observer = new SettingsObserver(mHandler); - observer.observe(); + LEDSettingsObserver ledObserver = new LEDSettingsObserver(mHandler); + ledObserver.observe(); + QuietHoursSettingsObserver qhObserver = new QuietHoursSettingsObserver(mHandler); + qhObserver.observe(); } void systemReady() { @@ -965,6 +1080,8 @@ public class NotificationManagerService extends INotificationManager.Stub } synchronized (mNotificationList) { + final boolean inQuietHours = inQuietHours(); + NotificationRecord r = new NotificationRecord(pkg, tag, id, callingUid, callingPid, userId, score, @@ -1040,6 +1157,16 @@ public class NotificationManagerService extends INotificationManager.Stub } } + try { + final ProfileManager profileManager = + (ProfileManager) mContext.getSystemService(Context.PROFILE_SERVICE); + + ProfileGroup group = profileManager.getActiveProfileGroup(pkg); + notification = group.processNotification(notification); + } catch(Throwable th) { + Log.e(TAG, "An error occurred profiling the notification.", th); + } + // If we're not supposed to beep, vibrate, etc. then don't. if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) && (!(old != null @@ -1053,7 +1180,8 @@ public class NotificationManagerService extends INotificationManager.Stub // sound final boolean useDefaultSound = (notification.defaults & Notification.DEFAULT_SOUND) != 0; - if (useDefaultSound || notification.sound != null) { + if (!(inQuietHours && mQuietHoursMute) + && (useDefaultSound || notification.sound != null)) { Uri uri; if (useDefaultSound) { uri = Settings.System.DEFAULT_NOTIFICATION_URI; @@ -1096,8 +1224,8 @@ public class NotificationManagerService extends INotificationManager.Stub final boolean useDefaultVibrate = (notification.defaults & Notification.DEFAULT_VIBRATE) != 0 || convertSoundToVibration; - - if ((useDefaultVibrate || notification.vibrate != null) + if (!(inQuietHours && mQuietHoursStill) + && (useDefaultVibrate || notification.vibrate != null) && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) { mVibrateNotification = r; @@ -1131,6 +1259,21 @@ public class NotificationManagerService extends INotificationManager.Stub idOut[0] = id; } + private boolean inQuietHours() { + if (mQuietHoursEnabled && (mQuietHoursStart != mQuietHoursEnd)) { + // Get the date in "quiet hours" format. + Calendar calendar = Calendar.getInstance(); + int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE); + if (mQuietHoursEnd < mQuietHoursStart) { + // Starts at night, ends in the morning. + return (minutes > mQuietHoursStart) || (minutes < mQuietHoursEnd); + } else { + return (minutes > mQuietHoursStart) && (minutes < mQuietHoursEnd); + } + } + return false; + } + private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { AccessibilityManager manager = AccessibilityManager.getInstance(mContext); if (!manager.isEnabled()) { @@ -1376,17 +1519,45 @@ public class NotificationManagerService extends INotificationManager.Stub } } - // Don't flash while we are in a call or screen is on - if (mLedNotification == null || mInCall || mScreenOn) { + boolean wasScreenOn = mWasScreenOn; + mWasScreenOn = false; + + if (mLedNotification == null) { + mNotificationLight.turnOff(); + return; + } + + // We can assume that if the user turned the screen off while there was + // still an active notification then they wanted to keep the notification + // for later. In this case we shouldn't flash the notification light. + // For special notifications that automatically turn the screen on (such + // as missed calls), we use this flag to force the notification light + // even if the screen was turned off. + boolean forceWithScreenOff = (mLedNotification.notification.flags & + Notification.FLAG_FORCE_LED_SCREEN_OFF) != 0; + + // Don't flash while we are in a call, screen is on or we are in quiet hours with light dimmed + if (mInCall || mScreenOn || (inQuietHours() && mQuietHoursDim) || (wasScreenOn && !forceWithScreenOff)) { mNotificationLight.turnOff(); } else { - int ledARGB = mLedNotification.notification.ledARGB; - int ledOnMS = mLedNotification.notification.ledOnMS; - int ledOffMS = mLedNotification.notification.ledOffMS; - if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { - ledARGB = mDefaultNotificationColor; - ledOnMS = mDefaultNotificationLedOn; - ledOffMS = mDefaultNotificationLedOff; + int ledARGB; + int ledOnMS; + int ledOffMS; + NotificationLedValues ledValues = getLedValuesForNotification(mLedNotification); + if (ledValues != null) { + ledARGB = ledValues.color != 0 ? ledValues.color : mDefaultNotificationColor; + ledOnMS = ledValues.onMS >= 0 ? ledValues.onMS : mDefaultNotificationLedOn; + ledOffMS = ledValues.offMS >= 0 ? ledValues.offMS : mDefaultNotificationLedOn; + } else { + if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { + ledARGB = mDefaultNotificationColor; + ledOnMS = mDefaultNotificationLedOn; + ledOffMS = mDefaultNotificationLedOff; + } else { + ledARGB = mLedNotification.notification.ledARGB; + ledOnMS = mLedNotification.notification.ledOnMS; + ledOffMS = mLedNotification.notification.ledOffMS; + } } if (mNotificationPulseEnabled) { // pulse repeatedly @@ -1396,6 +1567,47 @@ public class NotificationManagerService extends INotificationManager.Stub } } + private void parseNotificationPulseCustomValuesString(String customLedValuesString) { + if (TextUtils.isEmpty(customLedValuesString)) { + return; + } + + for (String packageValuesString : customLedValuesString.split("\\|")) { + String[] packageValues = packageValuesString.split("="); + if (packageValues.length != 2) { + Log.e(TAG, "Error parsing custom led values for unknown package"); + continue; + } + String packageName = packageValues[0]; + String[] values = packageValues[1].split(";"); + if (values.length != 3) { + Log.e(TAG, "Error parsing custom led values '" + packageValues[1] + "' for " + packageName); + continue; + } + NotificationLedValues ledValues = new NotificationLedValues(); + try { + ledValues.color = Integer.parseInt(values[0]); + ledValues.onMS = Integer.parseInt(values[1]); + ledValues.offMS = Integer.parseInt(values[2]); + } catch (Exception e) { + Log.e(TAG, "Error parsing custom led values '" + packageValues[1] + "' for " + packageName); + continue; + } + mNotificationPulseCustomLedValues.put(packageName, ledValues); + } + } + + private NotificationLedValues getLedValuesForNotification(NotificationRecord ledNotification) { + return mNotificationPulseCustomLedValues.get(mapPackage(ledNotification.pkg)); + } + + private String mapPackage(String pkg) { + if(!mPackageNameMappings.containsKey(pkg)) { + return pkg; + } + return mPackageNameMappings.get(pkg); + } + // lock on mNotificationList private int indexOfNotificationLocked(String pkg, String tag, int id, int userId) { diff --git a/services/java/com/android/server/ProfileManagerService.java b/services/java/com/android/server/ProfileManagerService.java new file mode 100644 index 0000000..aa769a5 --- /dev/null +++ b/services/java/com/android/server/ProfileManagerService.java @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2011, 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.server; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import android.app.IProfileManager; +import android.app.NotificationGroup; +import android.app.Profile; +import android.app.ProfileGroup; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.XmlResourceParser; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; +import android.os.ParcelUuid; + +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** {@hide} */ +public class ProfileManagerService extends IProfileManager.Stub { + // Enable the below for detailed logging of this class + private static final boolean LOCAL_LOGV = false; + /** + * <p>Broadcast Action: A new profile has been selected. This can be triggered by the user + * or by calls to the ProfileManagerService / Profile.</p> + * @hide + */ + public static final String INTENT_ACTION_PROFILE_SELECTED = "android.intent.action.PROFILE_SELECTED"; + + public static final String PERMISSION_CHANGE_SETTINGS = "android.permission.WRITE_SETTINGS"; + + private static final String PROFILE_FILENAME = "/data/system/profiles.xml"; + + private static final String TAG = "ProfileService"; + + private Map<UUID, Profile> mProfiles; + + // Match UUIDs and names, used for reverse compatibility + private Map<String, UUID> mProfileNames; + + private Map<UUID, NotificationGroup> mGroups; + + private Profile mActiveProfile; + + // Well-known UUID of the wildcard group + private static final UUID mWildcardUUID = UUID.fromString("a126d48a-aaef-47c4-baed-7f0e44aeffe5"); + private NotificationGroup mWildcardGroup; + + private Context mContext; + private boolean mDirty; + + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { + persistIfDirty(); + initialize(); + } else if (action.equals(Intent.ACTION_SHUTDOWN)) { + persistIfDirty(); + } + } + }; + + public ProfileManagerService(Context context) { + mContext = context; + + mWildcardGroup = new NotificationGroup( + context.getString(com.android.internal.R.string.wildcardProfile), + com.android.internal.R.string.wildcardProfile, + mWildcardUUID); + + initialize(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_LOCALE_CHANGED); + filter.addAction(Intent.ACTION_SHUTDOWN); + mContext.registerReceiver(mIntentReceiver, filter); + } + + private void initialize() { + initialize(false); + } + + private void initialize(boolean skipFile) { + mProfiles = new HashMap<UUID, Profile>(); + mProfileNames = new HashMap<String, UUID>(); + mGroups = new HashMap<UUID, NotificationGroup>(); + mDirty = false; + + boolean init = skipFile; + + if (! skipFile) { + try { + loadFromFile(); + } catch (RemoteException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + init = true; + } catch (IOException e) { + init = true; + } + } + + if (init) { + try { + initialiseStructure(); + } catch (Throwable ex) { + Log.e(TAG, "Error loading xml from resource: ", ex); + } + } + } + + @Override + public void resetAll() { + enforceChangePermissions(); + initialize(true); + } + + @Override + @Deprecated + public boolean setActiveProfileByName(String profileName) throws RemoteException, SecurityException { + if (mProfileNames.containsKey(profileName)) { + if (LOCAL_LOGV) Log.v(TAG, "setActiveProfile(String) found profile name in mProfileNames."); + return setActiveProfile(mProfiles.get(mProfileNames.get(profileName)), true); + } else { + // Since profileName could not be casted into a UUID, we can call it a string. + Log.w(TAG, "Unable to find profile to set active, based on string: " + profileName); + return false; + } + } + + @Override + public boolean setActiveProfile(ParcelUuid profileParcelUuid) throws RemoteException, SecurityException { + UUID profileUuid = profileParcelUuid.getUuid(); + if(mProfiles.containsKey(profileUuid)){ + if (LOCAL_LOGV) Log.v(TAG, "setActiveProfileByUuid(ParcelUuid) found profile UUID in mProfileNames."); + return setActiveProfile(mProfiles.get(profileUuid), true); + } else { + Log.e(TAG, "Cannot set active profile to: " + profileUuid.toString() + " - does not exist."); + return false; + } + } + + private boolean setActiveProfile(UUID profileUuid, boolean doinit) throws RemoteException { + if(mProfiles.containsKey(profileUuid)){ + if (LOCAL_LOGV) Log.v(TAG, "setActiveProfile(UUID, boolean) found profile UUID in mProfiles."); + return setActiveProfile(mProfiles.get(profileUuid), doinit); + } else { + Log.e(TAG, "Cannot set active profile to: " + profileUuid.toString() + " - does not exist."); + return false; + } + } + + private boolean setActiveProfile(Profile newActiveProfile, boolean doinit) throws RemoteException { + /* + * NOTE: Since this is not a public function, and all public functions + * take either a string or a UUID, the active profile should always be + * in the collection. If writing another setActiveProfile which receives + * a Profile object, run enforceChangePermissions, add the profile to the + * list, and THEN add it. + */ + + try { + enforceChangePermissions(); + Log.d(TAG, "Set active profile to: " + newActiveProfile.getUuid().toString() + " - " + newActiveProfile.getName()); + Profile lastProfile = mActiveProfile; + mActiveProfile = newActiveProfile; + mDirty = true; + if (doinit) { + if (LOCAL_LOGV) Log.v(TAG, "setActiveProfile(Profile, boolean) - Running init"); + + /* + * We need to clear the caller's identity in order to + * - allow the profile switch to execute actions not included in the caller's permissions + * - broadcast INTENT_ACTION_PROFILE_SELECTED + */ + long token = clearCallingIdentity(); + + // Call profile's "doSelect" + mActiveProfile.doSelect(mContext); + + // Notify other applications of newly selected profile. + Intent broadcast = new Intent(INTENT_ACTION_PROFILE_SELECTED); + broadcast.putExtra("name", mActiveProfile.getName()); + broadcast.putExtra("uuid", mActiveProfile.getUuid().toString()); + broadcast.putExtra("lastName", lastProfile.getName()); + broadcast.putExtra("lastUuid", lastProfile.getUuid().toString()); + mContext.sendBroadcast(broadcast); + + restoreCallingIdentity(token); + persistIfDirty(); + } + return true; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + + @Override + public boolean addProfile(Profile profile) throws RemoteException, SecurityException { + enforceChangePermissions(); + addProfileInternal(profile); + persistIfDirty(); + return true; + } + + private void addProfileInternal(Profile profile) { + // Make sure this profile has all of the correct groups. + for (NotificationGroup group : mGroups.values()) { + ensureGroupInProfile(profile, group, false); + } + ensureGroupInProfile(profile, mWildcardGroup, true); + mProfiles.put(profile.getUuid(), profile); + mProfileNames.put(profile.getName(), profile.getUuid()); + mDirty = true; + } + + private void ensureGroupInProfile(Profile profile, NotificationGroup group, boolean defaultGroup) { + if (profile.getProfileGroup(group.getUuid()) != null) { + return; + } + + /* enforce a matchup between profile and notification group, which not only + * works by UUID, but also by name for backwards compatibility */ + for (ProfileGroup pg : profile.getProfileGroups()) { + if (pg.matches(group, defaultGroup)) { + return; + } + } + + /* didn't find any, create new group */ + profile.addProfileGroup(new ProfileGroup(group.getUuid(), defaultGroup)); + } + + @Override + @Deprecated + public Profile getProfileByName(String profileName) throws RemoteException { + if (mProfileNames.containsKey(profileName)) { + return mProfiles.get(mProfileNames.get(profileName)); + } else if (mProfiles.containsKey(UUID.fromString((profileName)))) { + return mProfiles.get(UUID.fromString(profileName)); + } else { + return null; + } + } + + @Override + public Profile getProfile(ParcelUuid profileParcelUuid) { + UUID profileUuid = profileParcelUuid.getUuid(); + return getProfile(profileUuid); + } + + public Profile getProfile(UUID profileUuid) { + // use primary UUID first + if (mProfiles.containsKey(profileUuid)) { + return mProfiles.get(profileUuid); + } + // if no match was found: try secondary UUID + for (Profile p : mProfiles.values()) { + for (UUID uuid : p.getSecondaryUuids()) { + if (profileUuid.equals(uuid)) { + return p; + } + } + } + // nothing found + return null; + } + + @Override + public Profile[] getProfiles() throws RemoteException { + Profile[] tmpArr = mProfiles.values().toArray(new Profile[mProfiles.size()]); + Arrays.sort(tmpArr); + return tmpArr; + } + + @Override + public Profile getActiveProfile() throws RemoteException { + return mActiveProfile; + } + + @Override + public boolean removeProfile(Profile profile) throws RemoteException, SecurityException { + enforceChangePermissions(); + if (mProfileNames.remove(profile.getName()) != null && mProfiles.remove(profile.getUuid()) != null) { + mDirty = true; + persistIfDirty(); + return true; + } else{ + return false; + } + } + + @Override + public void updateProfile(Profile profile) throws RemoteException, SecurityException { + enforceChangePermissions(); + Profile old = mProfiles.get(profile.getUuid()); + if (old != null) { + mProfileNames.remove(old.getName()); + mProfileNames.put(profile.getName(), profile.getUuid()); + mProfiles.put(profile.getUuid(), profile); + /* no need to set mDirty, if the profile was actually changed, + * it's marked as dirty by itself */ + persistIfDirty(); + + // Also update we changed the active profile + if (mActiveProfile != null && mActiveProfile.getUuid().equals(profile.getUuid())) { + setActiveProfile(profile, true); + } + } + } + + @Override + public boolean profileExists(ParcelUuid profileUuid) throws RemoteException { + return mProfiles.containsKey(profileUuid.getUuid()); + } + + @Override + public boolean profileExistsByName(String profileName) throws RemoteException { + for (Map.Entry<String, UUID> entry : mProfileNames.entrySet()) { + if (entry.getKey().equalsIgnoreCase(profileName)) { + return true; + } + } + return false; + } + + @Override + public boolean notificationGroupExistsByName(String notificationGroupName) throws RemoteException { + for (NotificationGroup group : mGroups.values()) { + if (group.getName().equalsIgnoreCase(notificationGroupName)) { + return true; + } + } + return false; + } + + @Override + public NotificationGroup[] getNotificationGroups() throws RemoteException { + return mGroups.values().toArray(new NotificationGroup[mGroups.size()]); + } + + @Override + public void addNotificationGroup(NotificationGroup group) throws RemoteException, SecurityException { + enforceChangePermissions(); + addNotificationGroupInternal(group); + persistIfDirty(); + } + + private void addNotificationGroupInternal(NotificationGroup group) { + if (mGroups.put(group.getUuid(), group) == null) { + // If the above is true, then the ProfileGroup shouldn't exist in + // the profile. Ensure it is added. + for (Profile profile : mProfiles.values()) { + ensureGroupInProfile(profile, group, false); + } + } + mDirty = true; + } + + @Override + public void removeNotificationGroup(NotificationGroup group) throws RemoteException, SecurityException { + enforceChangePermissions(); + mDirty |= (mGroups.remove(group.getUuid()) != null); + // Remove the corresponding ProfileGroup from all the profiles too if + // they use it. + for (Profile profile : mProfiles.values()) { + profile.removeProfileGroup(group.getUuid()); + } + persistIfDirty(); + } + + @Override + public void updateNotificationGroup(NotificationGroup group) throws RemoteException, SecurityException { + enforceChangePermissions(); + NotificationGroup old = mGroups.get(group.getUuid()); + if (old != null) { + mGroups.put(group.getUuid(), group); + /* no need to set mDirty, if the group was actually changed, + * it's marked as dirty by itself */ + persistIfDirty(); + } + } + + @Override + public NotificationGroup getNotificationGroupForPackage(String pkg) throws RemoteException { + for (NotificationGroup group : mGroups.values()) { + if (group.hasPackage(pkg)) { + return group; + } + } + return null; + } + + private void loadFromFile() throws RemoteException, XmlPullParserException, IOException { + XmlPullParserFactory xppf = XmlPullParserFactory.newInstance(); + XmlPullParser xpp = xppf.newPullParser(); + FileReader fr = new FileReader(PROFILE_FILENAME); + xpp.setInput(fr); + loadXml(xpp, mContext); + fr.close(); + persistIfDirty(); + } + + private void loadXml(XmlPullParser xpp, Context context) throws + XmlPullParserException, IOException, RemoteException { + int event = xpp.next(); + String active = null; + while (event != XmlPullParser.END_TAG || !"profiles".equals(xpp.getName())) { + if (event == XmlPullParser.START_TAG) { + String name = xpp.getName(); + if (name.equals("active")) { + active = xpp.nextText(); + Log.d(TAG, "Found active: " + active); + } else if (name.equals("profile")) { + Profile prof = Profile.fromXml(xpp, context); + addProfileInternal(prof); + // Failsafe if no active found + if (active == null) { + active = prof.getUuid().toString(); + } + } else if (name.equals("notificationGroup")) { + NotificationGroup ng = NotificationGroup.fromXml(xpp, context); + addNotificationGroupInternal(ng); + } + } else if (event == XmlPullParser.END_DOCUMENT) { + throw new IOException("Premature end of file while reading " + PROFILE_FILENAME); + } + event = xpp.next(); + } + // Don't do initialisation on startup. The AudioManager doesn't exist yet + // and besides, the volume settings will have survived the reboot. + try { + // Try / catch block to detect if XML file needs to be upgraded. + setActiveProfile(UUID.fromString(active), false); + } catch (IllegalArgumentException e) { + if (mProfileNames.containsKey(active)) { + setActiveProfile(mProfileNames.get(active), false); + } else { + // Final fail-safe: We must have SOME profile active. + // If we couldn't select one by now, we'll pick the first in the set. + setActiveProfile(mProfiles.values().iterator().next(), false); + } + // This is a hint that we probably just upgraded the XML file. Save changes. + mDirty = true; + } + } + + private void initialiseStructure() throws RemoteException, XmlPullParserException, IOException { + XmlResourceParser xml = mContext.getResources().getXml( + com.android.internal.R.xml.profile_default); + try { + loadXml(xml, mContext); + mDirty = true; + persistIfDirty(); + } finally { + xml.close(); + } + } + + private String getXmlString() throws RemoteException { + StringBuilder builder = new StringBuilder(); + builder.append("<profiles>\n<active>"); + builder.append(TextUtils.htmlEncode(getActiveProfile().getUuid().toString())); + builder.append("</active>\n"); + + for (Profile p : mProfiles.values()) { + p.getXmlString(builder, mContext); + } + for (NotificationGroup g : mGroups.values()) { + g.getXmlString(builder, mContext); + } + builder.append("</profiles>\n"); + return builder.toString(); + } + + @Override + public NotificationGroup getNotificationGroup(ParcelUuid uuid) throws RemoteException { + if (uuid.getUuid().equals(mWildcardGroup.getUuid())) { + return mWildcardGroup; + } + return mGroups.get(uuid.getUuid()); + } + + private synchronized void persistIfDirty() { + boolean dirty = mDirty; + if (!dirty) { + for (Profile profile : mProfiles.values()) { + if (profile.isDirty()) { + dirty = true; + break; + } + } + } + if (!dirty) { + for (NotificationGroup group : mGroups.values()) { + if (group.isDirty()) { + dirty = true; + break; + } + } + } + if (dirty) { + try { + Log.d(TAG, "Saving profile data..."); + FileWriter fw = new FileWriter(PROFILE_FILENAME); + fw.write(getXmlString()); + fw.close(); + Log.d(TAG, "Save completed."); + mDirty = false; + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + private void enforceChangePermissions() throws SecurityException { + mContext.enforceCallingOrSelfPermission(PERMISSION_CHANGE_SETTINGS, + "You do not have permissions to change the Profile Manager."); + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 894c4d0..3dd22b0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -346,6 +346,7 @@ class ServerThread extends Thread { StatusBarManagerService statusBar = null; InputMethodManagerService imm = null; AppWidgetService appWidget = null; + ProfileManagerService profile = null; NotificationManagerService notification = null; WallpaperManagerService wallpaper = null; LocationManagerService location = null; @@ -553,6 +554,14 @@ class ServerThread extends Thread { } try { + Slog.i(TAG, "Profile Manager"); + profile = new ProfileManagerService(context); + ServiceManager.addService(Context.PROFILE_SERVICE, profile); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting Profile Manager", e); + } + + try { Slog.i(TAG, "Notification Manager"); notification = new NotificationManagerService(context, statusBar, lights); ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java index df91dec..64cae1d 100755 --- a/services/java/com/android/server/VibratorService.java +++ b/services/java/com/android/server/VibratorService.java @@ -39,6 +39,7 @@ import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import android.view.InputDevice; +import java.util.Calendar; import java.util.ArrayList; import java.util.LinkedList; import java.util.ListIterator; @@ -164,6 +165,29 @@ public class VibratorService extends IVibratorService.Stub return doVibratorExists(); } + private boolean inQuietHours() { + boolean quietHoursEnabled = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.QUIET_HOURS_ENABLED, 0) != 0; + int quietHoursStart = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.QUIET_HOURS_START, 0); + int quietHoursEnd = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.QUIET_HOURS_END, 0); + boolean quietHoursHaptic = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.QUIET_HOURS_HAPTIC, 0) != 0; + if (quietHoursEnabled && quietHoursHaptic && (quietHoursStart != quietHoursEnd)) { + // Get the date in "quiet hours" format. + Calendar calendar = Calendar.getInstance(); + int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE); + if (quietHoursEnd < quietHoursStart) { + // Starts at night, ends in the morning. + return (minutes > quietHoursStart) || (minutes < quietHoursEnd); + } else { + return (minutes > quietHoursStart) && (minutes < quietHoursEnd); + } + } + return false; + } + public void vibrate(long milliseconds, IBinder token) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { @@ -174,7 +198,7 @@ public class VibratorService extends IVibratorService.Stub // timeout of 0 or negative. This will ensure that a vibration has // either a timeout of > 0 or a non-null pattern. if (milliseconds <= 0 || (mCurrentVibration != null - && mCurrentVibration.hasLongerTimeout(milliseconds))) { + && mCurrentVibration.hasLongerTimeout(milliseconds)) || inQuietHours()) { // Ignore this vibration since the current vibration will play for // longer than milliseconds. return; @@ -204,6 +228,9 @@ public class VibratorService extends IVibratorService.Stub != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); } + if (inQuietHours()) { + return; + } int uid = Binder.getCallingUid(); // so wakelock calls will succeed long identity = Binder.clearCallingIdentity(); diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java index c7f7390..1aa1fb0 100644 --- a/services/java/com/android/server/power/ShutdownThread.java +++ b/services/java/com/android/server/power/ShutdownThread.java @@ -47,6 +47,7 @@ import com.android.internal.telephony.ITelephony; import android.util.Log; import android.view.WindowManager; +import android.view.KeyEvent; public final class ShutdownThread extends Thread { // constants @@ -129,22 +130,63 @@ public final class ShutdownThread extends Thread { if (sConfirmDialog != null) { sConfirmDialog.dismiss(); } - sConfirmDialog = new AlertDialog.Builder(context) - .setTitle(mRebootSafeMode - ? com.android.internal.R.string.reboot_safemode_title - : com.android.internal.R.string.power_off) - .setMessage(resourceId) - .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - beginShutdownSequence(context); - } - }) - .setNegativeButton(com.android.internal.R.string.no, null) - .create(); + if (mReboot && !mRebootSafeMode){ + sConfirmDialog = new AlertDialog.Builder(context) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(com.android.internal.R.string.reboot_system) + .setSingleChoiceItems(com.android.internal.R.array.shutdown_reboot_options, 0, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (which < 0) + return; + + String actions[] = context.getResources().getStringArray(com.android.internal.R.array.shutdown_reboot_actions); + + if (actions != null && which < actions.length) + mRebootReason = actions[which]; + } + }) + .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mReboot = true; + beginShutdownSequence(context); + } + }) + .setNegativeButton(com.android.internal.R.string.no, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mReboot = false; + dialog.cancel(); + } + }) + .create(); + sConfirmDialog.setOnKeyListener(new DialogInterface.OnKeyListener() { + public boolean onKey (DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + mReboot = false; + dialog.cancel(); + } + return true; + } + }); + } else { + sConfirmDialog = new AlertDialog.Builder(context) + .setTitle(mRebootSafeMode + ? com.android.internal.R.string.reboot_safemode_title + : com.android.internal.R.string.power_off) + .setMessage(resourceId) + .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + beginShutdownSequence(context); + } + }) + .setNegativeButton(com.android.internal.R.string.no, null) + .create(); + } + closer.dialog = sConfirmDialog; sConfirmDialog.setOnDismissListener(closer); sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); sConfirmDialog.show(); + } else { beginShutdownSequence(context); } @@ -213,8 +255,13 @@ public final class ShutdownThread extends Thread { // throw up an indeterminate system dialog to indicate radio is // shutting down. ProgressDialog pd = new ProgressDialog(context); - pd.setTitle(context.getText(com.android.internal.R.string.power_off)); - pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); + if (mReboot) { + pd.setTitle(context.getText(com.android.internal.R.string.reboot_system)); + pd.setMessage(context.getText(com.android.internal.R.string.reboot_progress)); + } else { + pd.setTitle(context.getText(com.android.internal.R.string.power_off)); + pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); + } pd.setIndeterminate(true); pd.setCancelable(false); pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); @@ -294,7 +341,7 @@ public final class ShutdownThread extends Thread { } Log.i(TAG, "Sending shutdown broadcast..."); - + // First send the high-level shut down broadcast. mActionDone = false; mContext.sendOrderedBroadcastAsUser(new Intent(Intent.ACTION_SHUTDOWN), @@ -314,9 +361,9 @@ public final class ShutdownThread extends Thread { } } } - + Log.i(TAG, "Shutting down activity manager..."); - + final IActivityManager am = ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); if (am != null) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 51edb44..38233d0 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5430,6 +5430,12 @@ public class WindowManagerService extends IWindowManager.Stub mInputManager.setInputFilter(filter); } + // Called by window manager policy. Not exposed externally. + @Override + public void reboot() { + ShutdownThread.reboot(mContext, null, true); + } + public void setCurrentUser(final int newUserId) { synchronized (mWindowMap) { mCurrentUserId = newUserId; |