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