SettingInjectorService.java revision 184e75146cb17f8695dffba69e0ca8d80b350af3
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.location; 18 19import android.app.IntentService; 20import android.content.Intent; 21import android.os.Bundle; 22import android.os.Message; 23import android.os.Messenger; 24import android.os.RemoteException; 25import android.preference.Preference; 26import android.util.Log; 27 28/** 29 * Dynamically specifies the summary (subtile) and enabled status of a preference injected into 30 * the "Settings > Location > Location services" list. 31 * 32 * The location services list is intended for use only by preferences that affect multiple apps from 33 * the same developer. Location settings that apply only to one app should be shown within that app, 34 * rather than in the system settings. 35 * 36 * To add a preference to the list, a subclass of {@link SettingInjectorService} must be declared in 37 * the manifest as so: 38 * <pre> 39 * <service android:name="com.example.android.injector.MyInjectorService" > 40 * <intent-filter> 41 * <action android:name="com.android.settings.InjectedLocationSetting" /> 42 * </intent-filter> 43 * 44 * <meta-data 45 * android:name="com.android.settings.InjectedLocationSetting" 46 * android:resource="@xml/my_injected_location_setting" /> 47 * </service> 48 * </pre> 49 * The resource file specifies the static data for the setting: 50 * <pre> 51 * <injected-location-setting xmlns:android="http://schemas.android.com/apk/res/android" 52 * android:label="@string/injected_setting_label" 53 * android:icon="@drawable/ic_launcher" 54 * android:settingsActivity="com.example.android.injector.MySettingActivity" 55 * /> 56 * </pre> 57 * Here: 58 * <ul> 59 * <li>label: The {@link Preference#getTitle()} value. The title should make it clear which apps 60 * are affected by the setting, typically by including the name of the developer. For example, 61 * "Acme Corp. ads preferences." </li> 62 * 63 * <li>icon: The {@link Preference#getIcon()} value. Typically this will be a generic icon for 64 * the developer rather than the icon for an individual app.</li> 65 * 66 * <li>settingsActivity: the activity which is launched to allow the user to modify the setting 67 * value The activity must be in the same package as the subclass of 68 * {@link SettingInjectorService}. The activity should use your own branding to help emphasize 69 * to the user that it is not part of the system settings.</li> 70 * </ul> 71 * 72 * To ensure a good user experience, your {@link #onHandleIntent(Intent)} must complete within 73 * 200 msec even if your app is not already running. This means that both 74 * {@link android.app.Application#onCreate()} and {@link #getStatus()} must be fast. If you exceed 75 * this time, then this can delay the retrieval of settings status for other apps as well. 76 * 77 * For consistency, the label and {@link #getStatus()} values should be provided in all of the 78 * locales supported by the system settings app. The text should not contain offensive language. 79 * 80 * For compactness, only one copy of a given setting should be injected. If each account has a 81 * distinct value for the setting, then the {@link #getStatus()} value should represent a summary of 82 * the state across all of the accounts and {@code settingsActivity} should display the value for 83 * each account. 84 * 85 * Apps that violate these guidelines will be taken down from the Google Play Store and/or flagged 86 * as malware. 87 */ 88// TODO: is there a public list of supported locales? 89// TODO: is there a public list of guidelines for settings text? 90// TODO: would a bound service be better? E.g., we could just disconnect if a service took too long 91public abstract class SettingInjectorService extends IntentService { 92 93 /** 94 * Name of the bundle key for the string specifying the status of the setting (e.g., "ON" or 95 * "OFF"). 96 * 97 * @hide 98 */ 99 public static final String STATUS_KEY = "status"; 100 101 /** 102 * Name of the bundle key for the string specifying whether the setting is currently enabled. 103 * 104 * @hide 105 */ 106 public static final String ENABLED_KEY = "enabled"; 107 108 /** 109 * Name of the intent key used to specify the messenger 110 * 111 * @hide 112 */ 113 public static final String MESSENGER_KEY = "messenger"; 114 115 /** 116 * Intent action a client should broadcast when the value of one of its injected settings has 117 * changed, so that the setting can be updated in the UI. 118 */ 119 public static final String UPDATE_INTENT = "com.android.location.InjectedSettingChanged"; 120 121 private final String mLogTag; 122 123 /** 124 * Constructor. 125 * 126 * @param logTag used for logging, must be less than 23 characters 127 */ 128 public SettingInjectorService(String logTag) { 129 super(logTag); 130 131 // Fast fail if log tag is too long 132 Log.isLoggable(logTag, Log.WARN); 133 134 mLogTag = logTag; 135 } 136 137 @Override 138 final protected void onHandleIntent(Intent intent) { 139 // Get messenger first to ensure intent doesn't get messed with (in case we later decide 140 // to pass intent into getStatus()) 141 Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY); 142 143 Status status = getStatus(); 144 145 // Send the status back to the caller via the messenger 146 Message message = Message.obtain(); 147 Bundle bundle = new Bundle(); 148 bundle.putString(STATUS_KEY, status.summary); 149 bundle.putBoolean(ENABLED_KEY, status.enabled); 150 message.setData(bundle); 151 152 if (Log.isLoggable(mLogTag, Log.DEBUG)) { 153 Log.d(mLogTag, 154 "received " + intent + " and " + status + ", sending message: " + message); 155 } 156 try { 157 messenger.send(message); 158 } catch (RemoteException e) { 159 Log.e(mLogTag, "", e); 160 } 161 } 162 163 /** 164 * Reads the status of the setting. 165 */ 166 protected abstract Status getStatus(); 167 168 /** 169 * Dynamic characteristics of an injected location setting. 170 */ 171 public static final class Status { 172 173 public final String summary; 174 175 public final boolean enabled; 176 177 /** 178 * Constructor. 179 * 180 * @param summary the {@link Preference#getSummary()} value 181 * @param enabled the {@link Preference#isEnabled()} value 182 */ 183 public Status(String summary, boolean enabled) { 184 this.summary = summary; 185 this.enabled = enabled; 186 } 187 188 @Override 189 public String toString() { 190 return "Status{summary='" + summary + '\'' + ", enabled=" + enabled + '}'; 191 } 192 } 193} 194