CMSDK: Introduce Profiles API from frameworks.
Refactor to create a system service in secondary CM framework. Change-Id: Ic69da01d941bbd09271c260429d744f8e79ab7b9
This commit is contained in:
parent
4e081aef67
commit
4334b3d969
@ -0,0 +1,616 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2014 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 org.cyanogenmod.platform.internal;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
|
||||||
|
import android.app.ActivityManagerNative;
|
||||||
|
import android.app.NotificationGroup;
|
||||||
|
import android.app.backup.BackupManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.res.XmlResourceParser;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
|
||||||
|
import com.android.server.SystemService;
|
||||||
|
|
||||||
|
import cyanogenmod.app.CMContextConstants;
|
||||||
|
import cyanogenmod.app.Profile;
|
||||||
|
import cyanogenmod.app.ProfileGroup;
|
||||||
|
import cyanogenmod.app.ProfileManager;
|
||||||
|
import cyanogenmod.app.IProfileManager;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.io.File;
|
||||||
|
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 SystemService {
|
||||||
|
|
||||||
|
private static final String TAG = "CMProfileService";
|
||||||
|
// Enable the below for detailed logging of this class
|
||||||
|
private static final boolean LOCAL_LOGV = false;
|
||||||
|
|
||||||
|
public static final String PERMISSION_CHANGE_SETTINGS = "android.permission.WRITE_SETTINGS";
|
||||||
|
|
||||||
|
/* package */ static final File PROFILE_FILE =
|
||||||
|
new File(Environment.getSystemSecureDirectory(), "profiles.xml");
|
||||||
|
|
||||||
|
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 BackupManager mBackupManager;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
super(context);
|
||||||
|
mContext = context;
|
||||||
|
publishBinderService(CMContextConstants.CM_PROFILE_SERVICE, mService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
mBackupManager = new BackupManager(mContext);
|
||||||
|
|
||||||
|
mWildcardGroup = new NotificationGroup(
|
||||||
|
mContext.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 (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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final IBinder mService = new IProfileManager.Stub() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetAll() {
|
||||||
|
enforceChangePermissions();
|
||||||
|
initialize(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public boolean setActiveProfileByName(String profileName) {
|
||||||
|
if (!mProfileNames.containsKey(profileName)) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOCAL_LOGV) {
|
||||||
|
Log.v(TAG, "setActiveProfile(String) found profile name in mProfileNames.");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
setActiveProfileInternal(mProfiles.get(mProfileNames.get(profileName)), true);
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setActiveProfile(ParcelUuid profileParcelUuid) {
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
boolean ret = setActiveProfileInternal(profileParcelUuid.getUuid(), true);
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addProfile(Profile profile) {
|
||||||
|
enforceChangePermissions();
|
||||||
|
addProfileInternal(profile);
|
||||||
|
long token = clearCallingIdentity();
|
||||||
|
persistIfDirty();
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public Profile getProfileByName(String profileName) {
|
||||||
|
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 internalGetProfile(profileUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Profile[] getProfiles() {
|
||||||
|
Profile[] profiles = getProfileList().toArray(new Profile[mProfiles.size()]);
|
||||||
|
Arrays.sort(profiles);
|
||||||
|
return profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Profile getActiveProfile() {
|
||||||
|
return mActiveProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeProfile(Profile profile) {
|
||||||
|
enforceChangePermissions();
|
||||||
|
if (mProfileNames.remove(profile.getName()) != null
|
||||||
|
&& mProfiles.remove(profile.getUuid()) != null) {
|
||||||
|
mDirty = true;
|
||||||
|
long token = clearCallingIdentity();
|
||||||
|
persistIfDirty();
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProfile(Profile profile) {
|
||||||
|
enforceChangePermissions();
|
||||||
|
Profile old = mProfiles.get(profile.getUuid());
|
||||||
|
|
||||||
|
if (old == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
long token = clearCallingIdentity();
|
||||||
|
persistIfDirty();
|
||||||
|
|
||||||
|
// Also update if we changed the active profile
|
||||||
|
if (mActiveProfile != null && mActiveProfile.getUuid().equals(profile.getUuid())) {
|
||||||
|
setActiveProfileInternal(profile, true);
|
||||||
|
}
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean profileExists(ParcelUuid profileUuid) {
|
||||||
|
return mProfiles.containsKey(profileUuid.getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public boolean profileExistsByName(String profileName) {
|
||||||
|
for (Map.Entry<String, UUID> entry : mProfileNames.entrySet()) {
|
||||||
|
if (entry.getKey().equalsIgnoreCase(profileName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public boolean notificationGroupExistsByName(String notificationGroupName) {
|
||||||
|
for (NotificationGroup group : mGroups.values()) {
|
||||||
|
if (group.getName().equalsIgnoreCase(notificationGroupName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotificationGroup[] getNotificationGroups() {
|
||||||
|
return mGroups.values().toArray(new NotificationGroup[mGroups.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addNotificationGroup(NotificationGroup group) {
|
||||||
|
enforceChangePermissions();
|
||||||
|
addNotificationGroupInternal(group);
|
||||||
|
long token = clearCallingIdentity();
|
||||||
|
persistIfDirty();
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeNotificationGroup(NotificationGroup group) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
long token = clearCallingIdentity();
|
||||||
|
persistIfDirty();
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateNotificationGroup(NotificationGroup group) {
|
||||||
|
enforceChangePermissions();
|
||||||
|
NotificationGroup old = mGroups.get(group.getUuid());
|
||||||
|
if (old == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGroups.put(group.getUuid(), group);
|
||||||
|
/* no need to set mDirty, if the group was actually changed,
|
||||||
|
* it's marked as dirty by itself */
|
||||||
|
long token = clearCallingIdentity();
|
||||||
|
persistIfDirty();
|
||||||
|
restoreCallingIdentity(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotificationGroup getNotificationGroupForPackage(String pkg) {
|
||||||
|
for (NotificationGroup group : mGroups.values()) {
|
||||||
|
if (group.hasPackage(pkg)) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotificationGroup getNotificationGroup(ParcelUuid uuid) {
|
||||||
|
if (uuid.getUuid().equals(mWildcardGroup.getUuid())) {
|
||||||
|
return mWildcardGroup;
|
||||||
|
}
|
||||||
|
return mGroups.get(uuid.getUuid());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Profile internalGetProfile(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ Collection<Profile> getProfileList() {
|
||||||
|
return mProfiles.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getXmlString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("<profiles>\n<active>");
|
||||||
|
builder.append(TextUtils.htmlEncode(mActiveProfile.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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_FILE);
|
||||||
|
fw.write(getXmlString());
|
||||||
|
fw.close();
|
||||||
|
Log.d(TAG, "Save completed.");
|
||||||
|
mDirty = false;
|
||||||
|
mBackupManager.dataChanged();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforceChangePermissions() {
|
||||||
|
mContext.enforceCallingOrSelfPermission(PERMISSION_CHANGE_SETTINGS,
|
||||||
|
"You do not have permissions to change the Profile Manager.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by SystemBackupAgent after files are restored to disk.
|
||||||
|
void settingsRestored() {
|
||||||
|
initialize();
|
||||||
|
for (Profile p : mProfiles.values()) {
|
||||||
|
p.validateRingtones(mContext);
|
||||||
|
}
|
||||||
|
persistIfDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFromFile() throws XmlPullParserException, IOException {
|
||||||
|
XmlPullParserFactory xppf = XmlPullParserFactory.newInstance();
|
||||||
|
XmlPullParser xpp = xppf.newPullParser();
|
||||||
|
FileReader fr = new FileReader(PROFILE_FILE);
|
||||||
|
xpp.setInput(fr);
|
||||||
|
loadXml(xpp, mContext);
|
||||||
|
fr.close();
|
||||||
|
persistIfDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadXml(XmlPullParser xpp, Context context) throws
|
||||||
|
XmlPullParserException, IOException {
|
||||||
|
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_FILE);
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
setActiveProfileInternal(UUID.fromString(active), false);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
if (mProfileNames.containsKey(active)) {
|
||||||
|
setActiveProfileInternal(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.
|
||||||
|
setActiveProfileInternal(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 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 boolean setActiveProfileInternal(UUID profileUuid, boolean doInit) {
|
||||||
|
if (!mProfiles.containsKey(profileUuid)) {
|
||||||
|
Log.e(TAG, "Cannot set active profile to: "
|
||||||
|
+ profileUuid.toString() + " - does not exist.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOCAL_LOGV) Log.v(TAG, "setActiveProfile(UUID, boolean) found UUID in mProfiles.");
|
||||||
|
setActiveProfileInternal(mProfiles.get(profileUuid), doInit);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ void setActiveProfileInternal(Profile newActiveProfile, boolean doInit) {
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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");
|
||||||
|
// Call profile's "doSelect"
|
||||||
|
mActiveProfile.doSelect(mContext);
|
||||||
|
|
||||||
|
// Notify other applications of newly selected profile.
|
||||||
|
Intent broadcast = new Intent(ProfileManager.INTENT_ACTION_PROFILE_SELECTED);
|
||||||
|
broadcast.putExtra(ProfileManager.EXTRA_PROFILE_NAME,
|
||||||
|
mActiveProfile.getName());
|
||||||
|
broadcast.putExtra(ProfileManager.EXTRA_PROFILE_UUID,
|
||||||
|
mActiveProfile.getUuid().toString());
|
||||||
|
broadcast.putExtra(ProfileManager.EXTRA_LAST_PROFILE_NAME,
|
||||||
|
lastProfile.getName());
|
||||||
|
broadcast.putExtra(ProfileManager.EXTRA_LAST_PROFILE_UUID,
|
||||||
|
lastProfile.getUuid().toString());
|
||||||
|
|
||||||
|
mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
|
||||||
|
persistIfDirty();
|
||||||
|
} else if (lastProfile != mActiveProfile && ActivityManagerNative.isSystemReady()) {
|
||||||
|
// Something definitely changed: notify.
|
||||||
|
Intent broadcast = new Intent(ProfileManager.INTENT_ACTION_PROFILE_UPDATED);
|
||||||
|
broadcast.putExtra(ProfileManager.EXTRA_PROFILE_NAME,
|
||||||
|
mActiveProfile.getName());
|
||||||
|
broadcast.putExtra(ProfileManager.EXTRA_PROFILE_UUID,
|
||||||
|
mActiveProfile.getUuid().toString());
|
||||||
|
mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -39,4 +39,15 @@ public final class CMContextConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String CM_STATUS_BAR_SERVICE = "cmstatusbar";
|
public static final String CM_STATUS_BAR_SERVICE = "cmstatusbar";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use with {@link android.content.Context#getSystemService} to retrieve a
|
||||||
|
* {@link cyanogenmod.app.ProfileManager} for informing the user of
|
||||||
|
* background events.
|
||||||
|
*
|
||||||
|
* @see android.content.Context#getSystemService
|
||||||
|
* @see cyanogenmod.app.ProfileManager
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String CM_PROFILE_SERVICE = "profile";
|
||||||
}
|
}
|
||||||
|
50
src/java/cyanogenmod/app/IProfileManager.aidl
Normal file
50
src/java/cyanogenmod/app/IProfileManager.aidl
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/* //device/java/android/android/app/IProfileManager.aidl
|
||||||
|
**
|
||||||
|
** 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 cyanogenmod.app;
|
||||||
|
|
||||||
|
import cyanogenmod.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();
|
||||||
|
}
|
19
src/java/cyanogenmod/app/Profile.aidl
Normal file
19
src/java/cyanogenmod/app/Profile.aidl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2014 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 cyanogenmod.app;
|
||||||
|
|
||||||
|
parcelable Profile;
|
869
src/java/cyanogenmod/app/Profile.java
Executable file
869
src/java/cyanogenmod/app/Profile.java
Executable file
@ -0,0 +1,869 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 cyanogenmod.app;
|
||||||
|
|
||||||
|
import android.app.AirplaneModeSettings;
|
||||||
|
import android.app.BrightnessSettings;
|
||||||
|
import android.app.ConnectionSettings;
|
||||||
|
import android.app.RingModeSettings;
|
||||||
|
import android.app.StreamSettings;
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<String, ProfileTrigger> mTriggers = new HashMap<String, ProfileTrigger>();
|
||||||
|
|
||||||
|
private Map<Integer, ConnectionSettings> connections = new HashMap<Integer, ConnectionSettings>();
|
||||||
|
|
||||||
|
private RingModeSettings mRingMode = new RingModeSettings();
|
||||||
|
|
||||||
|
private AirplaneModeSettings mAirplaneMode = new AirplaneModeSettings();
|
||||||
|
|
||||||
|
private BrightnessSettings mBrightness = new BrightnessSettings();
|
||||||
|
|
||||||
|
private int mScreenLockMode = LockMode.DEFAULT;
|
||||||
|
|
||||||
|
private int mExpandedDesktopMode = ExpandedDesktopMode.DEFAULT;
|
||||||
|
|
||||||
|
private int mDozeMode = DozeMode.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 ExpandedDesktopMode {
|
||||||
|
public static final int DEFAULT = 0;
|
||||||
|
public static final int ENABLE = 1;
|
||||||
|
public static final int DISABLE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static class DozeMode {
|
||||||
|
public static final int DEFAULT = 0;
|
||||||
|
public static final int ENABLE = 1;
|
||||||
|
public static final int DISABLE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static class TriggerType {
|
||||||
|
public static final int WIFI = 0;
|
||||||
|
public static final int BLUETOOTH = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static class TriggerState {
|
||||||
|
public static final int ON_CONNECT = 0;
|
||||||
|
public static final int ON_DISCONNECT = 1;
|
||||||
|
public static final int DISABLED = 2;
|
||||||
|
public static final int ON_A2DP_CONNECT = 3;
|
||||||
|
public static final int ON_A2DP_DISCONNECT = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ProfileTrigger implements Parcelable {
|
||||||
|
private int mType;
|
||||||
|
private String mId;
|
||||||
|
private String mName;
|
||||||
|
private int mState;
|
||||||
|
|
||||||
|
public ProfileTrigger(int type, String id, int state, String name) {
|
||||||
|
mType = type;
|
||||||
|
mId = id;
|
||||||
|
mState = state;
|
||||||
|
mName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProfileTrigger(Parcel in) {
|
||||||
|
mType = in.readInt();
|
||||||
|
mId = in.readString();
|
||||||
|
mState = in.readInt();
|
||||||
|
mName = in.readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(mType);
|
||||||
|
dest.writeString(mId);
|
||||||
|
dest.writeInt(mState);
|
||||||
|
dest.writeString(mName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getState() {
|
||||||
|
return mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @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 getTrigger(int type, String id) {
|
||||||
|
ProfileTrigger trigger = id != null ? mTriggers.get(id) : null;
|
||||||
|
if (trigger != null) {
|
||||||
|
return trigger.mState;
|
||||||
|
}
|
||||||
|
return TriggerState.DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.writeParcelable(mRingMode, flags);
|
||||||
|
dest.writeParcelable(mAirplaneMode, flags);
|
||||||
|
dest.writeParcelable(mBrightness, flags);
|
||||||
|
dest.writeInt(mScreenLockMode);
|
||||||
|
dest.writeMap(mTriggers);
|
||||||
|
dest.writeInt(mExpandedDesktopMode);
|
||||||
|
dest.writeInt(mDozeMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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);
|
||||||
|
}
|
||||||
|
mRingMode = (RingModeSettings) in.readParcelable(null);
|
||||||
|
mAirplaneMode = (AirplaneModeSettings) in.readParcelable(null);
|
||||||
|
mBrightness = (BrightnessSettings) in.readParcelable(null);
|
||||||
|
mScreenLockMode = in.readInt();
|
||||||
|
in.readMap(mTriggers, null);
|
||||||
|
mExpandedDesktopMode = in.readInt();
|
||||||
|
mDozeMode = 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 RingModeSettings getRingMode() {
|
||||||
|
return mRingMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRingMode(RingModeSettings descriptor) {
|
||||||
|
mRingMode = descriptor;
|
||||||
|
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 getExpandedDesktopMode() {
|
||||||
|
return mExpandedDesktopMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpandedDesktopMode(int expandedDesktopMode) {
|
||||||
|
if (expandedDesktopMode < ExpandedDesktopMode.DEFAULT
|
||||||
|
|| expandedDesktopMode > ExpandedDesktopMode.DISABLE) {
|
||||||
|
mExpandedDesktopMode = ExpandedDesktopMode.DEFAULT;
|
||||||
|
} else {
|
||||||
|
mExpandedDesktopMode = expandedDesktopMode;
|
||||||
|
}
|
||||||
|
mDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDozeMode() {
|
||||||
|
return mDozeMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDozeMode(int dozeMode) {
|
||||||
|
if (dozeMode < DozeMode.DEFAULT
|
||||||
|
|| dozeMode > DozeMode.DISABLE) {
|
||||||
|
mDozeMode = DozeMode.DEFAULT;
|
||||||
|
} else {
|
||||||
|
mDozeMode = dozeMode;
|
||||||
|
}
|
||||||
|
mDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AirplaneModeSettings getAirplaneMode() {
|
||||||
|
return mAirplaneMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAirplaneMode(AirplaneModeSettings descriptor) {
|
||||||
|
mAirplaneMode = descriptor;
|
||||||
|
mDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BrightnessSettings getBrightness() {
|
||||||
|
return mBrightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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() == 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("<expanded-desktop-mode>");
|
||||||
|
builder.append(mExpandedDesktopMode);
|
||||||
|
builder.append("</expanded-desktop-mode>\n");
|
||||||
|
|
||||||
|
builder.append("<doze-mode>");
|
||||||
|
builder.append(mDozeMode);
|
||||||
|
builder.append("</doze-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);
|
||||||
|
}
|
||||||
|
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", "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("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")) {
|
||||||
|
profile.setScreenLockMode(Integer.valueOf(xpp.nextText()));
|
||||||
|
}
|
||||||
|
if (name.equals("expanded-desktop-mode")) {
|
||||||
|
profile.setExpandedDesktopMode(Integer.valueOf(xpp.nextText()));
|
||||||
|
}
|
||||||
|
if (name.equals("doze-mode")) {
|
||||||
|
profile.setDozeMode(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("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) {
|
||||||
|
// 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 ring mode
|
||||||
|
mRingMode.processOverride(context);
|
||||||
|
// Set airplane mode
|
||||||
|
mAirplaneMode.processOverride(context);
|
||||||
|
|
||||||
|
// Set brightness
|
||||||
|
mBrightness.processOverride(context);
|
||||||
|
|
||||||
|
// Set expanded desktop
|
||||||
|
// if (mExpandedDesktopMode != ExpandedDesktopMode.DEFAULT) {
|
||||||
|
// Settings.System.putIntForUser(context.getContentResolver(),
|
||||||
|
// Settings.System.EXPANDED_DESKTOP_STATE,
|
||||||
|
// mExpandedDesktopMode == ExpandedDesktopMode.ENABLE ? 1 : 0,
|
||||||
|
// UserHandle.USER_CURRENT);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Set doze mode
|
||||||
|
if (mDozeMode != DozeMode.DEFAULT) {
|
||||||
|
Settings.Secure.putIntForUser(context.getContentResolver(),
|
||||||
|
Settings.Secure.DOZE_ENABLED,
|
||||||
|
mDozeMode == DozeMode.ENABLE ? 1 : 0,
|
||||||
|
UserHandle.USER_CURRENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
370
src/java/cyanogenmod/app/ProfileGroup.java
Normal file
370
src/java/cyanogenmod/app/ProfileGroup.java
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 cyanogenmod.app;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationGroup;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
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 void applyOverridesToNotification(Notification notification) {
|
||||||
|
switch (mSoundMode) {
|
||||||
|
case OVERRIDE:
|
||||||
|
notification.sound = mSoundOverride;
|
||||||
|
break;
|
||||||
|
case SUPPRESS:
|
||||||
|
notification.defaults &= ~Notification.DEFAULT_SOUND;
|
||||||
|
notification.sound = null;
|
||||||
|
break;
|
||||||
|
case DEFAULT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (mVibrateMode) {
|
||||||
|
case OVERRIDE:
|
||||||
|
notification.defaults |= Notification.DEFAULT_VIBRATE;
|
||||||
|
break;
|
||||||
|
case SUPPRESS:
|
||||||
|
notification.defaults &= ~Notification.DEFAULT_VIBRATE;
|
||||||
|
notification.vibrate = null;
|
||||||
|
break;
|
||||||
|
case DEFAULT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (mLightsMode) {
|
||||||
|
case OVERRIDE:
|
||||||
|
notification.defaults |= Notification.DEFAULT_LIGHTS;
|
||||||
|
break;
|
||||||
|
case SUPPRESS:
|
||||||
|
notification.defaults &= ~Notification.DEFAULT_LIGHTS;
|
||||||
|
notification.flags &= ~Notification.FLAG_SHOW_LIGHTS;
|
||||||
|
break;
|
||||||
|
case DEFAULT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateOverrideUri(Context context, Uri uri) {
|
||||||
|
if (RingtoneManager.isDefault(uri)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||||
|
boolean valid = false;
|
||||||
|
|
||||||
|
if (cursor != null) {
|
||||||
|
valid = cursor.moveToFirst();
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void validateOverrideUris(Context context) {
|
||||||
|
if (!validateOverrideUri(context, mSoundOverride)) {
|
||||||
|
mSoundOverride = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||||
|
mSoundMode = Mode.DEFAULT;
|
||||||
|
mDirty = true;
|
||||||
|
}
|
||||||
|
if (!validateOverrideUri(context, mRingerOverride)) {
|
||||||
|
mRingerOverride = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
|
||||||
|
mRingerMode = Mode.DEFAULT;
|
||||||
|
mDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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()));
|
||||||
|
}
|
||||||
|
} else if (event == XmlPullParser.END_DOCUMENT) {
|
||||||
|
throw new IOException("Premature end of file while parsing profleGroup:" + name);
|
||||||
|
}
|
||||||
|
event = xpp.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we just loaded from XML, no need to save */
|
||||||
|
profileGroup.mDirty = false;
|
||||||
|
|
||||||
|
return profileGroup;
|
||||||
|
}
|
||||||
|
}
|
467
src/java/cyanogenmod/app/ProfileManager.java
Normal file
467
src/java/cyanogenmod/app/ProfileManager.java
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 cyanogenmod.app;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import android.annotation.SdkConstant;
|
||||||
|
import android.annotation.SdkConstant.SdkConstantType;
|
||||||
|
import android.app.NotificationGroup;
|
||||||
|
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.provider.Settings;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import cyanogenmod.app.IProfileManager;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class ProfileManager {
|
||||||
|
|
||||||
|
private static IProfileManager sService;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
private static final String TAG = "ProfileManager";
|
||||||
|
|
||||||
|
private static final String SYSTEM_PROFILES_ENABLED = "system_profiles_enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra for {@link INTENT_ACTION_PROFILE_SELECTED} and {@link INTENT_ACTION_PROFILE_UPDATED}:
|
||||||
|
* The name of the newly activated or updated profile
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILE_NAME = "name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra for {@link INTENT_ACTION_PROFILE_SELECTED} and {@link INTENT_ACTION_PROFILE_UPDATED}:
|
||||||
|
* The string representation of the UUID of the newly activated or updated profile
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILE_UUID = "uuid";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra for {@link INTENT_ACTION_PROFILE_SELECTED}:
|
||||||
|
* The name of the previously active profile
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_LAST_PROFILE_NAME = "lastName";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra for {@link INTENT_ACTION_PROFILE_SELECTED}:
|
||||||
|
* The string representation of the UUID of the previously active profile
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_LAST_PROFILE_UUID = "uuid";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Broadcast Action: Current profile has been updated. This is triggered every time the
|
||||||
|
* currently active profile is updated, instead of selected.</p>
|
||||||
|
* <p> For instance, this includes profile updates caused by a locale change, which doesn't
|
||||||
|
* trigger a profile selection, but causes its name to change.</p>
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String INTENT_ACTION_PROFILE_UPDATED =
|
||||||
|
"android.intent.action.PROFILE_UPDATED";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity Action: Shows a profile picker.
|
||||||
|
* <p>
|
||||||
|
* Input: {@link #EXTRA_PROFILE_EXISTING_UUID}, {@link #EXTRA_PROFILE_SHOW_NONE},
|
||||||
|
* {@link #EXTRA_PROFILE_TITLE}.
|
||||||
|
* <p>
|
||||||
|
* Output: {@link #EXTRA_PROFILE_PICKED_UUID}.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
|
||||||
|
public static final String ACTION_PROFILE_PICKER = "android.intent.action.PROFILE_PICKER";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final UUID NO_PROFILE =
|
||||||
|
UUID.fromString("00000000-0000-0000-0000-000000000000");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given to the profile picker as a boolean. Whether to show an item for
|
||||||
|
* deselect the profile. If the "None" item is picked,
|
||||||
|
* {@link #EXTRA_PROFILE_PICKED_UUID} will be {@link #NO_PROFILE}.
|
||||||
|
*
|
||||||
|
* @see #ACTION_PROFILE_PICKER
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILE_SHOW_NONE =
|
||||||
|
"android.intent.extra.profile.SHOW_NONE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given to the profile picker as a {@link UUID} string representation. The {@link UUID}
|
||||||
|
* representation of the current profile, which will be used to show a checkmark next to
|
||||||
|
* the item for this {@link UUID}. If the item is {@link #NO_PROFILE} then "None" item
|
||||||
|
* is selected if {@link EXTRA_PROFILE_SHOW_NONE} is enabled. Otherwise, the current
|
||||||
|
* profile is selected.
|
||||||
|
*
|
||||||
|
* @see #ACTION_PROFILE_PICKER
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILE_EXISTING_UUID =
|
||||||
|
"android.intent.extra.profile.EXISTING_UUID";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given to the profile picker as a {@link CharSequence}. The title to
|
||||||
|
* show for the profile picker. This has a default value that is suitable
|
||||||
|
* in most cases.
|
||||||
|
*
|
||||||
|
* @see #ACTION_PROFILE_PICKER
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILE_TITLE = "android.intent.extra.profile.TITLE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returned from the profile picker as a {@link UUID} string representation.
|
||||||
|
* <p>
|
||||||
|
* It will be one of:
|
||||||
|
* <li> the picked profile,
|
||||||
|
* <li> null if the "None" item was picked.
|
||||||
|
*
|
||||||
|
* @see #ACTION_PROFILE_PICKER
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILE_PICKED_UUID =
|
||||||
|
"android.intent.extra.profile.PICKED_UUID";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast intent action indicating that Profiles has been enabled or disabled.
|
||||||
|
* One extra provides this state as an int.
|
||||||
|
*
|
||||||
|
* @see #EXTRA_PROFILES_STATE
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||||
|
public static final String PROFILES_STATE_CHANGED_ACTION =
|
||||||
|
"android.app.profiles.PROFILES_STATE_CHANGED";
|
||||||
|
/**
|
||||||
|
* The lookup key for an int that indicates whether Profiles are enabled or
|
||||||
|
* disabled. Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}.
|
||||||
|
*
|
||||||
|
* @see #PROFILES_STATE_DISABLED
|
||||||
|
* @see #PROFILES_STATE_ENABLED
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILES_STATE = "profile_state";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the resource id theme to use for the dialog picker activity.<br/>
|
||||||
|
* The default theme is <code>com.android.internal.R.Theme_Holo_Dialog_Alert</code>.
|
||||||
|
*
|
||||||
|
* @see #ACTION_PROFILE_PICKER
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_PROFILE_DIALOG_THEME =
|
||||||
|
"android.intent.extra.profile.DIALOG_THEME";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profiles are disabled.
|
||||||
|
*
|
||||||
|
* @see #PROFILES_STATE_CHANGED_ACTION
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int PROFILES_STATE_DISABLED = 0;
|
||||||
|
/**
|
||||||
|
* Profiles are enabled.
|
||||||
|
*
|
||||||
|
* @see #PROFILES_STATE_CHANGED_ACTION
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int PROFILES_STATE_ENABLED = 1;
|
||||||
|
|
||||||
|
// A blank profile that is created to be returned if profiles disabled
|
||||||
|
private static Profile mEmptyProfile;
|
||||||
|
|
||||||
|
private static ProfileManager sProfileManagerInstance;
|
||||||
|
private ProfileManager(Context context) {
|
||||||
|
Context appContext = context.getApplicationContext();
|
||||||
|
if (appContext != null) {
|
||||||
|
mContext = appContext;
|
||||||
|
} else {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
sService = getService();
|
||||||
|
mEmptyProfile = new Profile("EmptyProfile");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create an instance of the {@link cyanogenmod.app.ProfileManager}
|
||||||
|
* @param context
|
||||||
|
* @return {@link cyanogenmod.app.ProfileManager}
|
||||||
|
*/
|
||||||
|
public static ProfileManager getInstance(Context context) {
|
||||||
|
if (sProfileManagerInstance == null) {
|
||||||
|
sProfileManagerInstance = new ProfileManager(context);
|
||||||
|
}
|
||||||
|
return sProfileManagerInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
static public IProfileManager getService() {
|
||||||
|
if (sService != null) {
|
||||||
|
return sService;
|
||||||
|
}
|
||||||
|
IBinder b = ServiceManager.getService(CMContextConstants.CM_PROFILE_SERVICE);
|
||||||
|
sService = IProfileManager.Stub.asInterface(b);
|
||||||
|
return sService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void setActiveProfile(String profileName) {
|
||||||
|
if (Settings.System.getInt(mContext.getContentResolver(),
|
||||||
|
SYSTEM_PROFILES_ENABLED, 1) == 1) {
|
||||||
|
// Profiles are enabled, return active profile
|
||||||
|
try {
|
||||||
|
getService().setActiveProfileByName(profileName);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveProfile(UUID profileUuid) {
|
||||||
|
if (Settings.System.getInt(mContext.getContentResolver(),
|
||||||
|
SYSTEM_PROFILES_ENABLED, 1) == 1) {
|
||||||
|
// Profiles are enabled, return active profile
|
||||||
|
try {
|
||||||
|
getService().setActiveProfile(new ParcelUuid(profileUuid));
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Profile getActiveProfile() {
|
||||||
|
if (Settings.System.getInt(mContext.getContentResolver(),
|
||||||
|
SYSTEM_PROFILES_ENABLED, 1) == 1) {
|
||||||
|
// Profiles are enabled, return active profile
|
||||||
|
try {
|
||||||
|
return getService().getActiveProfile();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Profiles are not enabled, return the empty profile
|
||||||
|
return mEmptyProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
144
src/java/cyanogenmod/app/ProfileTriggerHelper.java
Normal file
144
src/java/cyanogenmod/app/ProfileTriggerHelper.java
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2014 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 cyanogenmod.app;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.net.wifi.WifiSsid;
|
||||||
|
import android.net.wifi.WifiInfo;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ProfileTriggerHelper extends BroadcastReceiver {
|
||||||
|
private static final String TAG = "ProfileTriggerHelper";
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private ProfileManager mManager;
|
||||||
|
|
||||||
|
private WifiManager mWifiManager;
|
||||||
|
private String mLastConnectedSSID;
|
||||||
|
|
||||||
|
private IntentFilter mIntentFilter;
|
||||||
|
private boolean mFilterRegistered = false;
|
||||||
|
|
||||||
|
private ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange) {
|
||||||
|
updateEnabled();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public ProfileTriggerHelper(Context context, ProfileManager manager) {
|
||||||
|
mContext = context;
|
||||||
|
mManager = manager;
|
||||||
|
|
||||||
|
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
|
||||||
|
mLastConnectedSSID = getActiveSSID();
|
||||||
|
|
||||||
|
mIntentFilter = new IntentFilter();
|
||||||
|
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
|
||||||
|
mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||||
|
mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||||
|
// mIntentFilter.addAction(AudioManager.A2DP_ROUTE_CHANGED_ACTION);
|
||||||
|
updateEnabled();
|
||||||
|
|
||||||
|
mContext.getContentResolver().registerContentObserver(
|
||||||
|
Settings.System.getUriFor(Settings.System.SYSTEM_PROFILES_ENABLED), false,
|
||||||
|
mSettingsObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateEnabled() {
|
||||||
|
boolean enabled = Settings.System.getInt(mContext.getContentResolver(),
|
||||||
|
Settings.System.SYSTEM_PROFILES_ENABLED, 1) == 1;
|
||||||
|
if (enabled && !mFilterRegistered) {
|
||||||
|
Log.v(TAG, "Enabling");
|
||||||
|
mContext.registerReceiver(this, mIntentFilter);
|
||||||
|
mFilterRegistered = true;
|
||||||
|
} else if (!enabled && mFilterRegistered) {
|
||||||
|
Log.v(TAG, "Disabling");
|
||||||
|
mContext.unregisterReceiver(this);
|
||||||
|
mFilterRegistered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
|
||||||
|
String activeSSID = getActiveSSID();
|
||||||
|
int triggerState;
|
||||||
|
|
||||||
|
if (activeSSID != null) {
|
||||||
|
triggerState = Profile.TriggerState.ON_CONNECT;
|
||||||
|
mLastConnectedSSID = activeSSID;
|
||||||
|
} else {
|
||||||
|
triggerState = Profile.TriggerState.ON_DISCONNECT;
|
||||||
|
}
|
||||||
|
checkTriggers(Profile.TriggerType.WIFI, mLastConnectedSSID, triggerState);
|
||||||
|
} else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)
|
||||||
|
|| action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
|
||||||
|
int triggerState = action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)
|
||||||
|
? Profile.TriggerState.ON_CONNECT : Profile.TriggerState.ON_DISCONNECT;
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
|
||||||
|
checkTriggers(Profile.TriggerType.BLUETOOTH, device.getAddress(), triggerState);
|
||||||
|
/* } else if (action.equals(AudioManager.A2DP_ROUTE_CHANGED_ACTION)) {
|
||||||
|
BluetoothDevice device = intent
|
||||||
|
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
|
||||||
|
int triggerState = (state == BluetoothProfile.STATE_CONNECTED)
|
||||||
|
? Profile.TriggerState.ON_A2DP_CONNECT :
|
||||||
|
Profile.TriggerState.ON_A2DP_DISCONNECT;
|
||||||
|
|
||||||
|
checkTriggers(Profile.TriggerType.BLUETOOTH, device.getAddress(), triggerState);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkTriggers(int type, String id, int newState) {
|
||||||
|
/*for (Profile p : mManager.getProfileList()) {
|
||||||
|
if (newState != p.getTrigger(type, id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID currentProfileUuid = mManager.getActiveProfile().getUuid();
|
||||||
|
if (!currentProfileUuid.equals(p.getUuid())) {
|
||||||
|
//mManager.setActiveProfile(p, true);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getActiveSSID() {
|
||||||
|
WifiInfo wifiinfo = mWifiManager.getConnectionInfo();
|
||||||
|
if (wifiinfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
WifiSsid ssid = wifiinfo.getWifiSsid();
|
||||||
|
if (ssid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ssid.toString();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user