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