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