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.internal.policy.impl.keyguard;
18
19import android.content.Context;
20import android.text.TextUtils;
21import android.util.AttributeSet;
22import android.widget.TextView;
23
24import com.android.internal.R;
25import com.android.internal.telephony.IccCardConstants;
26import com.android.internal.telephony.IccCardConstants.State;
27import com.android.internal.widget.LockPatternUtils;
28
29public class CarrierText extends TextView {
30    private static CharSequence mSeparator;
31
32    private LockPatternUtils mLockPatternUtils;
33
34    private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
35        private CharSequence mPlmn;
36        private CharSequence mSpn;
37        private State mSimState;
38
39        @Override
40        public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
41            mPlmn = plmn;
42            mSpn = spn;
43            updateCarrierText(mSimState, mPlmn, mSpn);
44        }
45
46        @Override
47        public void onSimStateChanged(IccCardConstants.State simState) {
48            mSimState = simState;
49            updateCarrierText(mSimState, mPlmn, mSpn);
50        }
51    };
52    /**
53     * The status of this lock screen. Primarily used for widgets on LockScreen.
54     */
55    private static enum StatusMode {
56        Normal, // Normal case (sim card present, it's not locked)
57        NetworkLocked, // SIM card is 'network locked'.
58        SimMissing, // SIM card is missing.
59        SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
60        SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
61        SimLocked, // SIM card is currently locked
62        SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
63        SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM.
64    }
65
66    public CarrierText(Context context) {
67        this(context, null);
68    }
69
70    public CarrierText(Context context, AttributeSet attrs) {
71        super(context, attrs);
72        mLockPatternUtils = new LockPatternUtils(mContext);
73    }
74
75    protected void updateCarrierText(State simState, CharSequence plmn, CharSequence spn) {
76        CharSequence text = getCarrierTextForSimState(simState, plmn, spn);
77        if (KeyguardViewManager.USE_UPPER_CASE) {
78            setText(text != null ? text.toString().toUpperCase() : null);
79        } else {
80            setText(text);
81        }
82    }
83
84    @Override
85    protected void onFinishInflate() {
86        super.onFinishInflate();
87        mSeparator = getResources().getString(R.string.kg_text_message_separator);
88        setSelected(true); // Allow marquee to work.
89    }
90
91    @Override
92    protected void onAttachedToWindow() {
93        super.onAttachedToWindow();
94        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback);
95    }
96
97    @Override
98    protected void onDetachedFromWindow() {
99        super.onDetachedFromWindow();
100        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
101    }
102
103    /**
104     * Top-level function for creating carrier text. Makes text based on simState, PLMN
105     * and SPN as well as device capabilities, such as being emergency call capable.
106     *
107     * @param simState
108     * @param plmn
109     * @param spn
110     * @return
111     */
112    private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
113            CharSequence plmn, CharSequence spn) {
114        CharSequence carrierText = null;
115        StatusMode status = getStatusForIccState(simState);
116        switch (status) {
117            case Normal:
118                carrierText = concatenate(plmn, spn);
119                break;
120
121            case SimNotReady:
122                carrierText = null; // nothing to display yet.
123                break;
124
125            case NetworkLocked:
126                carrierText = makeCarrierStringOnEmergencyCapable(
127                        mContext.getText(R.string.lockscreen_network_locked_message), plmn);
128                break;
129
130            case SimMissing:
131                // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
132                // This depends on mPlmn containing the text "Emergency calls only" when the radio
133                // has some connectivity. Otherwise, it should be null or empty and just show
134                // "No SIM card"
135                carrierText =  makeCarrierStringOnEmergencyCapable(
136                        getContext().getText(R.string.lockscreen_missing_sim_message_short),
137                        plmn);
138                break;
139
140            case SimPermDisabled:
141                carrierText = getContext().getText(
142                        R.string.lockscreen_permanent_disabled_sim_message_short);
143                break;
144
145            case SimMissingLocked:
146                carrierText =  makeCarrierStringOnEmergencyCapable(
147                        getContext().getText(R.string.lockscreen_missing_sim_message_short),
148                        plmn);
149                break;
150
151            case SimLocked:
152                carrierText = makeCarrierStringOnEmergencyCapable(
153                        getContext().getText(R.string.lockscreen_sim_locked_message),
154                        plmn);
155                break;
156
157            case SimPukLocked:
158                carrierText = makeCarrierStringOnEmergencyCapable(
159                        getContext().getText(R.string.lockscreen_sim_puk_locked_message),
160                        plmn);
161                break;
162        }
163
164        return carrierText;
165    }
166
167    /*
168     * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
169     */
170    private CharSequence makeCarrierStringOnEmergencyCapable(
171            CharSequence simMessage, CharSequence emergencyCallMessage) {
172        if (mLockPatternUtils.isEmergencyCallCapable()) {
173            return concatenate(simMessage, emergencyCallMessage);
174        }
175        return simMessage;
176    }
177
178    /**
179     * Determine the current status of the lock screen given the SIM state and other stuff.
180     */
181    private StatusMode getStatusForIccState(IccCardConstants.State simState) {
182        // Since reading the SIM may take a while, we assume it is present until told otherwise.
183        if (simState == null) {
184            return StatusMode.Normal;
185        }
186
187        final boolean missingAndNotProvisioned =
188                !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
189                && (simState == IccCardConstants.State.ABSENT ||
190                        simState == IccCardConstants.State.PERM_DISABLED);
191
192        // Assume we're NETWORK_LOCKED if not provisioned
193        simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
194        switch (simState) {
195            case ABSENT:
196                return StatusMode.SimMissing;
197            case NETWORK_LOCKED:
198                return StatusMode.SimMissingLocked;
199            case NOT_READY:
200                return StatusMode.SimNotReady;
201            case PIN_REQUIRED:
202                return StatusMode.SimLocked;
203            case PUK_REQUIRED:
204                return StatusMode.SimPukLocked;
205            case READY:
206                return StatusMode.Normal;
207            case PERM_DISABLED:
208                return StatusMode.SimPermDisabled;
209            case UNKNOWN:
210                return StatusMode.SimMissing;
211        }
212        return StatusMode.SimMissing;
213    }
214
215    private static CharSequence concatenate(CharSequence plmn, CharSequence spn) {
216        final boolean plmnValid = !TextUtils.isEmpty(plmn);
217        final boolean spnValid = !TextUtils.isEmpty(spn);
218        if (plmnValid && spnValid) {
219            return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
220        } else if (plmnValid) {
221            return plmn;
222        } else if (spnValid) {
223            return spn;
224        } else {
225            return "";
226        }
227    }
228
229    private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
230            String plmn, String spn) {
231        int carrierHelpTextId = 0;
232        StatusMode status = getStatusForIccState(simState);
233        switch (status) {
234            case NetworkLocked:
235                carrierHelpTextId = R.string.lockscreen_instructions_when_pattern_disabled;
236                break;
237
238            case SimMissing:
239                carrierHelpTextId = R.string.lockscreen_missing_sim_instructions_long;
240                break;
241
242            case SimPermDisabled:
243                carrierHelpTextId = R.string.lockscreen_permanent_disabled_sim_instructions;
244                break;
245
246            case SimMissingLocked:
247                carrierHelpTextId = R.string.lockscreen_missing_sim_instructions;
248                break;
249
250            case Normal:
251            case SimLocked:
252            case SimPukLocked:
253                break;
254        }
255
256        return mContext.getText(carrierHelpTextId);
257    }
258}
259