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