1 /* 2 * Created on 28.11.2003 3 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 2 8 * of the License, or (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program; if not, write to the Free Software 15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 * 17 */ 18 package org.gudy.azureus2.ui.swt; 19 20 import java.applet.Applet; 21 import java.applet.AudioClip; 22 import java.io.File; 23 import java.net.URL; 24 import java.util.Map; 25 26 import org.gudy.azureus2.core3.config.COConfigurationManager; 27 import org.gudy.azureus2.core3.disk.*; 28 import org.gudy.azureus2.core3.download.DownloadManager; 29 import org.gudy.azureus2.core3.download.DownloadManagerDiskListener; 30 import org.gudy.azureus2.core3.download.DownloadManagerState; 31 import org.gudy.azureus2.core3.download.impl.DownloadManagerAdapter; 32 import org.gudy.azureus2.core3.global.GlobalManager; 33 import org.gudy.azureus2.core3.global.GlobalManagerAdapter; 34 import org.gudy.azureus2.core3.internat.MessageText; 35 import org.gudy.azureus2.core3.logging.LogAlert; 36 import org.gudy.azureus2.core3.logging.Logger; 37 import org.gudy.azureus2.core3.util.*; 38 import org.gudy.azureus2.platform.PlatformManager; 39 import org.gudy.azureus2.platform.PlatformManagerCapabilities; 40 import org.gudy.azureus2.platform.PlatformManagerFactory; 41 import org.gudy.azureus2.plugins.platform.PlatformManagerException; 42 import org.gudy.azureus2.ui.swt.minibar.DownloadBar; 43 44 import com.aelitis.azureus.ui.UIFunctions; 45 import com.aelitis.azureus.ui.UIFunctionsManager; 46 import com.aelitis.azureus.ui.mdi.MultipleDocumentInterface; 47 48 /** 49 * Contains methods to alert the user of certain events. 50 * @author Rene Leonhardt 51 */ 52 53 public class 54 UserAlerts 55 { 56 private AudioClip audio_clip = null; 57 private String audio_resource = ""; 58 59 private AEMonitor this_mon = new AEMonitor( "UserAlerts" ); 60 61 private boolean startup = true; 62 63 public UserAlerts( GlobalManager global_manager )64 UserAlerts( 65 GlobalManager global_manager ) 66 { 67 final DownloadManagerAdapter download_manager_listener = 68 new DownloadManagerAdapter() 69 { 70 public void downloadComplete(DownloadManager manager) { 71 activityFinished( manager, null ); 72 } 73 74 // @see org.gudy.azureus2.core3.download.impl.DownloadManagerAdapter#stateChanged(org.gudy.azureus2.core3.download.DownloadManager, int) 75 public void stateChanged(final DownloadManager manager, int state) { 76 77 boolean lowNoise = manager.getDownloadState().getFlag( 78 DownloadManagerState.FLAG_LOW_NOISE); 79 if (lowNoise) { 80 return; 81 } 82 83 // if state == STARTED, then open the details window (according to config) 84 if (state == DownloadManager.STATE_DOWNLOADING 85 || state == DownloadManager.STATE_SEEDING) { 86 Utils.execSWTThread(new AERunnable() { 87 public void runSupport() { 88 boolean complete = manager.isDownloadComplete(false); 89 90 if ((!complete && COConfigurationManager.getBooleanParameter("Open Details")) 91 || (complete && COConfigurationManager.getBooleanParameter("Open Seeding Details"))) { 92 UIFunctionsManager.getUIFunctions().getMDI().loadEntryByID( 93 MultipleDocumentInterface.SIDEBAR_SECTION_TORRENT_DETAILS, 94 false, false, manager); 95 } 96 97 if (((!complete) && COConfigurationManager.getBooleanParameter("Open Bar Incomplete")) 98 || (complete && COConfigurationManager.getBooleanParameter("Open Bar Complete"))) { 99 100 DownloadBar.open(manager, Utils.findAnyShell()); 101 } 102 } 103 }); 104 } 105 106 boolean error_reported = manager.getDownloadState().getFlag( DownloadManagerState.FLAG_ERROR_REPORTED ); 107 108 if ( state == DownloadManager.STATE_ERROR ){ 109 110 if ( !error_reported ){ 111 112 manager.getDownloadState().setFlag( DownloadManagerState.FLAG_ERROR_REPORTED, true ); 113 114 reportError( manager ); 115 } 116 }else if ( state == DownloadManager.STATE_DOWNLOADING || state == DownloadManager.STATE_SEEDING ){ 117 118 if ( error_reported ){ 119 120 manager.getDownloadState().setFlag( DownloadManagerState.FLAG_ERROR_REPORTED, false ); 121 } 122 } 123 } 124 }; 125 126 final DiskManagerListener disk_listener = 127 new DiskManagerListener() 128 { 129 public void 130 stateChanged( 131 int oldState, 132 int newState ) 133 { 134 } 135 136 public void 137 filePriorityChanged( 138 DiskManagerFileInfo file ) 139 { 140 } 141 142 public void 143 pieceDoneChanged( 144 DiskManagerPiece piece ) 145 { 146 } 147 148 public void 149 fileAccessModeChanged( 150 DiskManagerFileInfo file, 151 int old_mode, 152 int new_mode ) 153 { 154 DownloadManager dm = file.getDownloadManager(); 155 156 if ( dm != null ){ 157 158 if ( old_mode == DiskManagerFileInfo.WRITE && 159 new_mode == DiskManagerFileInfo.READ && 160 file.getDownloaded() == file.getLength()){ 161 162 activityFinished( dm, file ); 163 } 164 } 165 166 /* 167 System.out.println( 168 "amc:" + 169 file.getDownloadManager().getDisplayName() + "/" + 170 file.getName() + ":" + old_mode + " -> " + new_mode ); 171 */ 172 } 173 }; 174 175 final DownloadManagerDiskListener dm_disk_listener = 176 new DownloadManagerDiskListener() 177 { 178 public void 179 diskManagerAdded( 180 DiskManager dm ) 181 { 182 dm.addListener( disk_listener ); 183 } 184 185 public void 186 diskManagerRemoved( 187 DiskManager dm ) 188 { 189 dm.removeListener( disk_listener ); 190 } 191 192 }; 193 194 global_manager.addListener( 195 new GlobalManagerAdapter() 196 { 197 public void 198 downloadManagerAdded(DownloadManager manager) 199 { 200 // don't pop up for non-persistent as these get added late in the day every time 201 // so we'll notify for each download every startup 202 203 if (!startup && manager.isPersistent()) { 204 205 boolean bPopup = COConfigurationManager.getBooleanParameter("Popup Download Added"); 206 207 if (bPopup) { 208 209 if( !manager.getDownloadState().getFlag( DownloadManagerState.FLAG_LOW_NOISE )){ 210 211 String popup_text = MessageText.getString("popup.download.added", 212 new String[] { manager.getDisplayName() 213 }); 214 UIFunctionsManager.getUIFunctions().forceNotify( 215 UIFunctions.STATUSICON_NONE, null, popup_text, null, 216 new Object[] { 217 manager 218 }, -1); 219 } 220 } 221 } 222 223 manager.addListener(download_manager_listener); 224 225 manager.addDiskListener(dm_disk_listener); 226 } 227 228 public void 229 downloadManagerRemoved(DownloadManager manager) 230 { 231 manager.removeListener(download_manager_listener); 232 233 manager.removeDiskListener( dm_disk_listener ); 234 } 235 236 public void 237 destroyed() 238 { 239 tidyUp(); 240 } 241 }); 242 startup = false; 243 } 244 245 private void activityFinished( DownloadManager manager, DiskManagerFileInfo dm_file )246 activityFinished( 247 DownloadManager manager, 248 DiskManagerFileInfo dm_file ) 249 { 250 DownloadManagerState dm_state = manager.getDownloadState(); 251 252 if ( dm_state.getFlag( DownloadManagerState.FLAG_LOW_NOISE)) { 253 254 return; 255 } 256 257 boolean download = dm_file == null; 258 259 Object relatedObject; 260 String item_name; 261 262 if ( download ){ 263 264 relatedObject = manager; 265 item_name = manager.getDisplayName(); 266 267 }else{ 268 269 relatedObject = dm_file.getDiskManager(); 270 item_name = dm_file.getFile( true ).getName(); 271 } 272 273 final String sound_enabler; 274 final String sound_file; 275 final String default_sound = "org/gudy/azureus2/ui/icons/downloadFinished.wav"; 276 277 final String speech_enabler; 278 final String speech_text; 279 280 final String popup_enabler; 281 final String popup_def_text; 282 283 if ( download ){ 284 sound_enabler = "Play Download Finished"; 285 sound_file = "Play Download Finished File"; 286 287 speech_enabler = "Play Download Finished Announcement"; 288 speech_text = "Play Download Finished Announcement Text"; 289 290 popup_enabler = "Popup Download Finished"; 291 popup_def_text = "popup.download.finished"; 292 293 }else{ 294 sound_enabler = "Play File Finished"; 295 sound_file = "Play File Finished File"; 296 297 speech_enabler = "Play File Finished Announcement"; 298 speech_text = "Play File Finished Announcement Text"; 299 300 popup_enabler = "Popup File Finished"; 301 popup_def_text = "popup.file.finished"; 302 } 303 304 Map dl_file_alerts = dm_state.getMapAttribute( DownloadManagerState.AT_DL_FILE_ALERTS ); 305 String dlf_prefix = download?"":(String.valueOf(dm_file.getIndex()) + "." ); 306 307 try{ 308 this_mon.enter(); 309 310 if ( COConfigurationManager.getBooleanParameter(popup_enabler) || isDLFEnabled( dl_file_alerts, dlf_prefix, popup_enabler )) { 311 String popup_text = MessageText.getString(popup_def_text, new String[]{item_name}); 312 UIFunctionsManager.getUIFunctions().forceNotify( 313 UIFunctions.STATUSICON_NONE, null, popup_text, null, 314 new Object[] { 315 relatedObject 316 }, -1); 317 } 318 319 if (Constants.isOSX 320 && ( COConfigurationManager.getBooleanParameter(speech_enabler) || isDLFEnabled( dl_file_alerts, dlf_prefix, speech_enabler ))) { 321 new AEThread2("SaySound") { 322 public void run() { 323 try { 324 Runtime.getRuntime().exec(new String[] { 325 "say", 326 COConfigurationManager.getStringParameter(speech_text) 327 }); // Speech Synthesis services 328 329 Thread.sleep(2500); 330 } catch (Throwable e) { 331 } 332 } 333 }.start(); 334 } 335 336 if ( COConfigurationManager.getBooleanParameter( sound_enabler, false) || isDLFEnabled( dl_file_alerts, dlf_prefix, sound_enabler )){ 337 338 String file = COConfigurationManager.getStringParameter( sound_file ); 339 340 file = file.trim(); 341 342 // turn "<default>" into blank 343 344 if ( file.startsWith( "<" )){ 345 346 file = ""; 347 } 348 349 if ( audio_clip == null || !file.equals( audio_resource )){ 350 351 audio_clip = null; 352 353 // try explicit file 354 355 if ( file.length() != 0 ){ 356 357 File f = new File( file ); 358 359 try{ 360 361 if ( f.exists()){ 362 363 URL file_url = f.toURI().toURL(); 364 365 audio_clip = Applet.newAudioClip( file_url ); 366 } 367 368 }catch( Throwable e ){ 369 370 Debug.printStackTrace(e); 371 372 }finally{ 373 374 if ( audio_clip == null ){ 375 Logger.log(new LogAlert(relatedObject, LogAlert.UNREPEATABLE, 376 LogAlert.AT_ERROR, "Failed to load audio file '" + file 377 + "'")); 378 } 379 } 380 } 381 382 // either non-explicit or explicit missing 383 384 if ( audio_clip == null ){ 385 386 audio_clip = Applet.newAudioClip(UserAlerts.class.getClassLoader().getResource( default_sound )); 387 388 } 389 390 audio_resource = file; 391 } 392 393 if ( audio_clip != null ){ 394 395 new AEThread2("DownloadSound") 396 { 397 public void 398 run() 399 { 400 try{ 401 audio_clip.play(); 402 403 Thread.sleep(2500); 404 405 }catch( Throwable e ){ 406 407 } 408 } 409 }.start(); 410 } 411 } 412 }catch( Throwable e ){ 413 414 Debug.printStackTrace( e ); 415 416 }finally{ 417 418 this_mon.exit(); 419 } 420 } 421 422 private long last_error_speech; 423 private long last_error_sound; 424 425 private void reportError( DownloadManager manager )426 reportError( 427 DownloadManager manager ) 428 { 429 final Object relatedObject = manager; 430 final String item_name = manager.getDisplayName(); 431 432 final String default_sound = "org/gudy/azureus2/ui/icons/downloadFinished.wav"; 433 434 435 final String sound_enabler = "Play Download Error"; 436 final String sound_file = "Play Download Error File"; 437 438 final String speech_enabler = "Play Download Error Announcement"; 439 final String speech_text = "Play Download Error Announcement Text"; 440 441 final String popup_enabler = "Popup Download Error"; 442 final String popup_def_text = "popup.download.error"; 443 444 445 try{ 446 this_mon.enter(); 447 448 if ( COConfigurationManager.getBooleanParameter(popup_enabler)) { 449 UIFunctionsManager.execWithUIFunctions( 450 new UIFunctionsManager.UIFCallback() { 451 452 public void run(UIFunctions functions) { 453 454 String popup_text = MessageText.getString(popup_def_text, new String[]{item_name}); 455 456 functions.forceNotify( 457 UIFunctions.STATUSICON_ERROR, null, popup_text, null, 458 new Object[] { 459 relatedObject 460 }, -1); 461 } 462 }); 463 } 464 465 long now = SystemTime.getMonotonousTime(); 466 467 if ( Constants.isOSX 468 && COConfigurationManager.getBooleanParameter(speech_enabler)){ 469 470 if ( last_error_speech == 0 || now - last_error_speech > 5000 ){ 471 472 last_error_speech = now; 473 474 new AEThread2("SaySound") { 475 public void run() { 476 try { 477 Runtime.getRuntime().exec(new String[] { 478 "say", 479 COConfigurationManager.getStringParameter(speech_text) 480 }); // Speech Synthesis services 481 482 Thread.sleep(2500); 483 } catch (Throwable e) { 484 } 485 } 486 }.start(); 487 } 488 } 489 490 if ( COConfigurationManager.getBooleanParameter( sound_enabler, false)){ 491 492 if ( last_error_sound == 0 || now - last_error_sound > 5000 ){ 493 494 last_error_sound = now; 495 496 String file = COConfigurationManager.getStringParameter( sound_file ); 497 498 file = file.trim(); 499 500 // turn "<default>" into blank 501 502 if ( file.startsWith( "<" )){ 503 504 file = ""; 505 } 506 507 if ( audio_clip == null || !file.equals( audio_resource )){ 508 509 audio_clip = null; 510 511 // try explicit file 512 513 if ( file.length() != 0 ){ 514 515 File f = new File( file ); 516 517 try{ 518 519 if ( f.exists()){ 520 521 URL file_url = f.toURI().toURL(); 522 523 audio_clip = Applet.newAudioClip( file_url ); 524 } 525 526 }catch( Throwable e ){ 527 528 Debug.printStackTrace(e); 529 530 }finally{ 531 532 if ( audio_clip == null ){ 533 Logger.log(new LogAlert(relatedObject, LogAlert.UNREPEATABLE, 534 LogAlert.AT_ERROR, "Failed to load audio file '" + file 535 + "'")); 536 } 537 } 538 } 539 540 // either non-explicit or explicit missing 541 542 if ( audio_clip == null ){ 543 544 audio_clip = Applet.newAudioClip(UserAlerts.class.getClassLoader().getResource( default_sound )); 545 546 } 547 548 audio_resource = file; 549 } 550 551 if ( audio_clip != null ){ 552 553 new AEThread2("DownloadSound") 554 { 555 public void 556 run() 557 { 558 try{ 559 audio_clip.play(); 560 561 Thread.sleep(2500); 562 563 }catch( Throwable e ){ 564 565 } 566 } 567 }.start(); 568 } 569 } 570 } 571 }catch( Throwable e ){ 572 573 Debug.printStackTrace( e ); 574 575 }finally{ 576 577 this_mon.exit(); 578 } 579 } 580 581 private boolean isDLFEnabled( Map map, String prefix, String key )582 isDLFEnabled( 583 Map map, 584 String prefix, 585 String key ) 586 { 587 if ( map == null ){ 588 589 return( false ); 590 } 591 592 key = prefix + key; 593 594 return( map.containsKey( key )); 595 } 596 597 protected void tidyUp()598 tidyUp() 599 { 600 /* 601 The Java audio system keeps some threads running even after playback is finished. 602 One of them, named "Java Sound event dispatcher", is *not* a daemon 603 thread and keeps the VM alive. 604 We have to locate and interrupt it explicitely. 605 */ 606 607 try{ 608 609 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 610 611 Thread[] threadList = new Thread[threadGroup.activeCount()]; 612 613 threadGroup.enumerate(threadList); 614 615 for (int i = 0; i < threadList.length; i++){ 616 617 if(threadList[i] != null && "Java Sound event dispatcher".equals(threadList[i].getName())){ 618 619 threadList[i].interrupt(); 620 } 621 } 622 }catch( Throwable e ){ 623 624 Debug.printStackTrace( e ); 625 } 626 } 627 628 629 /** 630 * Grab the user's attention in a platform dependent way 631 * @param type one of <code>PlatformManager.USER_REQUEST_INFO</code>, 632 * <code>PlatformManager.USER_REQUEST_WARNING</code>, OR 633 * <code>PlatformManager.USER_REQUEST_QUESTION</code> 634 * @param data user-defined data object; 635 * see the platform-specific <code>PlatformManager</code> for what may be supported 636 */ requestUserAttention(int type, Object data)637 public static void requestUserAttention(int type, Object data) { 638 639 PlatformManager pm = PlatformManagerFactory.getPlatformManager(); 640 if (true == pm.hasCapability(PlatformManagerCapabilities.RequestUserAttention)) { 641 try { 642 pm.requestUserAttention(type, data); 643 } catch (PlatformManagerException e) { 644 Debug.printStackTrace(e); 645 } 646 } 647 } 648 }