SettingInjectorService.java revision fa2992c412c08f76331a3f58ca57cf8cf04e7b84
1946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca/* 2946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * Copyright (C) 2013 The Android Open Source Project 3877128505431adaf817dc8069172ebe4a1cdf5d8José Fonseca * 4946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * Licensed under the Apache License, Version 2.0 (the "License"); 5946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * you may not use this file except in compliance with the License. 6946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * You may obtain a copy of the License at 7946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * 8946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * http://www.apache.org/licenses/LICENSE-2.0 9946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * 10946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * Unless required by applicable law or agreed to in writing, software 11946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * distributed under the License is distributed on an "AS IS" BASIS, 12946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * See the License for the specific language governing permissions and 14946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * limitations under the License. 15946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca */ 16946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca 17946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecapackage android.location; 18946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca 19946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecaimport android.app.IntentService; 20946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecaimport android.content.Intent; 21877128505431adaf817dc8069172ebe4a1cdf5d8José Fonsecaimport android.os.Bundle; 22946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecaimport android.os.Message; 23946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecaimport android.os.Messenger; 24946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecaimport android.os.RemoteException; 25946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecaimport android.preference.Preference; 26946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecaimport android.util.Log; 27946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca 28877128505431adaf817dc8069172ebe4a1cdf5d8José Fonseca/** 29946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * Dynamically specifies the summary (subtile) and enabled status of a preference injected into 30946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * the "Settings > Location > Location services" list. 31c9a5930fe45a0a0299769bd2b672ca516d1bf39eJosé Fonseca * 32c9a5930fe45a0a0299769bd2b672ca516d1bf39eJosé Fonseca * The location services list is intended for use only by preferences that affect multiple apps from 33946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * the same developer. Location settings that apply only to one app should be shown within that app, 34946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * rather than in the system settings. 35946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * 36946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * To add a preference to the list, a subclass of {@link SettingInjectorService} must be declared in 37ff2d192ec50e6a8cbaa8f14bca989fe5b61a3c46Marek Olšák * the manifest as so: 38946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * <pre> 39946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * <service android:name="com.example.android.injector.MyInjectorService" > 40c022e15d1e56ba3a9c6b74eef6556d6063e2e322José Fonseca * <intent-filter> 416ce68ad3ca242076bbb93fdd99bb448f87a31d15José Fonseca * <action android:name="com.android.settings.InjectedLocationSetting" /> 4217c9d7eea7b3365c59455a731fcb81e69bb86ce2Roland Scheidegger * </intent-filter> 435b4c43d98556c5a4806757513bcb196a724518c5Keith Whitwell * 44946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * <meta-data 45946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * android:name="com.android.settings.InjectedLocationSetting" 46946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * android:resource="@xml/my_injected_location_setting" /> 47946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * </service> 48946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * </pre> 49b995a010e688bc4d4557e973e5e28091c378e881José Fonseca * The resource file specifies the static data for the setting: 50946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * <pre> 512d6b39f05edcd575b1e10d53f96a38bec848fa67José Fonseca * <injected-location-setting xmlns:android="http://schemas.android.com/apk/res/android" 523160cbabccf1f7d8bdf344242507b9c3082f15c6José Fonseca * android:label="@string/injected_setting_label" 535b4c43d98556c5a4806757513bcb196a724518c5Keith Whitwell * android:icon="@drawable/ic_launcher" 54f89730385532056e89e3b9053c244a67f84e323eRoland Scheidegger * android:settingsActivity="com.example.android.injector.MySettingActivity" 55946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * /> 56946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * </pre> 57946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * Here: 58946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * <ul> 59946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * <li>label: The {@link Preference#getTitle()} value. The title should make it clear which apps 6097b4681d7e1ccf40d1584436ade7c70fc1893e27José Fonseca * are affected by the setting, typically by including the name of the developer. For example, 61b3538d356316b282d6408f710f2df5f06bf315f5Brian Paul * "Acme Corp. ads preferences." </li> 62b3538d356316b282d6408f710f2df5f06bf315f5Brian Paul * 63946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * <li>icon: The {@link Preference#getIcon()} value. Typically this will be a generic icon for 64946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * the developer rather than the icon for an individual app.</li> 6573af91e938eb27b001404f11195fb06ff9b08903José Fonseca * 66b995a010e688bc4d4557e973e5e28091c378e881José Fonseca * <li>settingsActivity: the activity which is launched to allow the user to modify the setting 67a6d9d18faecef9963be3e4b64a21b89889b4670dKeith Whitwell * value The activity must be in the same package as the subclass of 68f89730385532056e89e3b9053c244a67f84e323eRoland Scheidegger * {@link SettingInjectorService}. The activity should use your own branding to help emphasize 69b5e381d9783f17c9a527ac38122444eac6807566Zack Rusin * to the user that it is not part of the system settings.</li> 70946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * </ul> 71946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * 722d368b982af5e6566c7da7fd2bc8b190af28188bJosé Fonseca * For consistency, the label and {@link #getStatus()} values should be provided in all of the 73d904ed88c1d957f662497343de7dc3e9fa743e47José Fonseca * locales supported by the system settings app. The text should not contain offensive language. 746a509ec6dd16f93758a308a0f0eb1b032f099693Roland Scheidegger * 75946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * For compactness, only one copy of a given setting should be injected. If each account has a 763427466e6dbbb8db7c1ecda6b3859ca1cc5827a3Brian Paul * distinct value for the setting, then the {@link #getStatus()} value should represent a summary of 77946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * the state across all of the accounts and {@code settingsActivity} should display the value for 78946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * each account. 7997b8ae429ec64003e258764db12ea69d4e978f6dZack Rusin * 80c789b981b244333cfc903bcd1e2fefc010500013Roland Scheidegger * Apps that violate these guidelines will be taken down from the Google Play Store and/or flagged 81b3538d356316b282d6408f710f2df5f06bf315f5Brian Paul * as malware. 8297b8ae429ec64003e258764db12ea69d4e978f6dZack Rusin */ 83946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca// TODO: is there a public list of supported locales? 846d28bf917fb1d741d90fd3f05c22769376021fcaChia-I Wu// TODO: is there a public list of guidelines for settings text? 85946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonsecapublic abstract class SettingInjectorService extends IntentService { 86b3538d356316b282d6408f710f2df5f06bf315f5Brian Paul 87b3538d356316b282d6408f710f2df5f06bf315f5Brian Paul /** 88b3538d356316b282d6408f710f2df5f06bf315f5Brian Paul * Name of the bundle key for the string specifying the status of the setting (e.g., "ON" or 89946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * "OFF"). 90946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * 9177b26564c3f0395bf3e744abbf6d0e7aa9d2c8daDave Airlie * @hide 9277b26564c3f0395bf3e744abbf6d0e7aa9d2c8daDave Airlie */ 9377b26564c3f0395bf3e744abbf6d0e7aa9d2c8daDave Airlie public static final String STATUS_KEY = "status"; 9477b26564c3f0395bf3e744abbf6d0e7aa9d2c8daDave Airlie 9580ee4a407a2668f6a6a410c3e56ae9910510f773Zack Rusin /** 9680ee4a407a2668f6a6a410c3e56ae9910510f773Zack Rusin * Name of the bundle key for the string specifying whether the setting is currently enabled. 9780ee4a407a2668f6a6a410c3e56ae9910510f773Zack Rusin * 982e4da1f59444c550e4b1e31dd5cfec39d7ef2a26Roland Scheidegger * @hide 99946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca */ 1002e4da1f59444c550e4b1e31dd5cfec39d7ef2a26Roland Scheidegger public static final String ENABLED_KEY = "enabled"; 101946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca 102946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca /** 103946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * Name of the intent key used to specify the messenger 104946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * 105946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * @hide 106946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca */ 10757d84d9ca4a645ca326b66ff3b82bee0db18ac97Hui Qi Tay public static final String MESSENGER_KEY = "messenger"; 1087f0dc5ea1bb330c6589125baf4017c51a14dce8eHui Qi Tay 1098e3a76791f208e67392b7b7a2e63eca32945ac7bRoland Scheidegger private final String mLogTag; 1107f0dc5ea1bb330c6589125baf4017c51a14dce8eHui Qi Tay 1117f0dc5ea1bb330c6589125baf4017c51a14dce8eHui Qi Tay /** 1128e3a76791f208e67392b7b7a2e63eca32945ac7bRoland Scheidegger * Constructor. 1137f0dc5ea1bb330c6589125baf4017c51a14dce8eHui Qi Tay * 11457d84d9ca4a645ca326b66ff3b82bee0db18ac97Hui Qi Tay * @param logTag used for logging, must be less than 23 characters 1158e3a76791f208e67392b7b7a2e63eca32945ac7bRoland Scheidegger */ 116946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca public SettingInjectorService(String logTag) { 11797b8ae429ec64003e258764db12ea69d4e978f6dZack Rusin super(logTag); 1188e3a76791f208e67392b7b7a2e63eca32945ac7bRoland Scheidegger 11997b8ae429ec64003e258764db12ea69d4e978f6dZack Rusin // Fast fail if log tag is too long 120d8146f240e628e70d4c07f7e805a179f70c36e23Roland Scheidegger Log.isLoggable(logTag, Log.WARN); 1218e3a76791f208e67392b7b7a2e63eca32945ac7bRoland Scheidegger 122d8146f240e628e70d4c07f7e805a179f70c36e23Roland Scheidegger mLogTag = logTag; 123d6b3a193d4d525c5048ebf793e6a63fd98f92d64Zack Rusin } 1248e3a76791f208e67392b7b7a2e63eca32945ac7bRoland Scheidegger 125af7ba989fb5a39925a0a1261ed281fe7f48a16cfRoland Scheidegger @Override 126f9e2c24326869542c9b43220f63dd9841c6de38fMatthew McClure final protected void onHandleIntent(Intent intent) { 127f9e2c24326869542c9b43220f63dd9841c6de38fMatthew McClure // Get messenger first to ensure intent doesn't get messed with (in case we later decide 128f9e2c24326869542c9b43220f63dd9841c6de38fMatthew McClure // to pass intent into getStatus()) 129f9e2c24326869542c9b43220f63dd9841c6de38fMatthew McClure Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY); 1306b65685def525a8023ee936e82e53af2bc4e38b2Keith Whitwell 1313160cbabccf1f7d8bdf344242507b9c3082f15c6José Fonseca Status status = getStatus(); 1325b4c43d98556c5a4806757513bcb196a724518c5Keith Whitwell 1336b65685def525a8023ee936e82e53af2bc4e38b2Keith Whitwell // Send the status back to the caller via the messenger 134946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca Message message = Message.obtain(); 135946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca Bundle bundle = new Bundle(); 13689498d01531cd515c769e570bf799c39fbafc8fbKeith Whitwell bundle.putString(STATUS_KEY, status.summary); 137ff2d192ec50e6a8cbaa8f14bca989fe5b61a3c46Marek Olšák bundle.putBoolean(ENABLED_KEY, status.enabled); 138ff2d192ec50e6a8cbaa8f14bca989fe5b61a3c46Marek Olšák message.setData(bundle); 1390f55a95b2faa16cc008143c53a8b82b19c5d750aKeith Whitwell 1406b65685def525a8023ee936e82e53af2bc4e38b2Keith Whitwell if (Log.isLoggable(mLogTag, Log.DEBUG)) { 141c022e15d1e56ba3a9c6b74eef6556d6063e2e322José Fonseca Log.d(mLogTag, 142efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul "received " + intent + " and " + status + ", sending message: " + message); 14317c9d7eea7b3365c59455a731fcb81e69bb86ce2Roland Scheidegger } 14417c9d7eea7b3365c59455a731fcb81e69bb86ce2Roland Scheidegger try { 1456cf7245f6938e27c9b8a1742f27659aec017bbdcJosé Fonseca messenger.send(message); 1465b4c43d98556c5a4806757513bcb196a724518c5Keith Whitwell } catch (RemoteException e) { 1475b4c43d98556c5a4806757513bcb196a724518c5Keith Whitwell Log.e(mLogTag, "", e); 1485b4c43d98556c5a4806757513bcb196a724518c5Keith Whitwell } 149ab14915dce41b26faabba878446b0ec0c8734434Dave Airlie } 150ab14915dce41b26faabba878446b0ec0c8734434Dave Airlie 151ab14915dce41b26faabba878446b0ec0c8734434Dave Airlie /** 152ab14915dce41b26faabba878446b0ec0c8734434Dave Airlie * Reads the status of the setting. 153793e8e3d7ed816cc9a066245dde798afdcf8b581Roland Scheidegger */ 15483c62597fc8eb38bf274fa1a3ca03c6adafb4bf9Mathias Fröhlich protected abstract Status getStatus(); 15583c62597fc8eb38bf274fa1a3ca03c6adafb4bf9Mathias Fröhlich 15683c62597fc8eb38bf274fa1a3ca03c6adafb4bf9Mathias Fröhlich /** 157946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca * Dynamic characteristics of an injected location setting. 158946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca */ 159946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca public static final class Status { 1607f41f5447c8f9113c8956901e1c5fff6081ecd94Keith Whitwell 1610fc21ecfc0891d239f20bf7724e51bc75503570cMarek Olšák public final String summary; 1620fc21ecfc0891d239f20bf7724e51bc75503570cMarek Olšák 1637f41f5447c8f9113c8956901e1c5fff6081ecd94Keith Whitwell public final boolean enabled; 1640b7d48cbad86eaac21fce3793da41b46db8be3b4Marek Olšák 1650b7d48cbad86eaac21fce3793da41b46db8be3b4Marek Olšák /** 1660b7d48cbad86eaac21fce3793da41b46db8be3b4Marek Olšák * Constructor. 1670b7d48cbad86eaac21fce3793da41b46db8be3b4Marek Olšák * 1681d28650b551bb516fdff99d0a196c1fa133f6d05Roland Scheidegger * @param summary the {@link Preference#getSummary()} value 1690b7d48cbad86eaac21fce3793da41b46db8be3b4Marek Olšák * @param enabled the {@link Preference#isEnabled()} value 1707f41f5447c8f9113c8956901e1c5fff6081ecd94Keith Whitwell */ 171a2a1a5805fd617e7f3cc8be44dd79b50da07ebb9Ilia Mirkin public Status(String summary, boolean enabled) { 172946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca this.summary = summary; 173946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca this.enabled = enabled; 174946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca } 175946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca 176946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca @Override 177c9a5930fe45a0a0299769bd2b672ca516d1bf39eJosé Fonseca public String toString() { 178946f432a08112148d743eb9faf6b27bb8cc7fa76José Fonseca return "Status{summary='" + summary + '\'' + ", enabled=" + enabled + '}'; 179 } 180 } 181} 182