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