| /* |
| * Copyright (C) 2015 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 lineageos.app; |
| |
| import android.content.Context; |
| import android.media.AudioManager; |
| import android.os.Parcel; |
| import android.os.ParcelUuid; |
| import android.os.Parcelable; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import com.android.internal.policy.IKeyguardService; |
| import lineageos.os.Build; |
| import lineageos.profiles.AirplaneModeSettings; |
| import lineageos.profiles.BrightnessSettings; |
| import lineageos.profiles.ConnectionSettings; |
| import lineageos.profiles.LockSettings; |
| import lineageos.profiles.RingModeSettings; |
| import lineageos.profiles.StreamSettings; |
| |
| import lineageos.os.Concierge; |
| import lineageos.os.Concierge.ParcelInfo; |
| |
| 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.Map.Entry; |
| import java.util.UUID; |
| |
| /** |
| * A class that represents a device profile. |
| * |
| * A {@link Profile} can serve a multitude of purposes, allowing the creator(user) |
| * to set overrides for streams, triggers, screen lock, brightness, various other |
| * settings. |
| */ |
| 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 Map<Integer, StreamSettings> streams = new HashMap<Integer, StreamSettings>(); |
| |
| private Map<String, ProfileTrigger> mTriggers = new HashMap<String, ProfileTrigger>(); |
| |
| private Map<Integer, ConnectionSettings> connections = new HashMap<Integer, ConnectionSettings>(); |
| |
| private Map<Integer, ConnectionSettings> networkConnectionSubIds = new HashMap<>(); |
| |
| private RingModeSettings mRingMode = new RingModeSettings(); |
| |
| private AirplaneModeSettings mAirplaneMode = new AirplaneModeSettings(); |
| |
| private BrightnessSettings mBrightness = new BrightnessSettings(); |
| |
| private LockSettings mScreenLockMode = new LockSettings(); |
| |
| private int mDozeMode = DozeMode.DEFAULT; |
| |
| private int mNotificationLightMode = NotificationLightMode.DEFAULT; |
| |
| /** |
| * Lock modes of a device |
| */ |
| public static class LockMode { |
| /** Represents a default state lock mode (user choice) */ |
| public static final int DEFAULT = 0; |
| /** Represents an insecure state lock mode, where the device has no security screen */ |
| public static final int INSECURE = 1; |
| /** Represents a disabled state lock mode, where the devices lock screen can be removed */ |
| public static final int DISABLE = 2; |
| } |
| |
| /** |
| * Doze modes available on a device |
| */ |
| public static class DozeMode { |
| /** Represents a default Doze mode (user choice) */ |
| public static final int DEFAULT = 0; |
| /** Represents an enabled Doze mode */ |
| public static final int ENABLE = 1; |
| /** Represents an disabled Doze mode */ |
| public static final int DISABLE = 2; |
| } |
| |
| /** |
| * Notification light modes available on a device |
| */ |
| public static class NotificationLightMode { |
| /** Represents a default Notification light mode (user choice) */ |
| public static final int DEFAULT = 0; |
| /** Represents an enabled Notification light mode */ |
| public static final int ENABLE = 1; |
| /** Represents a disabled Notification light mode */ |
| public static final int DISABLE = 2; |
| } |
| |
| /** |
| * Available trigger types on the device, usually hardware |
| */ |
| public static class TriggerType { |
| /** Represents a WiFi trigger type */ |
| public static final int WIFI = 0; |
| /** Represents a Bluetooth trigger type */ |
| public static final int BLUETOOTH = 1; |
| } |
| |
| /** |
| * Various trigger states associated with a {@link TriggerType} |
| */ |
| public static class TriggerState { |
| /** A {@link TriggerState) for when the {@link TriggerType} connects */ |
| public static final int ON_CONNECT = 0; |
| /** A {@link TriggerState) for when the {@link TriggerType} disconnects */ |
| public static final int ON_DISCONNECT = 1; |
| /** A {@link TriggerState) for when the {@link TriggerType} is disabled */ |
| public static final int DISABLED = 2; |
| /** |
| * A {@link TriggerState) for when the {@link TriggerType#BLUETOOTH} |
| * connects for A2DP session |
| */ |
| public static final int ON_A2DP_CONNECT = 3; |
| /** |
| * A {@link TriggerState) for when the {@link TriggerType#BLUETOOTH} |
| * disconnects from A2DP session |
| */ |
| public static final int ON_A2DP_DISCONNECT = 4; |
| } |
| |
| /** |
| * A {@link Profile} type |
| */ |
| public static class Type { |
| /** Profile type which represents a toggle {@link Profile} */ |
| public static final int TOGGLE = 0; |
| /** Profile type which represents a conditional {@link Profile} */ |
| public static final int CONDITIONAL = 1; |
| } |
| |
| /** |
| * A {@link ProfileTrigger} is a {@link TriggerType} which can be queried from the OS |
| */ |
| public static class ProfileTrigger implements Parcelable { |
| private int mType; |
| private String mId; |
| private String mName; |
| private int mState; |
| |
| |
| /** |
| * Construct a {@link ProfileTrigger} based on its type {@link Profile.TriggerType} and if |
| * the trigger should fire on a {@link Profile.TriggerState} change. |
| * |
| * Example: |
| * <pre class="prettyprint"> |
| * triggerId = trigger.getSSID(); // Use the AP's SSID as identifier |
| * triggerName = trigger.getTitle(); // Use the AP's name as the trigger name |
| * triggerType = Profile.TriggerType.WIFI; // This is a wifi trigger |
| * triggerState = Profile.TriggerState.ON_CONNECT; // On Connect of this, trigger |
| * |
| * Profile.ProfileTrigger profileTrigger = |
| * new Profile.ProfileTrigger(triggerType, triggerId, triggerState, triggerName); |
| * </pre> |
| * |
| * @param type a {@link Profile.TriggerType} |
| * @param id an identifier for the ProfileTrigger |
| * @param state {@link Profile.TriggerState} depending on the TriggerType |
| * @param name an identifying name for the ProfileTrigger |
| */ |
| public ProfileTrigger(int type, String id, int state, String name) { |
| mType = type; |
| mId = id; |
| mState = state; |
| mName = name; |
| } |
| |
| private ProfileTrigger(Parcel in) { |
| // Read parcelable version via the Concierge |
| ParcelInfo parcelInfo = Concierge.receiveParcel(in); |
| int parcelableVersion = parcelInfo.getParcelVersion(); |
| |
| // Pattern here is that all new members should be added to the end of |
| // the writeToParcel method. Then we step through each version, until the latest |
| // API release to help unravel this parcel |
| if (parcelableVersion >= Build.LINEAGE_VERSION_CODES.BOYSENBERRY) { |
| mType = in.readInt(); |
| mId = in.readString(); |
| mState = in.readInt(); |
| mName = in.readString(); |
| } |
| |
| // Complete parcel info for the concierge |
| parcelInfo.complete(); |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| // Tell the concierge to prepare the parcel |
| ParcelInfo parcelInfo = Concierge.prepareParcel(dest); |
| |
| dest.writeInt(mType); |
| dest.writeString(mId); |
| dest.writeInt(mState); |
| dest.writeString(mName); |
| |
| // Complete the parcel info for the concierge |
| parcelInfo.complete(); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * Get the {@link ProfileTrigger} {@link TriggerType} |
| * @return {@link TriggerType} |
| */ |
| public int getType() { |
| return mType; |
| } |
| |
| /** |
| * Get the name associated with the {@link ProfileTrigger} |
| * @return a string name |
| */ |
| public String getName() { |
| return mName; |
| } |
| |
| /** |
| * Get the id associated with the {@link ProfileTrigger} |
| * @return an string identifier |
| */ |
| public String getId() { |
| return mId; |
| } |
| |
| /** |
| * Get the state associated with the {@link ProfileTrigger} |
| * @return an integer indicating the state |
| */ |
| public int getState() { |
| return mState; |
| } |
| |
| /** |
| * @hide |
| */ |
| public void getXmlString(StringBuilder builder, Context context) { |
| final String itemType = mType == TriggerType.WIFI ? "wifiAP" : "btDevice"; |
| |
| builder.append("<"); |
| builder.append(itemType); |
| builder.append(" "); |
| builder.append(getIdType(mType)); |
| builder.append("=\""); |
| builder.append(mId); |
| builder.append("\" state=\""); |
| builder.append(mState); |
| builder.append("\" name=\""); |
| builder.append(mName); |
| builder.append("\"></"); |
| builder.append(itemType); |
| builder.append(">\n"); |
| } |
| |
| /** |
| * @hide |
| */ |
| public static ProfileTrigger fromXml(XmlPullParser xpp, Context context) { |
| final String name = xpp.getName(); |
| final int type; |
| |
| if (name.equals("wifiAP")) { |
| type = TriggerType.WIFI; |
| } else if (name.equals("btDevice")) { |
| type = TriggerType.BLUETOOTH; |
| } else { |
| return null; |
| } |
| |
| String id = xpp.getAttributeValue(null, getIdType(type)); |
| int state = Integer.valueOf(xpp.getAttributeValue(null, "state")); |
| String triggerName = xpp.getAttributeValue(null, "name"); |
| if (triggerName == null) { |
| triggerName = id; |
| } |
| |
| return new ProfileTrigger(type, id, state, triggerName); |
| } |
| |
| private static String getIdType(int type) { |
| return type == TriggerType.WIFI ? "ssid" : "address"; |
| } |
| |
| /** |
| * @hide |
| */ |
| public static final Parcelable.Creator<ProfileTrigger> CREATOR |
| = new Parcelable.Creator<ProfileTrigger>() { |
| public ProfileTrigger createFromParcel(Parcel in) { |
| return new ProfileTrigger(in); |
| } |
| |
| @Override |
| public ProfileTrigger[] newArray(int size) { |
| return new ProfileTrigger[size]; |
| } |
| }; |
| } |
| |
| /** @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]; |
| } |
| }; |
| |
| public Profile(String name) { |
| this(name, -1, UUID.randomUUID()); |
| } |
| |
| /** @hide */ |
| public Profile(String name, int nameResId, UUID uuid) { |
| mName = name; |
| mNameResId = nameResId; |
| mUuid = uuid; |
| mProfileType = Type.TOGGLE; //Default to toggle type |
| mDirty = false; |
| } |
| |
| private Profile(Parcel in) { |
| readFromParcel(in); |
| } |
| |
| /** |
| * Get the {@link TriggerState} for a {@link ProfileTrigger} with a given id |
| * @param type {@link TriggerType} |
| * @param id string id of {@link ProfileTrigger} |
| * @return {@link TriggerState} |
| */ |
| public int getTriggerState(int type, String id) { |
| ProfileTrigger trigger = id != null ? mTriggers.get(id) : null; |
| if (trigger != null) { |
| return trigger.mState; |
| } |
| return TriggerState.DISABLED; |
| } |
| |
| /** |
| * Get all the {@link ProfileTrigger}s for a given {@link TriggerType} |
| * @param type {@link TriggerType} |
| * @return an array list of {@link ProfileTrigger}s |
| */ |
| public ArrayList<ProfileTrigger> getTriggersFromType(int type) { |
| ArrayList<ProfileTrigger> result = new ArrayList<ProfileTrigger>(); |
| for (Entry<String, ProfileTrigger> profileTrigger: mTriggers.entrySet()) { |
| ProfileTrigger trigger = profileTrigger.getValue(); |
| if (trigger.getType() == type) { |
| result.add(trigger); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Set a custom {@link ProfileTrigger} |
| * @hide |
| */ |
| public void setTrigger(int type, String id, int state, String name) { |
| if (id == null |
| || type < TriggerType.WIFI || type > TriggerType.BLUETOOTH |
| || state < TriggerState.ON_CONNECT || state > TriggerState.ON_A2DP_DISCONNECT) { |
| return; |
| } |
| |
| ProfileTrigger trigger = mTriggers.get(id); |
| |
| if (state == TriggerState.DISABLED) { |
| if (trigger != null) { |
| mTriggers.remove(id); |
| } |
| } else if (trigger != null) { |
| trigger.mState = state; |
| } else { |
| mTriggers.put(id, new ProfileTrigger(type, id, state, name)); |
| } |
| |
| mDirty = true; |
| } |
| |
| /** |
| * Set a {@link ProfileTrigger} on the {@link Profile} |
| * @param trigger a {@link ProfileTrigger} |
| */ |
| public void setTrigger(ProfileTrigger trigger) { |
| setTrigger(trigger.getType(), trigger.getId(), trigger.getState(), trigger.getName()); |
| } |
| |
| 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; |
| } |
| |
| /** |
| * Add a {@link ProfileGroup} to the {@link Profile} |
| * @param profileGroup |
| * @hide |
| */ |
| public void addProfileGroup(ProfileGroup profileGroup) { |
| if (profileGroup == null) { |
| return; |
| } |
| |
| if (profileGroup.isDefaultGroup()) { |
| /* we must not have more than one default group */ |
| if (mDefaultGroup != null) { |
| return; |
| } |
| mDefaultGroup = profileGroup; |
| } |
| profileGroups.put(profileGroup.getUuid(), profileGroup); |
| mDirty = true; |
| } |
| |
| /** |
| * Remove a {@link ProfileGroup} with a given {@link UUID} |
| * @param uuid |
| * @hide |
| */ |
| public void removeProfileGroup(UUID uuid) { |
| if (!profileGroups.get(uuid).isDefaultGroup()) { |
| profileGroups.remove(uuid); |
| } else { |
| Log.e(TAG, "Cannot remove default group: " + uuid); |
| } |
| } |
| |
| /** |
| * Get {@link ProfileGroup}s associated with the {@link Profile} |
| * @return {@link ProfileGroup[]} |
| * @hide |
| */ |
| public ProfileGroup[] getProfileGroups() { |
| return profileGroups.values().toArray(new ProfileGroup[0]); |
| } |
| |
| /** |
| * Get a {@link ProfileGroup} with a given {@link UUID} |
| * @param uuid |
| * @return a {@link ProfileGroup} |
| * @hide |
| */ |
| public ProfileGroup getProfileGroup(UUID uuid) { |
| return profileGroups.get(uuid); |
| } |
| |
| /** |
| * Get the default {@link ProfileGroup} associated with the {@link Profile} |
| * @return the default {@link ProfileGroup} |
| * @hide |
| */ |
| public ProfileGroup getDefaultGroup() { |
| return mDefaultGroup; |
| } |
| |
| /** @hide */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** @hide */ |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| // Tell the concierge to prepare the parcel |
| ParcelInfo parcelInfo = Concierge.prepareParcel(dest); |
| |
| // === BOYSENBERRY === |
| if (!TextUtils.isEmpty(mName)) { |
| dest.writeInt(1); |
| dest.writeString(mName); |
| } else { |
| dest.writeInt(0); |
| } |
| if (mNameResId != 0) { |
| dest.writeInt(1); |
| dest.writeInt(mNameResId); |
| } else { |
| dest.writeInt(0); |
| } |
| if (mUuid != null) { |
| dest.writeInt(1); |
| new ParcelUuid(mUuid).writeToParcel(dest, 0); |
| } else { |
| dest.writeInt(0); |
| } |
| if (mSecondaryUuids != null && !mSecondaryUuids.isEmpty()) { |
| ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(mSecondaryUuids.size()); |
| for (UUID u : mSecondaryUuids) { |
| uuids.add(new ParcelUuid(u)); |
| } |
| dest.writeInt(1); |
| dest.writeParcelableArray(uuids.toArray(new Parcelable[0]), flags); |
| } else { |
| dest.writeInt(0); |
| } |
| dest.writeInt(mStatusBarIndicator ? 1 : 0); |
| dest.writeInt(mProfileType); |
| dest.writeInt(mDirty ? 1 : 0); |
| if (profileGroups != null && !profileGroups.isEmpty()) { |
| dest.writeInt(1); |
| dest.writeTypedArray(profileGroups.values().toArray( |
| new ProfileGroup[0]), flags); |
| } else { |
| dest.writeInt(0); |
| } |
| if (streams != null && !streams.isEmpty()) { |
| dest.writeInt(1); |
| dest.writeTypedArray(streams.values().toArray( |
| new StreamSettings[0]), flags); |
| } else { |
| dest.writeInt(0); |
| } |
| if (connections != null && !connections.isEmpty()) { |
| dest.writeInt(1); |
| dest.writeTypedArray(connections.values().toArray( |
| new ConnectionSettings[0]), flags); |
| } else { |
| dest.writeInt(0); |
| } |
| if (mRingMode != null) { |
| dest.writeInt(1); |
| mRingMode.writeToParcel(dest, 0); |
| } else { |
| dest.writeInt(0); |
| } |
| if (mAirplaneMode != null) { |
| dest.writeInt(1); |
| mAirplaneMode.writeToParcel(dest, 0); |
| } else { |
| dest.writeInt(0); |
| } |
| if (mBrightness != null) { |
| dest.writeInt(1); |
| mBrightness.writeToParcel(dest, 0); |
| } else { |
| dest.writeInt(0); |
| } |
| if (mScreenLockMode != null) { |
| dest.writeInt(1); |
| mScreenLockMode.writeToParcel(dest, 0); |
| } else { |
| dest.writeInt(0); |
| } |
| dest.writeTypedArray(mTriggers.values().toArray(new ProfileTrigger[0]), flags); |
| dest.writeInt(mDozeMode); |
| |
| // === ELDERBERRY === |
| dest.writeInt(mNotificationLightMode); |
| |
| if (networkConnectionSubIds != null && !networkConnectionSubIds.isEmpty()) { |
| dest.writeInt(1); |
| dest.writeTypedArray(networkConnectionSubIds.values().toArray( |
| new ConnectionSettings[0]), flags); |
| } else { |
| dest.writeInt(0); |
| } |
| |
| // Complete the parcel info for the concierge |
| parcelInfo.complete(); |
| } |
| |
| /** @hide */ |
| public void readFromParcel(Parcel in) { |
| // Read parcelable version via the Concierge |
| ParcelInfo parcelInfo = Concierge.receiveParcel(in); |
| int parcelableVersion = parcelInfo.getParcelVersion(); |
| |
| // Pattern here is that all new members should be added to the end of |
| // the writeToParcel method. Then we step through each version, until the latest |
| // API release to help unravel this parcel |
| if (parcelableVersion >= Build.LINEAGE_VERSION_CODES.BOYSENBERRY) { |
| if (in.readInt() != 0) { |
| mName = in.readString(); |
| } |
| if (in.readInt() != 0) { |
| mNameResId = in.readInt(); |
| } |
| if (in.readInt() != 0) { |
| mUuid = ParcelUuid.CREATOR.createFromParcel(in).getUuid(); |
| } |
| if (in.readInt() != 0) { |
| 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); |
| if (in.readInt() != 0) { |
| for (ProfileGroup group : in.createTypedArray(ProfileGroup.CREATOR)) { |
| profileGroups.put(group.getUuid(), group); |
| if (group.isDefaultGroup()) { |
| mDefaultGroup = group; |
| } |
| } |
| } |
| if (in.readInt() != 0) { |
| for (StreamSettings stream : in.createTypedArray(StreamSettings.CREATOR)) { |
| streams.put(stream.getStreamId(), stream); |
| } |
| } |
| if (in.readInt() != 0) { |
| for (ConnectionSettings connection : |
| in.createTypedArray(ConnectionSettings.CREATOR)) { |
| connections.put(connection.getConnectionId(), connection); |
| } |
| } |
| if (in.readInt() != 0) { |
| mRingMode = RingModeSettings.CREATOR.createFromParcel(in); |
| } |
| if (in.readInt() != 0) { |
| mAirplaneMode = AirplaneModeSettings.CREATOR.createFromParcel(in); |
| } |
| if (in.readInt() != 0) { |
| mBrightness = BrightnessSettings.CREATOR.createFromParcel(in); |
| } |
| if (in.readInt() != 0) { |
| mScreenLockMode = LockSettings.CREATOR.createFromParcel(in); |
| } |
| for (ProfileTrigger trigger : in.createTypedArray(ProfileTrigger.CREATOR)) { |
| mTriggers.put(trigger.mId, trigger); |
| } |
| mDozeMode = in.readInt(); |
| } |
| if (parcelableVersion >= Build.LINEAGE_VERSION_CODES.ELDERBERRY) { |
| mNotificationLightMode = in.readInt(); |
| if (in.readInt() != 0) { |
| for (ConnectionSettings connection : |
| in.createTypedArray(ConnectionSettings.CREATOR)) { |
| // elderberry can do msim connections |
| networkConnectionSubIds.put(connection.getSubId(), connection); |
| } |
| } |
| } |
| |
| // Complete the parcel info for the concierge |
| parcelInfo.complete(); |
| } |
| |
| /** |
| * Get the name associated with the {@link Profile} |
| * @return a string name of the profile |
| */ |
| public String getName() { |
| return mName; |
| } |
| |
| /** |
| * Set a name for the {@link Profile} |
| * @param name a string for the {@link Profile} |
| */ |
| public void setName(String name) { |
| mName = name; |
| mNameResId = -1; |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link Type} of the {@link Profile} |
| * @return |
| */ |
| public int getProfileType() { |
| return mProfileType; |
| } |
| |
| /** |
| * Set the {@link Type} for the {@link Profile} |
| * @param type a type of profile |
| */ |
| public void setProfileType(int type) { |
| mProfileType = type; |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link UUID} associated with the {@link Profile} |
| * @return the uuid for the profile |
| */ |
| public UUID getUuid() { |
| if (this.mUuid == null) this.mUuid = UUID.randomUUID(); |
| return this.mUuid; |
| } |
| |
| /** |
| * Get the secondary {@link UUID}s for the {@link Profile} |
| * @return the secondary uuids for the Profile |
| */ |
| public UUID[] getSecondaryUuids() { |
| return mSecondaryUuids.toArray(new UUID[0]); |
| } |
| |
| /** |
| * Set a list of secondary {@link UUID}s for the {@link Profile} |
| * @param uuids |
| */ |
| public void setSecondaryUuids(List<UUID> uuids) { |
| mSecondaryUuids.clear(); |
| if (uuids != null) { |
| mSecondaryUuids.addAll(uuids); |
| mDirty = true; |
| } |
| } |
| |
| /** |
| * Add a secondary {@link UUID} to the {@link Profile} |
| * @param uuid |
| */ |
| public void addSecondaryUuid(UUID uuid) { |
| if (uuid != null) { |
| mSecondaryUuids.add(uuid); |
| mDirty = true; |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| public boolean getStatusBarIndicator() { |
| return mStatusBarIndicator; |
| } |
| |
| /** |
| * @hide |
| */ |
| public void setStatusBarIndicator(boolean newStatusBarIndicator) { |
| mStatusBarIndicator = newStatusBarIndicator; |
| mDirty = true; |
| } |
| |
| /** |
| * Check if the given {@link Profile} is a {@link Type#CONDITIONAL} |
| * @return true if conditional |
| */ |
| public boolean isConditionalType() { |
| return(mProfileType == Type.CONDITIONAL ? true : false); |
| } |
| |
| /** |
| * @hide |
| */ |
| public void setConditionalType() { |
| mProfileType = Type.CONDITIONAL; |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link RingModeSettings} for the {@link Profile} |
| * @return |
| */ |
| public RingModeSettings getRingMode() { |
| return mRingMode; |
| } |
| |
| /** |
| * Set the {@link RingModeSettings} for the {@link Profile} |
| * @param descriptor |
| */ |
| public void setRingMode(RingModeSettings descriptor) { |
| mRingMode = descriptor; |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link LockSettings} for the {@link Profile} |
| * @return |
| */ |
| public LockSettings getScreenLockMode() { |
| return mScreenLockMode; |
| } |
| |
| /** |
| * Set the {@link LockSettings} for the {@link Profile} |
| * @param screenLockMode |
| */ |
| public void setScreenLockMode(LockSettings screenLockMode) { |
| mScreenLockMode = screenLockMode; |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link DozeMode} associated with the {@link Profile} |
| * @return |
| */ |
| public int getDozeMode() { |
| return mDozeMode; |
| } |
| |
| /** |
| * Set the {@link DozeMode} associated with the {@link Profile} |
| * @return |
| */ |
| public void setDozeMode(int dozeMode) { |
| if (dozeMode < DozeMode.DEFAULT |
| || dozeMode > DozeMode.DISABLE) { |
| mDozeMode = DozeMode.DEFAULT; |
| } else { |
| mDozeMode = dozeMode; |
| } |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link NotificationLightMode} associated with the {@link Profile} |
| * @return |
| */ |
| public int getNotificationLightMode() { |
| return mNotificationLightMode; |
| } |
| |
| /** |
| * Set the {@link NotificationLightMode} associated with the {@link Profile} |
| * @return |
| */ |
| public void setNotificationLightMode(int notificationLightMode) { |
| if (notificationLightMode < NotificationLightMode.DEFAULT |
| || notificationLightMode > NotificationLightMode.DISABLE) { |
| mNotificationLightMode = NotificationLightMode.DEFAULT; |
| } else { |
| mNotificationLightMode = notificationLightMode; |
| } |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link AirplaneModeSettings} associated with the {@link Profile} |
| * @return |
| */ |
| public AirplaneModeSettings getAirplaneMode() { |
| return mAirplaneMode; |
| } |
| |
| /** |
| * Set the {@link AirplaneModeSettings} associated with the {@link Profile} |
| * @param descriptor |
| */ |
| public void setAirplaneMode(AirplaneModeSettings descriptor) { |
| mAirplaneMode = descriptor; |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link BrightnessSettings} associated with the {@link Profile} |
| * @return |
| */ |
| public BrightnessSettings getBrightness() { |
| return mBrightness; |
| } |
| |
| /** |
| * Set the {@link BrightnessSettings} associated with the {@link Profile} |
| * @return |
| */ |
| public void setBrightness(BrightnessSettings descriptor) { |
| mBrightness = descriptor; |
| 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 (ConnectionSettings conn : networkConnectionSubIds.values()) { |
| if (conn.isDirty()) { |
| return true; |
| } |
| } |
| if (mRingMode.isDirty()) { |
| return true; |
| } |
| if (mAirplaneMode.isDirty()) { |
| return true; |
| } |
| if (mBrightness.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() == Type.TOGGLE ? "toggle" : "conditional"); |
| builder.append("</profiletype>\n"); |
| |
| builder.append("<statusbar>"); |
| builder.append(getStatusBarIndicator() ? "yes" : "no"); |
| builder.append("</statusbar>\n"); |
| |
| if (mScreenLockMode != null) { |
| builder.append("<screen-lock-mode>"); |
| mScreenLockMode.writeXmlString(builder, context); |
| builder.append("</screen-lock-mode>\n"); |
| } |
| |
| builder.append("<doze-mode>"); |
| builder.append(mDozeMode); |
| builder.append("</doze-mode>\n"); |
| |
| builder.append("<notification-light-mode>"); |
| builder.append(mNotificationLightMode); |
| builder.append("</notification-light-mode>\n"); |
| |
| mAirplaneMode.getXmlString(builder, context); |
| |
| mBrightness.getXmlString(builder, context); |
| |
| mRingMode.getXmlString(builder, context); |
| |
| 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 (ConnectionSettings cs : networkConnectionSubIds.values()) { |
| cs.getXmlString(builder, context); |
| } |
| if (!mTriggers.isEmpty()) { |
| builder.append("<triggers>\n"); |
| for (ProfileTrigger trigger : mTriggers.values()) { |
| trigger.getXmlString(builder, context); |
| } |
| builder.append("</triggers>\n"); |
| } |
| |
| 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; |
| } |
| |
| private static void readTriggersFromXml(XmlPullParser xpp, Context context, Profile profile) |
| throws XmlPullParserException, IOException { |
| int event = xpp.next(); |
| while (event != XmlPullParser.END_TAG || !xpp.getName().equals("triggers")) { |
| if (event == XmlPullParser.START_TAG) { |
| ProfileTrigger trigger = ProfileTrigger.fromXml(xpp, context); |
| if (trigger != null) { |
| profile.mTriggers.put(trigger.mId, trigger); |
| } |
| } else if (event == XmlPullParser.END_DOCUMENT) { |
| throw new IOException("Premature end of file while parsing triggers"); |
| } |
| event = xpp.next(); |
| } |
| } |
| |
| /** @hide */ |
| public void validateRingtones(Context context) { |
| for (ProfileGroup pg : profileGroups.values()) { |
| pg.validateOverrideUris(context); |
| } |
| } |
| |
| /** @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", |
| "lineageos.platform"); |
| 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") |
| ? Type.TOGGLE : Type.CONDITIONAL); |
| } |
| if (name.equals("ringModeDescriptor")) { |
| RingModeSettings smd = RingModeSettings.fromXml(xpp, context); |
| profile.setRingMode(smd); |
| } |
| if (name.equals("airplaneModeDescriptor")) { |
| AirplaneModeSettings amd = AirplaneModeSettings.fromXml(xpp, context); |
| profile.setAirplaneMode(amd); |
| } |
| if (name.equals("brightnessDescriptor")) { |
| BrightnessSettings bd = BrightnessSettings.fromXml(xpp, context); |
| profile.setBrightness(bd); |
| } |
| if (name.equals("screen-lock-mode")) { |
| LockSettings lockMode = new LockSettings(Integer.valueOf(xpp.nextText())); |
| profile.setScreenLockMode(lockMode); |
| } |
| if (name.equals("doze-mode")) { |
| profile.setDozeMode(Integer.valueOf(xpp.nextText())); |
| } |
| if (name.equals("notification-light-mode")) { |
| profile.setNotificationLightMode(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); |
| if (Build.LINEAGE_VERSION.SDK_INT >= Build.LINEAGE_VERSION_CODES.ELDERBERRY |
| && cs.getConnectionId() == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) { |
| profile.networkConnectionSubIds.put(cs.getSubId(), cs); |
| } else { |
| profile.connections.put(cs.getConnectionId(), cs); |
| } |
| } |
| if (name.equals("triggers")) { |
| readTriggersFromXml(xpp, context, profile); |
| } |
| } else if (event == XmlPullParser.END_DOCUMENT) { |
| throw new IOException("Premature end of file while parsing profle:" + profileName); |
| } |
| event = xpp.next(); |
| } |
| |
| /* we just loaded from XML, so nothing needs saving */ |
| profile.mDirty = false; |
| |
| return profile; |
| } |
| |
| /** @hide */ |
| public void doSelect(Context context, IKeyguardService keyguardService) { |
| // 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); |
| } |
| } |
| for (ConnectionSettings cs : networkConnectionSubIds.values()) { |
| if (cs.isOverride()) { |
| cs.processOverride(context); |
| } |
| } |
| |
| // Set ring mode |
| mRingMode.processOverride(context); |
| // Set airplane mode |
| mAirplaneMode.processOverride(context); |
| |
| // Set brightness |
| mBrightness.processOverride(context); |
| |
| if (keyguardService != null) { |
| // Set lock screen mode |
| mScreenLockMode.processOverride(context, keyguardService); |
| } else { |
| Log.e(TAG, "cannot process screen lock override without a keyguard service."); |
| } |
| |
| // Set doze mode |
| if (mDozeMode != DozeMode.DEFAULT) { |
| Settings.Secure.putIntForUser(context.getContentResolver(), |
| Settings.Secure.DOZE_ENABLED, |
| mDozeMode == DozeMode.ENABLE ? 1 : 0, |
| UserHandle.USER_CURRENT); |
| } |
| |
| // Set notification light mode |
| if (mNotificationLightMode != NotificationLightMode.DEFAULT) { |
| Settings.System.putIntForUser(context.getContentResolver(), |
| Settings.System.NOTIFICATION_LIGHT_PULSE, |
| mNotificationLightMode == NotificationLightMode.ENABLE ? 1 : 0, |
| UserHandle.USER_CURRENT); |
| } |
| } |
| |
| /** |
| * Get the settings for a stream id in the {@link Profile} |
| * @return {@link StreamSettings} |
| */ |
| public StreamSettings getSettingsForStream(int streamId){ |
| return streams.get(streamId); |
| } |
| |
| /** |
| * Set the {@link StreamSettings} for the {@link Profile} |
| * @param descriptor |
| */ |
| public void setStreamSettings(StreamSettings descriptor){ |
| streams.put(descriptor.getStreamId(), descriptor); |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link StreamSettings} for the {@link Profile} |
| * @return {@link java.util.Collection<StreamSettings>} |
| */ |
| public Collection<StreamSettings> getStreamSettings(){ |
| return streams.values(); |
| } |
| |
| /** |
| * Get the settings for a connection id in the {@link Profile} |
| * @return {@link ConnectionSettings} |
| */ |
| public ConnectionSettings getSettingsForConnection(int connectionId){ |
| if (connectionId == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) { |
| if (networkConnectionSubIds.size() > 1) { |
| throw new UnsupportedOperationException("Use getConnectionSettingsWithSubId for MSIM devices!"); |
| } else { |
| return networkConnectionSubIds.values().iterator().next(); |
| } |
| } |
| return connections.get(connectionId); |
| } |
| |
| /** |
| * Get the settings for a {@link ConnectionSettings#PROFILE_CONNECTION_2G3G4G} by sub id. |
| * |
| * @param subId the sub id to lookup. Can be {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} |
| * @return {@link ConnectionSettings} |
| */ |
| public ConnectionSettings getConnectionSettingWithSubId(int subId) { |
| return networkConnectionSubIds.get(subId); |
| } |
| |
| /** |
| * Set the {@link ConnectionSettings} for the {@link Profile} |
| * @param descriptor |
| */ |
| public void setConnectionSettings(ConnectionSettings descriptor) { |
| if (descriptor.getConnectionId() == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) { |
| networkConnectionSubIds.put(descriptor.getSubId(), descriptor); |
| } else { |
| connections.put(descriptor.getConnectionId(), descriptor); |
| } |
| mDirty = true; |
| } |
| |
| /** |
| * Get the {@link ConnectionSettings} for the {@link Profile} |
| * @return {@link java.util.Collection<ConnectionSettings>} |
| */ |
| public Collection<ConnectionSettings> getConnectionSettings(){ |
| List<ConnectionSettings> combinedList = new ArrayList<>(); |
| combinedList.addAll(connections.values()); |
| combinedList.addAll(networkConnectionSubIds.values()); |
| return combinedList; |
| } |
| } |