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 // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT, 532 // Clear message after delay, successful) expects result code OK. 533 // If the command qualifier specifies no user response is required 534 // then send OK instead of NO_RESPONSE_FROM_USER 535 if ((mCurrentCmd.getCmdType().value() == AppInterface.CommandType.DISPLAY_TEXT 536 .value()) 537 && (mCurrentCmd.geTextMessage().userClear == false)) { 538 resMsg.setResultCode(ResultCode.OK); 539 } else { 540 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER); 541 } 542 break; 543 default: 544 StkLog.d(this, "Unknown result id"); 545 return; 546 } 547 mStkService.onCmdResponse(resMsg); 548 } 549 550 /** 551 * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action. 552 * 553 * @param userAction If the userAction is yes then we always return 0 otherwise 554 * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true 555 * then we are the foreground app and we'll return 0 as from our perspective a 556 * user action did cause. If it's false than we aren't the foreground app and 557 * FLAG_ACTIVITY_NO_USER_ACTION is returned. 558 * 559 * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION 560 */ getFlagActivityNoUserAction(InitiatedByUserAction userAction)561 private int getFlagActivityNoUserAction(InitiatedByUserAction userAction) { 562 return ((userAction == InitiatedByUserAction.yes) | mMenuIsVisibile) ? 563 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION; 564 } 565 launchMenuActivity(Menu menu)566 private void launchMenuActivity(Menu menu) { 567 Intent newIntent = new Intent(Intent.ACTION_VIEW); 568 newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME); 569 int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK 570 | Intent.FLAG_ACTIVITY_CLEAR_TOP; 571 if (menu == null) { 572 // We assume this was initiated by the user pressing the tool kit icon 573 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes); 574 575 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN); 576 } else { 577 // We don't know and we'll let getFlagActivityNoUserAction decide. 578 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown); 579 580 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY); 581 } 582 newIntent.setFlags(intentFlags); 583 mContext.startActivity(newIntent); 584 } 585 launchInputActivity()586 private void launchInputActivity() { 587 Intent newIntent = new Intent(Intent.ACTION_VIEW); 588 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 589 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 590 newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME); 591 newIntent.putExtra("INPUT", mCurrentCmd.geInput()); 592 mContext.startActivity(newIntent); 593 } 594 launchTextDialog()595 private void launchTextDialog() { 596 Intent newIntent = new Intent(this, StkDialogActivity.class); 597 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 598 | Intent.FLAG_ACTIVITY_MULTIPLE_TASK 599 | Intent.FLAG_ACTIVITY_NO_HISTORY 600 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 601 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 602 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage()); 603 startActivity(newIntent); 604 } 605 launchEventMessage()606 private void launchEventMessage() { 607 TextMessage msg = mCurrentCmd.geTextMessage(); 608 if (msg == null || msg.text == null) { 609 return; 610 } 611 Toast toast = new Toast(mContext.getApplicationContext()); 612 LayoutInflater inflate = (LayoutInflater) mContext 613 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 614 View v = inflate.inflate(R.layout.stk_event_msg, null); 615 TextView tv = (TextView) v 616 .findViewById(com.android.internal.R.id.message); 617 ImageView iv = (ImageView) v 618 .findViewById(com.android.internal.R.id.icon); 619 if (msg.icon != null) { 620 iv.setImageBitmap(msg.icon); 621 } else { 622 iv.setVisibility(View.GONE); 623 } 624 if (!msg.iconSelfExplanatory) { 625 tv.setText(msg.text); 626 } 627 628 toast.setView(v); 629 toast.setDuration(Toast.LENGTH_LONG); 630 toast.setGravity(Gravity.BOTTOM, 0, 0); 631 toast.show(); 632 } 633 launchConfirmationDialog(TextMessage msg)634 private void launchConfirmationDialog(TextMessage msg) { 635 msg.title = lastSelectedItem; 636 Intent newIntent = new Intent(this, StkDialogActivity.class); 637 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 638 | Intent.FLAG_ACTIVITY_NO_HISTORY 639 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 640 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 641 newIntent.putExtra("TEXT", msg); 642 startActivity(newIntent); 643 } 644 launchBrowser(BrowserSettings settings)645 private void launchBrowser(BrowserSettings settings) { 646 if (settings == null) { 647 return; 648 } 649 // Set browser launch mode 650 Intent intent = new Intent(); 651 intent.setClassName("com.android.browser", 652 "com.android.browser.BrowserActivity"); 653 654 // to launch home page, make sure that data Uri is null. 655 Uri data = null; 656 if (settings.url != null) { 657 data = Uri.parse(settings.url); 658 } 659 intent.setData(data); 660 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 661 switch (settings.mode) { 662 case USE_EXISTING_BROWSER: 663 intent.setAction(Intent.ACTION_VIEW); 664 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 665 break; 666 case LAUNCH_NEW_BROWSER: 667 intent.setAction(Intent.ACTION_VIEW); 668 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 669 break; 670 case LAUNCH_IF_NOT_ALREADY_LAUNCHED: 671 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 672 break; 673 } 674 // start browser activity 675 startActivity(intent); 676 // a small delay, let the browser start, before processing the next command. 677 // this is good for scenarios where a related DISPLAY TEXT command is 678 // followed immediately. 679 try { 680 Thread.sleep(10000); 681 } catch (InterruptedException e) {} 682 } 683 launchCallMsg()684 private void launchCallMsg() { 685 TextMessage msg = mCurrentCmd.getCallSettings().callMsg; 686 if (msg.text == null || msg.text.length() == 0) { 687 return; 688 } 689 msg.title = lastSelectedItem; 690 691 Toast toast = Toast.makeText(mContext.getApplicationContext(), msg.text, 692 Toast.LENGTH_LONG); 693 toast.setGravity(Gravity.BOTTOM, 0, 0); 694 toast.show(); 695 } 696 launchIdleText()697 private void launchIdleText() { 698 TextMessage msg = mCurrentCmd.geTextMessage(); 699 if (msg.text == null) { 700 mNotificationManager.cancel(STK_NOTIFICATION_ID); 701 } else { 702 Notification notification = new Notification(); 703 RemoteViews contentView = new RemoteViews( 704 PACKAGE_NAME, 705 com.android.internal.R.layout.status_bar_latest_event_content); 706 707 notification.flags |= Notification.FLAG_NO_CLEAR; 708 notification.icon = com.android.internal.R.drawable.stat_notify_sim_toolkit; 709 // Set text and icon for the status bar and notification body. 710 if (!msg.iconSelfExplanatory) { 711 notification.tickerText = msg.text; 712 contentView.setTextViewText(com.android.internal.R.id.text, 713 msg.text); 714 } 715 if (msg.icon != null) { 716 contentView.setImageViewBitmap(com.android.internal.R.id.icon, 717 msg.icon); 718 } else { 719 contentView 720 .setImageViewResource( 721 com.android.internal.R.id.icon, 722 com.android.internal.R.drawable.stat_notify_sim_toolkit); 723 } 724 notification.contentView = contentView; 725 notification.contentIntent = PendingIntent.getService(mContext, 0, 726 new Intent(mContext, StkAppService.class), 0); 727 728 mNotificationManager.notify(STK_NOTIFICATION_ID, notification); 729 } 730 } 731 launchToneDialog()732 private void launchToneDialog() { 733 Intent newIntent = new Intent(this, ToneDialog.class); 734 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 735 | Intent.FLAG_ACTIVITY_NO_HISTORY 736 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 737 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown)); 738 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage()); 739 newIntent.putExtra("TONE", mCurrentCmd.getToneSettings()); 740 startActivity(newIntent); 741 } 742 getItemName(int itemId)743 private String getItemName(int itemId) { 744 Menu menu = mCurrentCmd.getMenu(); 745 if (menu == null) { 746 return null; 747 } 748 for (Item item : menu.items) { 749 if (item.id == itemId) { 750 return item.text; 751 } 752 } 753 return null; 754 } 755 removeMenu()756 private boolean removeMenu() { 757 try { 758 if (mCurrentMenu.items.size() == 1 && 759 mCurrentMenu.items.get(0) == null) { 760 return true; 761 } 762 } catch (NullPointerException e) { 763 StkLog.d(this, "Unable to get Menu's items size"); 764 return true; 765 } 766 return false; 767 } 768 } 769