CarrierText.java revision 726fb28248a2d0d13123ec808a1e28c813aa7fb5
1/*
2 * Copyright (C) 2012 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 com.android.keyguard;
18
19import java.util.List;
20import java.util.Locale;
21
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.res.TypedArray;
26import android.net.ConnectivityManager;
27import android.telephony.SubscriptionInfo;
28import android.text.TextUtils;
29import android.text.method.SingleLineTransformationMethod;
30import android.util.AttributeSet;
31import android.util.Log;
32import android.view.View;
33import android.widget.TextView;
34
35import com.android.internal.telephony.IccCardConstants;
36import com.android.internal.telephony.IccCardConstants.State;
37import com.android.internal.telephony.TelephonyIntents;
38import com.android.settingslib.WirelessUtils;
39
40public class CarrierText extends TextView {
41    private static final boolean DEBUG = KeyguardConstants.DEBUG;
42    private static final String TAG = "CarrierText";
43
44    private static CharSequence mSeparator;
45
46    private final boolean mIsEmergencyCallCapable;
47
48    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
49
50    private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
51        @Override
52        public void onRefreshCarrierInfo() {
53            updateCarrierText();
54        }
55
56        public void onScreenTurnedOff(int why) {
57            setSelected(false);
58        };
59
60        public void onScreenTurnedOn() {
61            setSelected(true);
62        };
63    };
64    /**
65     * The status of this lock screen. Primarily used for widgets on LockScreen.
66     */
67    private static enum StatusMode {
68        Normal, // Normal case (sim card present, it's not locked)
69        NetworkLocked, // SIM card is 'network locked'.
70        SimMissing, // SIM card is missing.
71        SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
72        SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
73        SimLocked, // SIM card is currently locked
74        SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
75        SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM.
76    }
77
78    public CarrierText(Context context) {
79        this(context, null);
80    }
81
82    public CarrierText(Context context, AttributeSet attrs) {
83        super(context, attrs);
84        mIsEmergencyCallCapable = context.getResources().getBoolean(
85                com.android.internal.R.bool.config_voice_capable);
86        boolean useAllCaps;
87        TypedArray a = context.getTheme().obtainStyledAttributes(
88                attrs, R.styleable.CarrierText, 0, 0);
89        try {
90            useAllCaps = a.getBoolean(R.styleable.CarrierText_allCaps, false);
91        } finally {
92            a.recycle();
93        }
94        setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
95    }
96
97    protected void updateCarrierText() {
98        boolean allSimsMissing = true;
99        CharSequence displayText = null;
100
101        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
102        final int N = subs.size();
103        if (DEBUG) Log.d(TAG, "updateCarrierText(): " + N);
104        for (int i = 0; i < N; i++) {
105            State simState = mKeyguardUpdateMonitor.getSimState(subs.get(i).getSubscriptionId());
106            CharSequence carrierName = subs.get(i).getCarrierName();
107            CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
108            if (DEBUG) Log.d(TAG, "Handling " + simState + " " + carrierName);
109            if (carrierTextForSimState != null) {
110                allSimsMissing = false;
111                displayText = concatenate(displayText, carrierTextForSimState);
112            }
113        }
114        if (allSimsMissing) {
115            if (N != 0) {
116                // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
117                // This depends on mPlmn containing the text "Emergency calls only" when the radio
118                // has some connectivity. Otherwise, it should be null or empty and just show
119                // "No SIM card"
120                // Grab the first subscripton, because they all should contain the emergency text,
121                // described above.
122                displayText =  makeCarrierStringOnEmergencyCapable(
123                        getContext().getText(R.string.keyguard_missing_sim_message_short),
124                        subs.get(0).getCarrierName());
125            } else {
126                // We don't have a SubscriptionInfo to get the emergency calls only from.
127                // Grab it from the old sticky broadcast if possible instead. We can use it
128                // here because no subscriptions are active, so we don't have
129                // to worry about MSIM clashing.
130                CharSequence text =
131                        getContext().getText(com.android.internal.R.string.emergency_calls_only);
132                Intent i = getContext().registerReceiver(null,
133                        new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
134                if (i != null) {
135                    String spn = "";
136                    String plmn = "";
137                    if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
138                        spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN);
139                    }
140                    if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
141                        plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
142                    }
143                    if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
144                    text = concatenate(plmn, spn);
145                }
146                displayText =  makeCarrierStringOnEmergencyCapable(
147                        getContext().getText(R.string.keyguard_missing_sim_message_short), text);
148            }
149        }
150        if (WirelessUtils.isAirplaneModeOn(mContext)) {
151            displayText = getContext().getString(R.string.airplane_mode);
152        }
153        setText(displayText);
154    }
155
156    @Override
157    protected void onFinishInflate() {
158        super.onFinishInflate();
159        mSeparator = getResources().getString(
160                com.android.internal.R.string.kg_text_message_separator);
161        final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
162        setSelected(screenOn); // Allow marquee to work.
163    }
164
165    @Override
166    protected void onAttachedToWindow() {
167        super.onAttachedToWindow();
168        if (ConnectivityManager.from(mContext).isNetworkSupported(
169                ConnectivityManager.TYPE_MOBILE)) {
170            mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
171            mKeyguardUpdateMonitor.registerCallback(mCallback);
172        } else {
173            // Don't listen and clear out the text when the device isn't a phone.
174            mKeyguardUpdateMonitor = null;
175            setText("");
176        }
177    }
178
179    @Override
180    protected void onDetachedFromWindow() {
181        super.onDetachedFromWindow();
182        if (mKeyguardUpdateMonitor != null) {
183            mKeyguardUpdateMonitor.removeCallback(mCallback);
184        }
185    }
186
187    /**
188     * Top-level function for creating carrier text. Makes text based on simState, PLMN
189     * and SPN as well as device capabilities, such as being emergency call capable.
190     *
191     * @param simState
192     * @param text
193     * @param spn
194     * @return Carrier text if not in missing state, null otherwise.
195     */
196    private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
197            CharSequence text) {
198        CharSequence carrierText = null;
199        StatusMode status = getStatusForIccState(simState);
200        switch (status) {
201            case Normal:
202                carrierText = text;
203                break;
204
205            case SimNotReady:
206                // Null is reserved for denoting missing, in this case we have nothing to display.
207                carrierText = ""; // nothing to display yet.
208                break;
209
210            case NetworkLocked:
211                carrierText = makeCarrierStringOnEmergencyCapable(
212                        mContext.getText(R.string.keyguard_network_locked_message), text);
213                break;
214
215            case SimMissing:
216                carrierText = null;
217                break;
218
219            case SimPermDisabled:
220                carrierText = getContext().getText(
221                        R.string.keyguard_permanent_disabled_sim_message_short);
222                break;
223
224            case SimMissingLocked:
225                carrierText = null;
226                break;
227
228            case SimLocked:
229                carrierText = makeCarrierStringOnEmergencyCapable(
230                        getContext().getText(R.string.keyguard_sim_locked_message),
231                        text);
232                break;
233
234            case SimPukLocked:
235                carrierText = makeCarrierStringOnEmergencyCapable(
236                        getContext().getText(R.string.keyguard_sim_puk_locked_message),
237                        text);
238                break;
239        }
240
241        return carrierText;
242    }
243
244    /*
245     * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
246     */
247    private CharSequence makeCarrierStringOnEmergencyCapable(
248            CharSequence simMessage, CharSequence emergencyCallMessage) {
249        if (mIsEmergencyCallCapable) {
250            return concatenate(simMessage, emergencyCallMessage);
251        }
252        return simMessage;
253    }
254
255    /**
256     * Determine the current status of the lock screen given the SIM state and other stuff.
257     */
258    private StatusMode getStatusForIccState(IccCardConstants.State simState) {
259        // Since reading the SIM may take a while, we assume it is present until told otherwise.
260        if (simState == null) {
261            return StatusMode.Normal;
262        }
263
264        final boolean missingAndNotProvisioned =
265                !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
266                && (simState == IccCardConstants.State.ABSENT ||
267                        simState == IccCardConstants.State.PERM_DISABLED);
268
269        // Assume we're NETWORK_LOCKED if not provisioned
270        simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
271        switch (simState) {
272            case ABSENT:
273                return StatusMode.SimMissing;
274            case NETWORK_LOCKED:
275                return StatusMode.SimMissingLocked;
276            case NOT_READY:
277                return StatusMode.SimNotReady;
278            case PIN_REQUIRED:
279                return StatusMode.SimLocked;
280            case PUK_REQUIRED:
281                return StatusMode.SimPukLocked;
282            case READY:
283                return StatusMode.Normal;
284            case PERM_DISABLED:
285                return StatusMode.SimPermDisabled;
286            case UNKNOWN:
287                return StatusMode.SimMissing;
288        }
289        return StatusMode.SimMissing;
290    }
291
292    private static CharSequence concatenate(CharSequence plmn, CharSequence spn) {
293        final boolean plmnValid = !TextUtils.isEmpty(plmn);
294        final boolean spnValid = !TextUtils.isEmpty(spn);
295        if (plmnValid && spnValid) {
296            if (plmn.equals(spn)) {
297                return plmn;
298            } else {
299                return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
300            }
301        } else if (plmnValid) {
302            return plmn;
303        } else if (spnValid) {
304            return spn;
305        } else {
306            return "";
307        }
308    }
309
310    private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
311            String plmn, String spn) {
312        int carrierHelpTextId = 0;
313        StatusMode status = getStatusForIccState(simState);
314        switch (status) {
315            case NetworkLocked:
316                carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
317                break;
318
319            case SimMissing:
320                carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
321                break;
322
323            case SimPermDisabled:
324                carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
325                break;
326
327            case SimMissingLocked:
328                carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
329                break;
330
331            case Normal:
332            case SimLocked:
333            case SimPukLocked:
334                break;
335        }
336
337        return mContext.getText(carrierHelpTextId);
338    }
339
340    private class CarrierTextTransformationMethod extends SingleLineTransformationMethod {
341        private final Locale mLocale;
342        private final boolean mAllCaps;
343
344        public CarrierTextTransformationMethod(Context context, boolean allCaps) {
345            mLocale = context.getResources().getConfiguration().locale;
346            mAllCaps = allCaps;
347        }
348
349        @Override
350        public CharSequence getTransformation(CharSequence source, View view) {
351            source = super.getTransformation(source, view);
352
353            if (mAllCaps && source != null) {
354                source = source.toString().toUpperCase(mLocale);
355            }
356
357            return source;
358        }
359    }
360}
361