1 /* 2 * Copyright (C) 2007 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 17 package com.android.stk; 18 19 import android.app.Notification; 20 import android.app.NotificationManager; 21 import android.app.PendingIntent; 22 import android.app.Service; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.telephony.TelephonyManager; 32 import android.view.Gravity; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.widget.ImageView; 36 import android.widget.RemoteViews; 37 import android.widget.TextView; 38 import android.widget.Toast; 39 40 import com.android.internal.telephony.gsm.stk.AppInterface; 41 import com.android.internal.telephony.gsm.stk.Menu; 42 import com.android.internal.telephony.gsm.stk.Item; 43 import com.android.internal.telephony.gsm.stk.ResultCode; 44 import com.android.internal.telephony.gsm.stk.StkCmdMessage; 45 import com.android.internal.telephony.gsm.stk.StkCmdMessage.BrowserSettings; 46 import com.android.internal.telephony.gsm.stk.StkLog; 47 import com.android.internal.telephony.gsm.stk.StkResponseMessage; 48 import com.android.internal.telephony.gsm.stk.TextMessage; 49 50 import java.util.LinkedList; 51 52 /** 53 * SIM toolkit application level service. Interacts with Telephopny messages, 54 * application's launch and user input from STK UI elements. 55 * 56 */ 57 public class StkAppService extends Service implements Runnable { 58 59 // members 60 private volatile Looper mServiceLooper; 61 private volatile ServiceHandler mServiceHandler; 62 private AppInterface mStkService; 63 private Context mContext = null; 64 private StkCmdMessage mMainCmd = null; 65 private StkCmdMessage mCurrentCmd = null; 66 private Menu mCurrentMenu = null; 67 private String lastSelectedItem = null; 68 private boolean mMenuIsVisibile = false; 69 private boolean responseNeeded = true; 70 private boolean mCmdInProgress = false; 71 private NotificationManager mNotificationManager = null; 72 private LinkedList<DelayedCmd> mCmdsQ = null; 73 private boolean launchBrowser = false; 74 private BrowserSettings mBrowserSettings = null; 75 static StkAppService sInstance = null; 76 77 // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when 78 // creating an intent. 79 private enum InitiatedByUserAction { 80 yes, // The action was started via a user initiated action 81 unknown, // Not known for sure if user initated the action 82 } 83 84 // constants 85 static final String OPCODE = "op"; 86 static final String CMD_MSG = "cmd message"; 87 static final String RES_ID = "response id"; 88 static final String MENU_SELECTION = "menu selection"; 89 static final String INPUT = "input"; 90 static final String HELP = "help"; 91 static final String CONFIRMATION = "confirm"; 92 93 // operations ids for different service functionality. 94 static final int OP_CMD = 1; 95 static final int OP_RESPONSE = 2; 96 static final int OP_LAUNCH_APP = 3; 97 static final int OP_END_SESSION = 4; 98 static final int OP_BOOT_COMPLETED = 5; 99 private static final int OP_DELAYED_MSG = 6; 100 101 // Response ids 102 static final int RES_ID_MENU_SELECTION = 11; 103 static final int RES_ID_INPUT = 12; 104 static final int RES_ID_CONFIRM = 13; 105 static final int RES_ID_DONE = 14; 106 107 static final int RES_ID_TIMEOUT = 20; 108 static final int RES_ID_BACKWARD = 21; 109 static final int RES_ID_END_SESSION = 22; 110 static final int RES_ID_EXIT = 23; 111 112 private static final String PACKAGE_NAME = "com.android.stk"; 113 private static final String MENU_ACTIVITY_NAME = 114 PACKAGE_NAME + ".StkMenuActivity"; 115 private static final String INPUT_ACTIVITY_NAME = 116 PACKAGE_NAME + ".StkInputActivity"; 117 118 // Notification id used to display Idle Mode text in NotificationManager. 119 private static final int STK_NOTIFICATION_ID = 333; 120 121 // Inner class used for queuing telephony messages (proactive commands, 122 // session end) while the service is busy processing a previous message. 123 private class DelayedCmd { 124 // members 125 int id; 126 StkCmdMessage msg; 127 DelayedCmd(int id, StkCmdMessage msg)128 DelayedCmd(int id, StkCmdMessage msg) { 129 this.id = id; 130 this.msg = msg; 131 } 132 } 133 134 @Override onCreate()135 public void onCreate() { 136 // Initialize members 137 mStkService = com.android.internal.telephony.gsm.stk.StkService 138 .getInstance(); 139 140 // NOTE mStkService is a singleton and continues to exist even if the GSMPhone is disposed 141 // after the radio technology change from GSM to CDMA so the PHONE_TYPE_CDMA check is 142 // needed. In case of switching back from CDMA to GSM the GSMPhone constructor updates 143 // the instance. (TODO: test). 144 if ((mStkService == null) 145 && (TelephonyManager.getDefault().getPhoneType() 146 != TelephonyManager.PHONE_TYPE_CDMA)) { 147 StkLog.d(this, " Unable to get Service handle"); 148 return; 149 } 150 151 mCmdsQ = new LinkedList<DelayedCmd>(); 152 Thread serviceThread = new Thread(null, this, "Stk App Service"); 153 serviceThread.start(); 154 mContext = getBaseContext(); 155 mNotificationManager = (NotificationManager) mContext 156 .getSystemService(Context.NOTIFICATION_SERVICE); 157 sInstance = this; 158 } 159 160 @Override onStart(Intent intent, int startId)161 public void onStart(Intent intent, int startId) { 162 waitForLooper(); 163 164 // onStart() method can be passed a null intent 165 // TODO: replace onStart() with onStartCommand() 166 if (intent == null) { 167 return; 168 } 169 170 Bundle args = intent.getExtras(); 171 172 if (args == null) { 173 return; 174 } 175 176 Message msg = mServiceHandler.obtainMessage(); 177 msg.arg1 = args.getInt(OPCODE); 178 switch(msg.arg1) { 179 case OP_CMD: 180 msg.obj = args.getParcelable(CMD_MSG); 181 break; 182 case OP_RESPONSE: 183 msg.obj = args; 184 /* falls through */ 185 case OP_LAUNCH_APP: 186 case OP_END_SESSION: 187 case OP_BOOT_COMPLETED: 188 break; 189 default: 190 return; 191 } 192 mServiceHandler.sendMessage(msg); 193 } 194 195 @Override onDestroy()196 public void onDestroy() { 197 waitForLooper(); 198 mServiceLooper.quit(); 199 } 200 201 @Override onBind(Intent intent)202 public IBinder onBind(Intent intent) { 203 return null; 204 } 205 run()206 public void run() { 207 Looper.prepare(); 208 209 mServiceLooper = Looper.myLooper(); 210 mServiceHandler = new ServiceHandler(); 211 212 Looper.loop(); 213 } 214 215 /* 216 * Package api used by StkMenuActivity to indicate if its on the foreground. 217 */ indicateMenuVisibility(boolean visibility)218 void indicateMenuVisibility(boolean visibility) { 219 mMenuIsVisibile = visibility; 220 } 221 222 /* 223 * Package api used by StkMenuActivity to get its Menu parameter. 224 */ getMenu()225 Menu getMenu() { 226 return mCurrentMenu; 227 } 228 229 /* 230 * Package api used by UI Activities and Dialogs to communicate directly 231 * with the service to deliver state information and parameters. 232 */ getInstance()233 static StkAppService getInstance() { 234 return sInstance; 235 } 236 waitForLooper()237 private void waitForLooper() { 238 while (mServiceHandler == null) { 239 synchronized (this) { 240 try { 241 wait(100); 242 } catch (InterruptedException e) { 243 } 244 } 245 } 246 } 247 248 private final class ServiceHandler extends Handler { 249 @Override handleMessage(Message msg)250 public void handleMessage(Message msg) { 251 int opcode = msg.arg1; 252 253 switch (opcode) { 254 case OP_LAUNCH_APP: 255 if (mMainCmd == null) { 256 // nothing todo when no SET UP MENU command didn't arrive. 257 return; 258 } 259 launchMenuActivity(null); 260 break; 261 case OP_CMD: 262 StkCmdMessage cmdMsg = (StkCmdMessage) msg.obj; 263 // There are two types of commands: 264 // 1. Interactive - user's response is required. 265 // 2. Informative - display a message, no interaction with the user. 266 // 267 // Informative commands can be handled immediately without any delay. 268 // Interactive commands can't override each other. So if a command 269 // is already in progress, we need to queue the next command until 270 // the user has responded or a timeout expired. 271 if (!isCmdInteractive(cmdMsg)) { 272 handleCmd(cmdMsg); 273 } else { 274 if (!mCmdInProgress) { 275 mCmdInProgress = true; 276 handleCmd((StkCmdMessage) msg.obj); 277 } else { 278 mCmdsQ.addLast(new DelayedCmd(OP_CMD, 279 (StkCmdMessage) msg.obj)); 280 } 281 } 282 break; 283 case OP_RESPONSE: 284 if (responseNeeded) { 285 handleCmdResponse((Bundle) msg.obj); 286 } 287 // call delayed commands if needed. 288 if (mCmdsQ.size() != 0) { 289 callDelayedMsg(); 290 } else { 291 mCmdInProgress = false; 292 } 293 // reset response needed state var to its original value. 294 responseNeeded = true; 295 break; 296 case OP_END_SESSION: 297 if (!mCmdInProgress) { 298 mCmdInProgress = true; 299 handleSessionEnd(); 300 } else { 301 mCmdsQ.addLast(new DelayedCmd(OP_END_SESSION, null)); 302 } 303 break; 304 case OP_BOOT_COMPLETED: 305 StkLog.d(this, "OP_BOOT_COMPLETED"); 306 if (mMainCmd == null) { 307 StkAppInstaller.unInstall(mContext); 308 } 309 break; 310 case OP_DELAYED_MSG: 311 handleDelayedCmd(); 312 break; 313 } 314 } 315 } 316 isCmdInteractive(StkCmdMessage cmd)317 private boolean isCmdInteractive(StkCmdMessage cmd) { 318 switch (cmd.getCmdType()) { 319 case SEND_DTMF: 320 case SEND_SMS: 321 case SEND_SS: 322 case SEND_USSD: 323 case SET_UP_IDLE_MODE_TEXT: 324 case SET_UP_MENU: 325 return false; 326 } 327 328 return true; 329 } 330 handleDelayedCmd()331 private void handleDelayedCmd() { 332 if (mCmdsQ.size() != 0) { 333 DelayedCmd cmd = mCmdsQ.poll(); 334 switch (cmd.id) { 335 case OP_CMD: 336 handleCmd(cmd.msg); 337 break; 338 case OP_END_SESSION: 339 handleSessionEnd(); 340 break; 341 } 342 } 343 } 344 callDelayedMsg()345 private void callDelayedMsg() { 346 Message msg = mServiceHandler.obtainMessage(); 347 msg.arg1 = OP_DELAYED_MSG; 348 mServiceHandler.sendMessage(msg); 349 } 350 handleSessionEnd()351 private void handleSessionEnd() { 352 mCurrentCmd = mMainCmd; 353 lastSelectedItem = null; 354 // In case of SET UP MENU command which removed the app, don't 355 // update the current menu member. 356 if (mCurrentMenu != null && mMainCmd != null) { 357 mCurrentMenu = mMainCmd.getMenu(); 358 } 359 if (mMenuIsVisibile) { 360 launchMenuActivity(null); 361 } 362 if (mCmdsQ.size() != 0) { 363 callDelayedMsg(); 364 } else { 365 mCmdInProgress = false; 366 } 367 // In case a launch browser command was just confirmed, launch that url. 368 if (launchBrowser) { 369 launchBrowser = false; 370 launchBrowser(mBrowserSettings); 371 } 372 } 373 handleCmd(StkCmdMessage cmdMsg)374 private void handleCmd(StkCmdMessage cmdMsg) { 375 if (cmdMsg == null) { 376 return; 377 } 378 // save local reference for state tracking. 379 mCurrentCmd = cmdMsg; 380 boolean waitForUsersResponse = true; 381 382 StkLog.d(this, cmdMsg.getCmdType().name()); 383 switch (cmdMsg.getCmdType()) { 384 case DISPLAY_TEXT: 385 TextMessage msg = cmdMsg.geTextMessage(); 386 responseNeeded = msg.responseNeeded; 387 if (lastSelectedItem != null) { 388 msg.title = lastSelectedItem; 389 } else if (mMainCmd != null){ 390 msg.title = mMainCmd.getMenu().title; 391 } else { 392 // TODO: get the carrier name from the SIM 393 msg.title = ""; 394 } 395 launchTextDialog(); 396 break; 397 case SELECT_ITEM: 398 mCurrentMenu = cmdMsg.getMenu(); 399 launchMenuActivity(cmdMsg.getMenu()); 400 break; 401 case SET_UP_MENU: 402 mMainCmd = mCurrentCmd; 403 mCurrentMenu = cmdMsg.getMenu(); 404 if (removeMenu()) { 405 StkLog.d(this, "Uninstall App"); 406 mCurrentMenu = null; 407 StkAppInstaller.unInstall(mContext); 408 } else { 409 StkLog.d(this, "Install App"); 410 StkAppInstaller.install(mContext); 411 } 412 if (mMenuIsVisibile) { 413 launchMenuActivity(null); 414 } 415 break; 416 case GET_INPUT: 417 case GET_INKEY: 418 launchInputActivity(); 419 break; 420 case SET_UP_IDLE_MODE_TEXT: 421 waitForUsersResponse = false; 422 launchIdleText(); 423 break; 424 case SEND_DTMF: 425 case SEND_SMS: 426 case SEND_SS: 427 case SEND_USSD: 428 waitForUsersResponse = false; 429 launchEventMessage(); 430 break; 431 case LAUNCH_BROWSER: 432 launchConfirmationDialog(mCurrentCmd.geTextMessage()); 433 break; 434 case SET_UP_CALL: 435 launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg); 436 break; 437 case PLAY_TONE: 438 launchToneDialog(); 439 break; 440 } 441 442 if (!waitForUsersResponse) { 443 if (mCmdsQ.size() != 0) { 444 callDelayedMsg(); 445 } else { 446 mCmdInProgress = false; 447 } 448 } 449 } 450 handleCmdResponse(Bundle args)451 private void handleCmdResponse(Bundle args) { 452 if (mCurrentCmd == null) { 453 return; 454 } 455 StkResponseMessage resMsg = new StkResponseMessage(mCurrentCmd); 456 457 // set result code 458 boolean helpRequired = args.getBoolean(HELP, false); 459 460 switch(args.getInt(RES_ID)) { 461 case RES_ID_MENU_SELECTION: 462 StkLog.d(this, "RES_ID_MENU_SELECTION"); 463 int menuSelection = args.getInt(MENU_SELECTION); 464 switch(mCurrentCmd.getCmdType()) { 465 case SET_UP_MENU: 466 case SELECT_ITEM: 467 lastSelectedItem = getItemName(menuSelection); 468 if (helpRequired) { 469 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 470 } else { 471 resMsg.setResultCode(ResultCode.OK); 472 } 473 resMsg.setMenuSelection(menuSelection); 474 break; 475 } 476 break; 477 case RES_ID_INPUT: 478 StkLog.d(this, "RES_ID_INPUT"); 479 String input = args.getString(INPUT); 480 if (mCurrentCmd.geInput().yesNo) { 481 boolean yesNoSelection = input 482 .equals(StkInputActivity.YES_STR_RESPONSE); 483 resMsg.setYesNo(yesNoSelection); 484 } else { 485 if (helpRequired) { 486 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 487 } else { 488 resMsg.setResultCode(ResultCode.OK); 489 resMsg.setInput(input); 490 } 491 } 492 break; 493 case RES_ID_CONFIRM: 494 StkLog.d(this, "RES_ID_CONFIRM"); 495 boolean confirmed = args.getBoolean(CONFIRMATION); 496 switch (mCurrentCmd.getCmdType()) { 497 case DISPLAY_TEXT: 498 resMsg.setResultCode(confirmed ? ResultCode.OK 499 : ResultCode.UICC_SESSION_TERM_BY_USER); 500 break; 501 case LAUNCH_BROWSER: 502 resMsg.setResultCode(confirmed ? ResultCode.OK 503 : ResultCode.UICC_SESSION_TERM_BY_USER); 504 if (confirmed) { 505 launchBrowser = true; 506 mBrowserSettings = mCurrentCmd.getBrowserSettings(); 507 } 508 break; 509 case SET_UP_CALL: 510 resMsg.setResultCode(ResultCode.OK); 511 resMsg.setConfirmation(confirmed); 512 if (confirmed) { 513 launchCallMsg(); 514 } 515 break; 516 } 517 break; 518 case RES_ID_DONE: 519 resMsg.setResultCode(ResultCode.OK); 520 break; 521 case RES_ID_BACKWARD: 522 StkLog.d(this, "RES_ID_BACKWARD"); 523 resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER); 524 break; 525 case RES_ID_END_SESSION: 526 StkLog.d(this, "RES_ID_END_SESSION"); 527 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER); 528 break; 529 case RES_ID_TIMEOUT: 530 StkLog.d(this, "RES_ID_TIMEOUT"); 531 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER); 532 break; 533 default: 534 StkLog.d(this, "Unknown result id"); 535 return; 536 } 537 mStkService.onCmdResponse(resMsg); 538 } 539 540 /** 541 * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action. 542 * 543 * @param userAction If the userAction is yes then we always return 0 otherwise 544 * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true 545 * then we are the foreground app and we'll return 0 as from our perspective a 546 * user action did cause. If it's false than we aren't the foreground app and 547 * FLAG_ACTIVITY_NO_USER_ACTION is returned. 548 * 549 * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION 550 */ getFlagActivityNoUserAction(InitiatedByUserAction userAction)551 private int getFlagActivityNoUserAction(InitiatedByUserAction userAction) { 552 return ((userAction == InitiatedByUserAction.yes) | mMenuIsVisibile) ? 553 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION; 554 } 555 launchMenuActivity(Menu menu)556 private void launchMenuActivity(Menu menu) { 557 Intent newIntent = new Intent(Intent.ACTION_VIEW); 558 newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME); 559 int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK 560 | Intent.FLAG_ACTIVITY_CLEAR_TOP; 561 if (menu == null) { 562 // We assume this was initiated by the user pressing the tool kit icon 563 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes); 564 565 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN); 566 } else { 567 // We don't know and we'll let getFlagActivityNoUserAction decide. 568 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown); 569 570 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY); 571 } 572 newIntent.setFlags(intentFlags); 573 mContext.startActivity(newIntent); 574 } 575 launchInputActivity()576 private void launchInputActivity() { 577 Intent newIntent = new Intent(Intent.ACTION_VIEW); 578 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 579 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 580 newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME); 581 newIntent.putExtra("INPUT", mCurrentCmd.geInput()); 582 mContext.startActivity(newIntent); 583 } 584 launchTextDialog()585 private void launchTextDialog() { 586 Intent newIntent = new Intent(this, StkDialogActivity.class); 587 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 588 | Intent.FLAG_ACTIVITY_MULTIPLE_TASK 589 | Intent.FLAG_ACTIVITY_NO_HISTORY 590 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 591 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 592 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage()); 593 startActivity(newIntent); 594 } 595 launchEventMessage()596 private void launchEventMessage() { 597 TextMessage msg = mCurrentCmd.geTextMessage(); 598 if (msg == null) { 599 return; 600 } 601 Toast toast = new Toast(mContext.getApplicationContext()); 602 LayoutInflater inflate = (LayoutInflater) mContext 603 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 604 View v = inflate.inflate(R.layout.stk_event_msg, null); 605 TextView tv = (TextView) v 606 .findViewById(com.android.internal.R.id.message); 607 ImageView iv = (ImageView) v 608 .findViewById(com.android.internal.R.id.icon); 609 if (msg.icon != null) { 610 iv.setImageBitmap(msg.icon); 611 } else { 612 iv.setVisibility(View.GONE); 613 } 614 if (!msg.iconSelfExplanatory) { 615 tv.setText(msg.text); 616 } 617 618 toast.setView(v); 619 toast.setDuration(Toast.LENGTH_LONG); 620 toast.setGravity(Gravity.BOTTOM, 0, 0); 621 toast.show(); 622 } 623 launchConfirmationDialog(TextMessage msg)624 private void launchConfirmationDialog(TextMessage msg) { 625 msg.title = lastSelectedItem; 626 Intent newIntent = new Intent(this, StkDialogActivity.class); 627 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 628 | Intent.FLAG_ACTIVITY_NO_HISTORY 629 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 630 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 631 newIntent.putExtra("TEXT", msg); 632 startActivity(newIntent); 633 } 634 launchBrowser(BrowserSettings settings)635 private void launchBrowser(BrowserSettings settings) { 636 if (settings == null) { 637 return; 638 } 639 // Set browser launch mode 640 Intent intent = new Intent(); 641 intent.setClassName("com.android.browser", 642 "com.android.browser.BrowserActivity"); 643 644 // to launch home page, make sure that data Uri is null. 645 Uri data = null; 646 if (settings.url != null) { 647 data = Uri.parse(settings.url); 648 } 649 intent.setData(data); 650 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 651 switch (settings.mode) { 652 case USE_EXISTING_BROWSER: 653 intent.setAction(Intent.ACTION_VIEW); 654 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 655 break; 656 case LAUNCH_NEW_BROWSER: 657 intent.setAction(Intent.ACTION_VIEW); 658 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 659 break; 660 case LAUNCH_IF_NOT_ALREADY_LAUNCHED: 661 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 662 break; 663 } 664 // start browser activity 665 startActivity(intent); 666 // a small delay, let the browser start, before processing the next command. 667 // this is good for scenarios where a related DISPLAY TEXT command is 668 // followed immediately. 669 try { 670 Thread.sleep(10000); 671 } catch (InterruptedException e) {} 672 } 673 launchCallMsg()674 private void launchCallMsg() { 675 TextMessage msg = mCurrentCmd.getCallSettings().callMsg; 676 if (msg.text == null || msg.text.length() == 0) { 677 return; 678 } 679 msg.title = lastSelectedItem; 680 681 Toast toast = Toast.makeText(mContext.getApplicationContext(), msg.text, 682 Toast.LENGTH_LONG); 683 toast.setGravity(Gravity.BOTTOM, 0, 0); 684 toast.show(); 685 } 686 launchIdleText()687 private void launchIdleText() { 688 TextMessage msg = mCurrentCmd.geTextMessage(); 689 if (msg.text == null) { 690 mNotificationManager.cancel(STK_NOTIFICATION_ID); 691 } else { 692 Notification notification = new Notification(); 693 RemoteViews contentView = new RemoteViews( 694 PACKAGE_NAME, 695 com.android.internal.R.layout.status_bar_latest_event_content); 696 697 notification.flags |= Notification.FLAG_NO_CLEAR; 698 notification.icon = com.android.internal.R.drawable.stat_notify_sim_toolkit; 699 // Set text and icon for the status bar and notification body. 700 if (!msg.iconSelfExplanatory) { 701 notification.tickerText = msg.text; 702 contentView.setTextViewText(com.android.internal.R.id.text, 703 msg.text); 704 } 705 if (msg.icon != null) { 706 contentView.setImageViewBitmap(com.android.internal.R.id.icon, 707 msg.icon); 708 } else { 709 contentView 710 .setImageViewResource( 711 com.android.internal.R.id.icon, 712 com.android.internal.R.drawable.stat_notify_sim_toolkit); 713 } 714 notification.contentView = contentView; 715 notification.contentIntent = PendingIntent.getService(mContext, 0, 716 new Intent(mContext, StkAppService.class), 0); 717 718 mNotificationManager.notify(STK_NOTIFICATION_ID, notification); 719 } 720 } 721 launchToneDialog()722 private void launchToneDialog() { 723 Intent newIntent = new Intent(this, ToneDialog.class); 724 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 725 | Intent.FLAG_ACTIVITY_NO_HISTORY 726 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 727 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 728 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage()); 729 newIntent.putExtra("TONE", mCurrentCmd.getToneSettings()); 730 startActivity(newIntent); 731 } 732 getItemName(int itemId)733 private String getItemName(int itemId) { 734 Menu menu = mCurrentCmd.getMenu(); 735 if (menu == null) { 736 return null; 737 } 738 for (Item item : menu.items) { 739 if (item.id == itemId) { 740 return item.text; 741 } 742 } 743 return null; 744 } 745 removeMenu()746 private boolean removeMenu() { 747 try { 748 if (mCurrentMenu.items.size() == 1 && 749 mCurrentMenu.items.get(0) == null) { 750 return true; 751 } 752 } catch (NullPointerException e) { 753 StkLog.d(this, "Unable to get Menu's items size"); 754 return true; 755 } 756 return false; 757 } 758 } 759