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