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