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