1/* 2 * Copyright (C) 2011 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; 18 19import com.android.internal.R; 20import com.android.internal.telephony.IccCard; 21import com.android.internal.telephony.IccCard.State; 22import com.android.internal.widget.DigitalClock; 23import com.android.internal.widget.LockPatternUtils; 24import com.android.internal.widget.TransportControlView; 25import com.android.internal.policy.impl.KeyguardUpdateMonitor.InfoCallbackImpl; 26import com.android.internal.policy.impl.KeyguardUpdateMonitor.SimStateCallback; 27 28import java.util.ArrayList; 29import java.util.Date; 30 31import libcore.util.MutableInt; 32 33import android.content.ContentResolver; 34import android.content.Context; 35import android.provider.Settings; 36import android.text.TextUtils; 37import android.text.format.DateFormat; 38import android.util.Log; 39import android.view.View; 40import android.view.View.OnClickListener; 41import android.widget.Button; 42import android.widget.TextView; 43 44/*** 45 * Manages a number of views inside of LockScreen layouts. See below for a list of widgets 46 * 47 */ 48class KeyguardStatusViewManager implements OnClickListener { 49 private static final boolean DEBUG = false; 50 private static final String TAG = "KeyguardStatusView"; 51 52 public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock; 53 public static final int ALARM_ICON = R.drawable.ic_lock_idle_alarm; 54 public static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging; 55 public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery; 56 private static final long INSTRUCTION_RESET_DELAY = 2000; // time until instruction text resets 57 58 private static final int INSTRUCTION_TEXT = 10; 59 private static final int CARRIER_TEXT = 11; 60 private static final int CARRIER_HELP_TEXT = 12; 61 private static final int HELP_MESSAGE_TEXT = 13; 62 private static final int OWNER_INFO = 14; 63 private static final int BATTERY_INFO = 15; 64 65 private StatusMode mStatus; 66 private String mDateFormatString; 67 private TransientTextManager mTransientTextManager; 68 69 // Views that this class controls. 70 // NOTE: These may be null in some LockScreen screens and should protect from NPE 71 private TextView mCarrierView; 72 private TextView mDateView; 73 private TextView mStatus1View; 74 private TextView mOwnerInfoView; 75 private TextView mAlarmStatusView; 76 private TransportControlView mTransportView; 77 78 // Top-level container view for above views 79 private View mContainer; 80 81 // are we showing battery information? 82 private boolean mShowingBatteryInfo = false; 83 84 // last known plugged in state 85 private boolean mPluggedIn = false; 86 87 // last known battery level 88 private int mBatteryLevel = 100; 89 90 // last known SIM state 91 protected State mSimState; 92 93 private LockPatternUtils mLockPatternUtils; 94 private KeyguardUpdateMonitor mUpdateMonitor; 95 private Button mEmergencyCallButton; 96 private boolean mEmergencyButtonEnabledBecauseSimLocked; 97 98 // Shadowed text values 99 private CharSequence mCarrierText; 100 private CharSequence mCarrierHelpText; 101 private String mHelpMessageText; 102 private String mInstructionText; 103 private CharSequence mOwnerInfoText; 104 private boolean mShowingStatus; 105 private KeyguardScreenCallback mCallback; 106 private final boolean mEmergencyCallButtonEnabledInScreen; 107 private CharSequence mPlmn; 108 private CharSequence mSpn; 109 protected int mPhoneState; 110 private DigitalClock mDigitalClock; 111 112 private class TransientTextManager { 113 private TextView mTextView; 114 private class Data { 115 final int icon; 116 final CharSequence text; 117 Data(CharSequence t, int i) { 118 text = t; 119 icon = i; 120 } 121 }; 122 private ArrayList<Data> mMessages = new ArrayList<Data>(5); 123 124 TransientTextManager(TextView textView) { 125 mTextView = textView; 126 } 127 128 /* Show given message with icon for up to duration ms. Newer messages override older ones. 129 * The most recent message with the longest duration is shown as messages expire until 130 * nothing is left, in which case the text/icon is defined by a call to 131 * getAltTextMessage() */ 132 void post(final CharSequence message, final int icon, long duration) { 133 if (mTextView == null) { 134 return; 135 } 136 mTextView.setText(message); 137 mTextView.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0); 138 final Data data = new Data(message, icon); 139 mContainer.postDelayed(new Runnable() { 140 public void run() { 141 mMessages.remove(data); 142 int last = mMessages.size() - 1; 143 final CharSequence lastText; 144 final int lastIcon; 145 if (last > 0) { 146 final Data oldData = mMessages.get(last); 147 lastText = oldData.text; 148 lastIcon = oldData.icon; 149 } else { 150 final MutableInt tmpIcon = new MutableInt(0); 151 lastText = getAltTextMessage(tmpIcon); 152 lastIcon = tmpIcon.value; 153 } 154 mTextView.setText(lastText); 155 mTextView.setCompoundDrawablesWithIntrinsicBounds(lastIcon, 0, 0, 0); 156 } 157 }, duration); 158 } 159 }; 160 161 /** 162 * 163 * @param view the containing view of all widgets 164 * @param updateMonitor the update monitor to use 165 * @param lockPatternUtils lock pattern util object 166 * @param callback used to invoke emergency dialer 167 * @param emergencyButtonEnabledInScreen whether emergency button is enabled by default 168 */ 169 public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor, 170 LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback, 171 boolean emergencyButtonEnabledInScreen) { 172 if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()"); 173 mContainer = view; 174 mDateFormatString = getContext().getString(R.string.abbrev_wday_month_day_no_year); 175 mLockPatternUtils = lockPatternUtils; 176 mUpdateMonitor = updateMonitor; 177 mCallback = callback; 178 179 mCarrierView = (TextView) findViewById(R.id.carrier); 180 mDateView = (TextView) findViewById(R.id.date); 181 mStatus1View = (TextView) findViewById(R.id.status1); 182 mAlarmStatusView = (TextView) findViewById(R.id.alarm_status); 183 mOwnerInfoView = (TextView) findViewById(R.id.propertyOf); 184 mTransportView = (TransportControlView) findViewById(R.id.transport); 185 mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton); 186 mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen; 187 mDigitalClock = (DigitalClock) findViewById(R.id.time); 188 189 // Hide transport control view until we know we need to show it. 190 if (mTransportView != null) { 191 mTransportView.setVisibility(View.GONE); 192 } 193 194 if (mEmergencyCallButton != null) { 195 mEmergencyCallButton.setText(R.string.lockscreen_emergency_call); 196 mEmergencyCallButton.setOnClickListener(this); 197 mEmergencyCallButton.setFocusable(false); // touch only! 198 } 199 200 mTransientTextManager = new TransientTextManager(mCarrierView); 201 202 mUpdateMonitor.registerInfoCallback(mInfoCallback); 203 mUpdateMonitor.registerSimStateCallback(mSimStateCallback); 204 205 resetStatusInfo(); 206 refreshDate(); 207 updateOwnerInfo(); 208 209 // Required to get Marquee to work. 210 final View scrollableViews[] = { mCarrierView, mDateView, mStatus1View, mOwnerInfoView, 211 mAlarmStatusView }; 212 for (View v : scrollableViews) { 213 if (v != null) { 214 v.setSelected(true); 215 } 216 } 217 } 218 219 private boolean inWidgetMode() { 220 return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE; 221 } 222 223 void setInstructionText(String string) { 224 mInstructionText = string; 225 update(INSTRUCTION_TEXT, string); 226 } 227 228 void setCarrierText(CharSequence string) { 229 mCarrierText = string; 230 update(CARRIER_TEXT, string); 231 } 232 233 void setOwnerInfo(CharSequence string) { 234 mOwnerInfoText = string; 235 update(OWNER_INFO, string); 236 } 237 238 /** 239 * Sets the carrier help text message, if view is present. Carrier help text messages are 240 * typically for help dealing with SIMS and connectivity. 241 * 242 * @param resId resource id of the message 243 */ 244 public void setCarrierHelpText(int resId) { 245 mCarrierHelpText = getText(resId); 246 update(CARRIER_HELP_TEXT, mCarrierHelpText); 247 } 248 249 private CharSequence getText(int resId) { 250 return resId == 0 ? null : getContext().getText(resId); 251 } 252 253 /** 254 * Unlock help message. This is typically for help with unlock widgets, e.g. "wrong password" 255 * or "try again." 256 * 257 * @param textResId 258 * @param lockIcon 259 */ 260 public void setHelpMessage(int textResId, int lockIcon) { 261 final CharSequence tmp = getText(textResId); 262 mHelpMessageText = tmp == null ? null : tmp.toString(); 263 update(HELP_MESSAGE_TEXT, mHelpMessageText); 264 } 265 266 private void update(int what, CharSequence string) { 267 if (inWidgetMode()) { 268 if (DEBUG) Log.v(TAG, "inWidgetMode() is true"); 269 // Use Transient text for messages shown while widget is shown. 270 switch (what) { 271 case INSTRUCTION_TEXT: 272 case CARRIER_HELP_TEXT: 273 case HELP_MESSAGE_TEXT: 274 case BATTERY_INFO: 275 mTransientTextManager.post(string, 0, INSTRUCTION_RESET_DELAY); 276 break; 277 278 case OWNER_INFO: 279 case CARRIER_TEXT: 280 default: 281 if (DEBUG) Log.w(TAG, "Not showing message id " + what + ", str=" + string); 282 } 283 } else { 284 updateStatusLines(mShowingStatus); 285 } 286 } 287 288 public void onPause() { 289 if (DEBUG) Log.v(TAG, "onPause()"); 290 mUpdateMonitor.removeCallback(mInfoCallback); 291 mUpdateMonitor.removeCallback(mSimStateCallback); 292 } 293 294 /** {@inheritDoc} */ 295 public void onResume() { 296 if (DEBUG) Log.v(TAG, "onResume()"); 297 298 // First update the clock, if present. 299 if (mDigitalClock != null) { 300 mDigitalClock.updateTime(); 301 } 302 303 mUpdateMonitor.registerInfoCallback(mInfoCallback); 304 mUpdateMonitor.registerSimStateCallback(mSimStateCallback); 305 resetStatusInfo(); 306 // Issue the biometric unlock failure message in a centralized place 307 // TODO: we either need to make the Face Unlock multiple failures string a more general 308 // 'biometric unlock' or have each biometric unlock handle this on their own. 309 if (mUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) { 310 setInstructionText(getContext().getString(R.string.faceunlock_multiple_failures)); 311 } 312 } 313 314 void resetStatusInfo() { 315 mInstructionText = null; 316 mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo(); 317 mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); 318 mBatteryLevel = mUpdateMonitor.getBatteryLevel(); 319 updateStatusLines(true); 320 } 321 322 /** 323 * Update the status lines based on these rules: 324 * AlarmStatus: Alarm state always gets it's own line. 325 * Status1 is shared between help, battery status and generic unlock instructions, 326 * prioritized in that order. 327 * @param showStatusLines status lines are shown if true 328 */ 329 void updateStatusLines(boolean showStatusLines) { 330 if (DEBUG) Log.v(TAG, "updateStatusLines(" + showStatusLines + ")"); 331 mShowingStatus = showStatusLines; 332 updateAlarmInfo(); 333 updateOwnerInfo(); 334 updateStatus1(); 335 updateCarrierText(); 336 } 337 338 private void updateAlarmInfo() { 339 if (mAlarmStatusView != null) { 340 String nextAlarm = mLockPatternUtils.getNextAlarm(); 341 boolean showAlarm = mShowingStatus && !TextUtils.isEmpty(nextAlarm); 342 mAlarmStatusView.setText(nextAlarm); 343 mAlarmStatusView.setCompoundDrawablesWithIntrinsicBounds(ALARM_ICON, 0, 0, 0); 344 mAlarmStatusView.setVisibility(showAlarm ? View.VISIBLE : View.GONE); 345 } 346 } 347 348 private void updateOwnerInfo() { 349 final ContentResolver res = getContext().getContentResolver(); 350 final boolean ownerInfoEnabled = Settings.Secure.getInt(res, 351 Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1) != 0; 352 mOwnerInfoText = ownerInfoEnabled ? 353 Settings.Secure.getString(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO) : null; 354 if (mOwnerInfoView != null) { 355 mOwnerInfoView.setText(mOwnerInfoText); 356 mOwnerInfoView.setVisibility(TextUtils.isEmpty(mOwnerInfoText) ? View.GONE:View.VISIBLE); 357 } 358 } 359 360 private void updateStatus1() { 361 if (mStatus1View != null) { 362 MutableInt icon = new MutableInt(0); 363 CharSequence string = getPriorityTextMessage(icon); 364 mStatus1View.setText(string); 365 mStatus1View.setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0); 366 mStatus1View.setVisibility(mShowingStatus ? View.VISIBLE : View.INVISIBLE); 367 } 368 } 369 370 private void updateCarrierText() { 371 if (!inWidgetMode() && mCarrierView != null) { 372 mCarrierView.setText(mCarrierText); 373 } 374 } 375 376 private CharSequence getAltTextMessage(MutableInt icon) { 377 // If we have replaced the status area with a single widget, then this code 378 // prioritizes what to show in that space when all transient messages are gone. 379 CharSequence string = null; 380 if (mShowingBatteryInfo) { 381 // Battery status 382 if (mPluggedIn) { 383 // Charging or charged 384 if (mUpdateMonitor.isDeviceCharged()) { 385 string = getContext().getString(R.string.lockscreen_charged); 386 } else { 387 string = getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel); 388 } 389 icon.value = CHARGING_ICON; 390 } else if (mBatteryLevel < KeyguardUpdateMonitor.LOW_BATTERY_THRESHOLD) { 391 // Battery is low 392 string = getContext().getString(R.string.lockscreen_low_battery); 393 icon.value = BATTERY_LOW_ICON; 394 } 395 } else { 396 string = mCarrierText; 397 } 398 return string; 399 } 400 401 private CharSequence getPriorityTextMessage(MutableInt icon) { 402 CharSequence string = null; 403 if (!TextUtils.isEmpty(mInstructionText)) { 404 // Instructions only 405 string = mInstructionText; 406 icon.value = LOCK_ICON; 407 } else if (mShowingBatteryInfo) { 408 // Battery status 409 if (mPluggedIn) { 410 // Charging or charged 411 if (mUpdateMonitor.isDeviceCharged()) { 412 string = getContext().getString(R.string.lockscreen_charged); 413 } else { 414 string = getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel); 415 } 416 icon.value = CHARGING_ICON; 417 } else if (mBatteryLevel < KeyguardUpdateMonitor.LOW_BATTERY_THRESHOLD) { 418 // Battery is low 419 string = getContext().getString(R.string.lockscreen_low_battery); 420 icon.value = BATTERY_LOW_ICON; 421 } 422 } else if (!inWidgetMode() && mOwnerInfoView == null && mOwnerInfoText != null) { 423 // OwnerInfo shows in status if we don't have a dedicated widget 424 string = mOwnerInfoText; 425 } 426 return string; 427 } 428 429 void refreshDate() { 430 if (mDateView != null) { 431 mDateView.setText(DateFormat.format(mDateFormatString, new Date())); 432 } 433 } 434 435 /** 436 * Determine the current status of the lock screen given the sim state and other stuff. 437 */ 438 public StatusMode getStatusForIccState(IccCard.State simState) { 439 // Since reading the SIM may take a while, we assume it is present until told otherwise. 440 if (simState == null) { 441 return StatusMode.Normal; 442 } 443 444 final boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned() 445 && (simState == IccCard.State.ABSENT || simState == IccCard.State.PERM_DISABLED)); 446 447 // Assume we're NETWORK_LOCKED if not provisioned 448 simState = missingAndNotProvisioned ? State.NETWORK_LOCKED : simState; 449 switch (simState) { 450 case ABSENT: 451 return StatusMode.SimMissing; 452 case NETWORK_LOCKED: 453 return StatusMode.SimMissingLocked; 454 case NOT_READY: 455 return StatusMode.SimMissing; 456 case PIN_REQUIRED: 457 return StatusMode.SimLocked; 458 case PUK_REQUIRED: 459 return StatusMode.SimPukLocked; 460 case READY: 461 return StatusMode.Normal; 462 case PERM_DISABLED: 463 return StatusMode.SimPermDisabled; 464 case UNKNOWN: 465 return StatusMode.SimMissing; 466 } 467 return StatusMode.SimMissing; 468 } 469 470 private Context getContext() { 471 return mContainer.getContext(); 472 } 473 474 /** 475 * Update carrier text, carrier help and emergency button to match the current status based 476 * on SIM state. 477 * 478 * @param simState 479 */ 480 private void updateCarrierStateWithSimStatus(State simState) { 481 if (DEBUG) Log.d(TAG, "updateCarrierTextWithSimStatus(), simState = " + simState); 482 483 CharSequence carrierText = null; 484 int carrierHelpTextId = 0; 485 mEmergencyButtonEnabledBecauseSimLocked = false; 486 mStatus = getStatusForIccState(simState); 487 mSimState = simState; 488 switch (mStatus) { 489 case Normal: 490 carrierText = makeCarierString(mPlmn, mSpn); 491 break; 492 493 case NetworkLocked: 494 carrierText = makeCarrierStringOnEmergencyCapable( 495 getContext().getText(R.string.lockscreen_network_locked_message), 496 mPlmn); 497 carrierHelpTextId = R.string.lockscreen_instructions_when_pattern_disabled; 498 break; 499 500 case SimMissing: 501 // Shows "No SIM card | Emergency calls only" on devices that are voice-capable. 502 // This depends on mPlmn containing the text "Emergency calls only" when the radio 503 // has some connectivity. Otherwise, it should be null or empty and just show 504 // "No SIM card" 505 carrierText = makeCarrierStringOnEmergencyCapable( 506 getContext().getText(R.string.lockscreen_missing_sim_message_short), 507 mPlmn); 508 carrierHelpTextId = R.string.lockscreen_missing_sim_instructions_long; 509 break; 510 511 case SimPermDisabled: 512 carrierText = getContext().getText( 513 R.string.lockscreen_permanent_disabled_sim_message_short); 514 carrierHelpTextId = R.string.lockscreen_permanent_disabled_sim_instructions; 515 mEmergencyButtonEnabledBecauseSimLocked = true; 516 break; 517 518 case SimMissingLocked: 519 carrierText = makeCarrierStringOnEmergencyCapable( 520 getContext().getText(R.string.lockscreen_missing_sim_message_short), 521 mPlmn); 522 carrierHelpTextId = R.string.lockscreen_missing_sim_instructions; 523 mEmergencyButtonEnabledBecauseSimLocked = true; 524 break; 525 526 case SimLocked: 527 carrierText = makeCarrierStringOnEmergencyCapable( 528 getContext().getText(R.string.lockscreen_sim_locked_message), 529 mPlmn); 530 mEmergencyButtonEnabledBecauseSimLocked = true; 531 break; 532 533 case SimPukLocked: 534 carrierText = makeCarrierStringOnEmergencyCapable( 535 getContext().getText(R.string.lockscreen_sim_puk_locked_message), 536 mPlmn); 537 if (!mLockPatternUtils.isPukUnlockScreenEnable()) { 538 // This means we're showing the PUK unlock screen 539 mEmergencyButtonEnabledBecauseSimLocked = true; 540 } 541 break; 542 } 543 544 setCarrierText(carrierText); 545 setCarrierHelpText(carrierHelpTextId); 546 updateEmergencyCallButtonState(mPhoneState); 547 } 548 549 550 /* 551 * Add emergencyCallMessage to carrier string only if phone supports emergency calls. 552 */ 553 private CharSequence makeCarrierStringOnEmergencyCapable( 554 CharSequence simMessage, CharSequence emergencyCallMessage) { 555 if (mLockPatternUtils.isEmergencyCallCapable()) { 556 return makeCarierString(simMessage, emergencyCallMessage); 557 } 558 return simMessage; 559 } 560 561 private View findViewById(int id) { 562 return mContainer.findViewById(id); 563 } 564 565 /** 566 * The status of this lock screen. Primarily used for widgets on LockScreen. 567 */ 568 enum StatusMode { 569 /** 570 * Normal case (sim card present, it's not locked) 571 */ 572 Normal(true), 573 574 /** 575 * The sim card is 'network locked'. 576 */ 577 NetworkLocked(true), 578 579 /** 580 * The sim card is missing. 581 */ 582 SimMissing(false), 583 584 /** 585 * The sim card is missing, and this is the device isn't provisioned, so we don't let 586 * them get past the screen. 587 */ 588 SimMissingLocked(false), 589 590 /** 591 * The sim card is PUK locked, meaning they've entered the wrong sim unlock code too many 592 * times. 593 */ 594 SimPukLocked(false), 595 596 /** 597 * The sim card is locked. 598 */ 599 SimLocked(true), 600 601 /** 602 * The sim card is permanently disabled due to puk unlock failure 603 */ 604 SimPermDisabled(false); 605 606 private final boolean mShowStatusLines; 607 608 StatusMode(boolean mShowStatusLines) { 609 this.mShowStatusLines = mShowStatusLines; 610 } 611 612 /** 613 * @return Whether the status lines (battery level and / or next alarm) are shown while 614 * in this state. Mostly dictated by whether this is room for them. 615 */ 616 public boolean shouldShowStatusLines() { 617 return mShowStatusLines; 618 } 619 } 620 621 private void updateEmergencyCallButtonState(int phoneState) { 622 if (mEmergencyCallButton != null) { 623 boolean enabledBecauseSimLocked = 624 mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked() 625 && mEmergencyButtonEnabledBecauseSimLocked; 626 boolean shown = mEmergencyCallButtonEnabledInScreen || enabledBecauseSimLocked; 627 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton, 628 phoneState, shown); 629 } 630 } 631 632 private InfoCallbackImpl mInfoCallback = new InfoCallbackImpl() { 633 634 @Override 635 public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, 636 int batteryLevel) { 637 mShowingBatteryInfo = showBatteryInfo; 638 mPluggedIn = pluggedIn; 639 mBatteryLevel = batteryLevel; 640 final MutableInt tmpIcon = new MutableInt(0); 641 update(BATTERY_INFO, getAltTextMessage(tmpIcon)); 642 } 643 644 @Override 645 public void onTimeChanged() { 646 refreshDate(); 647 } 648 649 @Override 650 public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { 651 mPlmn = plmn; 652 mSpn = spn; 653 updateCarrierStateWithSimStatus(mSimState); 654 } 655 656 @Override 657 public void onPhoneStateChanged(int phoneState) { 658 mPhoneState = phoneState; 659 updateEmergencyCallButtonState(phoneState); 660 } 661 662 }; 663 664 private SimStateCallback mSimStateCallback = new SimStateCallback() { 665 666 public void onSimStateChanged(State simState) { 667 updateCarrierStateWithSimStatus(simState); 668 } 669 }; 670 671 public void onClick(View v) { 672 if (v == mEmergencyCallButton) { 673 mCallback.takeEmergencyCallAction(); 674 } 675 } 676 677 /** 678 * Performs concentenation of PLMN/SPN 679 * @param plmn 680 * @param spn 681 * @return 682 */ 683 private static CharSequence makeCarierString(CharSequence plmn, CharSequence spn) { 684 final boolean plmnValid = !TextUtils.isEmpty(plmn); 685 final boolean spnValid = !TextUtils.isEmpty(spn); 686 if (plmnValid && spnValid) { 687 return plmn + "|" + spn; 688 } else if (plmnValid) { 689 return plmn; 690 } else if (spnValid) { 691 return spn; 692 } else { 693 return ""; 694 } 695 } 696} 697