UIController.java
上传用户:liming6160
上传日期:2022-06-07
资源大小:785k
文件大小:34k
源码类别:

J2ME

开发平台:

Java

  1. /*
  2.  *    MFRadio - stream radio client for Java 2 Micro Edition
  3.  *    Copyright (C) 2001 - 2007 Mobicom-Kavkaz, Inc
  4.  *    
  5.  *    Visit the project page at: http://mfradio.sourceforge.net
  6.  *
  7.  *    This program is free software; you can redistribute it and/or modify
  8.  *    it under the terms of the GNU General Public License as published by
  9.  *    the Free Software Foundation; either version 2 of the License, or
  10.  *    (at your option) any later version.
  11.  *
  12.  *    This program is distributed in the hope that it will be useful,
  13.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *    GNU General Public License for more details.
  16.  *
  17.  *    You should have received a copy of the GNU General Public License
  18.  *    along with this program; if not, write to the Free Software
  19.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  *    Java (TM) and all Java (TM)-based marks are a trademark or 
  22.  *    registered trademark of Sun Microsystems, Inc, in the United States 
  23.  *    and other countries.
  24.  */
  25. package ru.mobicomk.mfradio.controller;
  26. import javax.microedition.lcdui.Alert;
  27. import javax.microedition.lcdui.AlertType;
  28. import javax.microedition.lcdui.Command;
  29. import javax.microedition.lcdui.CommandListener;
  30. import javax.microedition.lcdui.Display;
  31. import javax.microedition.lcdui.Displayable;
  32. import javax.microedition.lcdui.Form;
  33. import javax.microedition.lcdui.StringItem;
  34. import javax.microedition.media.PlayerListener;
  35. import ru.mobicomk.mfradio.Constants;
  36. import ru.mobicomk.mfradio.MFRadioMidlet;
  37. import ru.mobicomk.mfradio.iface.ContentHandler;
  38. import ru.mobicomk.mfradio.iface.Logger;
  39. import ru.mobicomk.mfradio.iface.ModelListener;
  40. import ru.mobicomk.mfradio.iface.ProgressObserver;
  41. import ru.mobicomk.mfradio.model.Model;
  42. import ru.mobicomk.mfradio.ui.EditStationUI;
  43. import ru.mobicomk.mfradio.ui.HelpUI;
  44. import ru.mobicomk.mfradio.ui.LocaleChoiceUI;
  45. import ru.mobicomk.mfradio.ui.PlayerUI;
  46. import ru.mobicomk.mfradio.ui.ProgressBarUI;
  47. import ru.mobicomk.mfradio.ui.SimpleWapBrowserUI;
  48. import ru.mobicomk.mfradio.ui.SplashUI;
  49. import ru.mobicomk.mfradio.util.AppException;
  50. import ru.mobicomk.mfradio.util.Locale;
  51. import ru.mobicomk.mfradio.util.M3UContentHandler;
  52. import ru.mobicomk.mfradio.util.MP3ContentHandler;
  53. import ru.mobicomk.mfradio.util.StationInfo;
  54. //import megafon.sap.one.util.MemoryLogger;
  55. //import megafon.sap.one.util.FileLogger;
  56. /**
  57.  * <h2>Main application controller</h2>
  58.  *
  59.  * <p>Application structure (only basic classes):</p>
  60.  *
  61.  * <code><pre>
  62.  *                                           +----------+
  63.  *                                     +---->| SplashUI |
  64.  *                                     |     +----------+
  65.  *                                     |             
  66.  *                       +-------------+-+              +----------+
  67.  *                +----->| UIController  |----------+-->| PlayerUI |
  68.  *                |      +--+------------+          |   +----------+
  69.  *  +-------------+-+       |                       |   
  70.  *  | MFRadioMidlet |       |   +---------------+   |   +----------+
  71.  *  +---+-----------+       |   | ProgressBarUI |<--+-->| EditUI   |
  72.  *      |                   |   +---------------+   |   +----------+
  73.  *      V                   |                       |   
  74.  *  +-------+               |        +--------+     |   +--------------------+    
  75.  *  | Model |<--------------+        | HelpUI |<----+-->| SimpleWapBrowserUI |   
  76.  *  +-------+               |        +--------+         +------+---------+---+      
  77.  *                          |                                  | Utils   |
  78.  *                          |                                  +---------+ 
  79.  *                          |
  80.  *                 +--------+                                     
  81.  *                 V                                                              
  82.  *     +------------------+                                     
  83.  *     | PlayerController +-------------+                  
  84.  *     +---+-----------+--+             V                              
  85.  *         |                     +--------------------+        
  86.  *         V                  +--+ PlayerRunnerThread |
  87.  *  +-------------------+     |  +--------------------+  
  88.  *  | PlayerMakerThread +-+   V      
  89.  *  +-----+-------------+ |  +-----------------+ 
  90.  *        |               +->| PlayerQueueImpl | 
  91.  *        +---------+        +-----------------+
  92.  *        V         V
  93.  *  +---------+   +---------------+
  94.  *  | Dloader |   | PlayerFactory |
  95.  *  +---------+   +---------------+  
  96.  *
  97.  * </pre></code>
  98.  *
  99.  * <p>{@link MFRadioMidlet} makes {@link Model} and {@link UIController}.</p>
  100.  * <p>{@link UIController} makes all UI forms and {@link PlayerController}.</p>
  101.  * <p>{@link PlayerController} runs {@link ru.mobicomk.mfradio.controller.PlayerMakerThread} and 
  102.  * {@link ru.mobicomk.mfradio.controller.PlayerRunnerThread} when user selects station.</p>
  103.  * <p>All manipulation with playlist through {@link UIController}.</p>
  104.  *
  105.  * @author  Roman Bondarenko
  106.  */
  107. public class UIController {
  108.     
  109.     // Members /////////////////////////////////////////////////////////////////
  110.     
  111.     private MFRadioMidlet midlet_;    // TODO: MFRadioMidlet -> MIDlet
  112.     private Model model_;
  113.     private PlayerController player_;
  114.     
  115.     // references to all UI classes
  116.     private ProgressObserver    progressUI_;
  117.     private ProgressBarUI       progressBarUI_;
  118.     private PlayerUI            playerUI_;
  119.     private EditStationUI       editStationUI_;
  120.     private SimpleWapBrowserUI  wapBrowserUI_;
  121.     private HelpUI              helpUI_;
  122.     private LocaleChoiceUI  localeChoiceUI_;
  123.     
  124.     // Navigable inteface
  125.     private Display     display_;
  126.     private Displayable currDisplayable_;
  127.     
  128.     // handlers
  129.     private ContentHandler[] urlHandlers_ = {
  130.         new M3UContentHandler(this)
  131.         , new MP3ContentHandler(this)
  132.         //, new ASFContentHandler(this)
  133.         //, new RAMContentHandler(this)
  134.     };
  135.     
  136.     private int playedStationIdx_;
  137.     private String repositoryURL_;
  138.     private Object stopPlaySync_;
  139.     private boolean inStopPlayStation_;
  140.     
  141.     // Log
  142.     private Logger logger_;
  143.     
  144.     private Locale locale;
  145.     
  146.     
  147.     // Methods /////////////////////////////////////////////////////////////////
  148.     
  149.     /**
  150.      * Initialize new UIController object.
  151.      * @param midlet MIDlet object
  152.      * @param model Application model object
  153.      */
  154.     public UIController(MFRadioMidlet midlet, Model model) {
  155.         midlet_ = midlet;
  156.         model_ = model;
  157.         playedStationIdx_ = -1;
  158.         
  159.         stopPlaySync_ = new Object();
  160.         inStopPlayStation_ = false;
  161.         
  162.         display_ = Display.getDisplay(midlet);
  163.         
  164.         player_ = new PlayerController(this);
  165.         progressUI_ = null;
  166.         helpUI_ = null;
  167.         
  168.         logger_ = null;
  169.         //logger_ = new MemoryLogger(this);
  170.         //logger_ = new FileLogger(this);
  171.     }
  172.     
  173.     // Actions /////////////////////////////////////////////////////////////////
  174.     
  175.     /**
  176.      * Start play selected station.
  177.      */
  178.     public void playStation() {
  179.         runWithProgress(
  180.             new EventDispatcherThread(EventIds.EVENT_ID_PLAY_STATION, getPlayerUI())
  181.             , locale.getString(Constants.STR_Connecting)
  182.             , true);
  183.     }
  184.     
  185.     /**
  186.      * Stop play selected station and (optionally) switch to other
  187.      * screen form.
  188.      * @param nextScreen next displayable object. May be <b>null</b>.
  189.      */
  190.     public void stopPlayStation(Displayable nextScreen) {
  191.         //log("stopPlayStation >> begin");
  192.         
  193.         synchronized (stopPlaySync_) {
  194.             if (inStopPlayStation_) {
  195.                 //log("stopPlayStation >> end (inStopPlayStation is TRUE)");
  196.                 return;
  197.             }
  198.             inStopPlayStation_ = true;
  199.         }
  200.         player_.stopPlay();
  201.         
  202.         if (nextScreen != null) {
  203.             replaceCurrent(nextScreen);
  204.         }
  205.         
  206.         if (getCurrDisplayable() == getPlayerUI()) {
  207.             getPlayerUI().updateUI();
  208.         }
  209.         
  210.         //log("stopPlayStation >> end");
  211.         
  212.         synchronized (stopPlaySync_) {
  213.             inStopPlayStation_ = false;
  214.         }
  215.     }
  216.     
  217.     /**
  218.      * Start UI - start initialize application thread.
  219.      */
  220.     public void startUI() {
  221. //        PlayerUI playerUI = getPlayerUI();
  222.         runWithSplash(new EventDispatcherThread(EventIds.EVENT_ID_START_UI, null)
  223.         , "");
  224.     }
  225.     
  226.     /**
  227.      * Stop UI - save and close all.
  228.      */
  229.     public void stopUI() {
  230.         stopPlayStation(null);
  231.         Displayable d = currDisplayable_;
  232.         
  233.         if (logger_ != null) {
  234.             logger_.flush();
  235.         }
  236.         showLog();
  237.         model_.save();
  238.         
  239.         try {
  240.             Thread.sleep((logger_ == null) ? 1000 : 4000); // 1sec
  241.         } catch (InterruptedException ex) { /*...*/ }
  242.     }
  243.     
  244.     /**
  245.      * Pause UI - not implemented yet.
  246.      */
  247.     public void pauseUI() {
  248.         // TODO: implement it!
  249.     }
  250.     
  251.     /**
  252.      * Stop execution MIDlet.
  253.      */
  254.     public void exitMIDlet() {
  255.         progressUI_ = null;
  256.         replaceCurrent(null);
  257.         midlet_.exitMIDlet();
  258.     }
  259.     
  260.     /**
  261.      * Initialize and show <i>Edit station</i> form.
  262.      */
  263.     public void editStationRequested() {
  264.         getEditStationUI().init(model_.getStationInfo(model_.getSelectedStationIdx()));
  265.         progressUI_ = null;
  266.         replaceCurrent(getEditStationUI());
  267.     }
  268.     
  269.     /**
  270.      * Close <i>Edit station</i> form and switch to <i>Player</i> form.
  271.      */
  272.     public void endEditStationRequested() {
  273.         playerUIRequested();
  274.     }
  275.     
  276.     /**
  277.      * Initialize and show <i>Add new station</i> form.
  278.      */
  279.     public void addStationRequested() {
  280.         getEditStationUI().init(null);
  281.         progressUI_ = null;
  282.         replaceCurrent(getEditStationUI());
  283.     }
  284.     
  285.     /**
  286.      * Close <i>Add new station</i> form and switch to <i>Player</i> form.
  287.      */
  288.     public void endAddStationRequested() {
  289.         playerUIRequested();
  290.     }
  291.     
  292.     /**
  293.      * Show <i>Player</i> form (main appilcation form).
  294.      */
  295.     public void playerUIRequested() {
  296.         getPlayerUI().updateUI();
  297.         
  298.         progressUI_ = null;
  299.         replaceCurrent(getPlayerUI());
  300.     }
  301.     
  302.     /**
  303.      * Start simple WAP/HTML browser for radio station links repository on
  304.      * the Internet.
  305.      */
  306.     public void repositoryRequested() {
  307.         if (!isRepositorySupported()) {
  308.             return;
  309.         }
  310.         
  311.         repositoryURL_ = midlet_.getAppProperty(Constants.APP_REPOSITORY_URL_KEY_PREFIX + "1");
  312.         
  313.         //if ("wtk-emulator".equals(System.getProperty("device.model"))) {
  314.         //    repositoryURL_ = "http://wap.local/index.wml";
  315.         //}
  316.         
  317.         new EventDispatcherThread(EventIds.EVENT_ID_GOTO_REPO, getWapBrowserUI()).start();
  318.     }
  319.     
  320.     /**
  321.      * Close <i>WAP browser</i> form and switch to <i>Player</i> form.
  322.      */
  323.     public void exitWapBrowserRequested() {
  324.         progressUI_ = null;
  325.         replaceCurrent(getPlayerUI());
  326.     }
  327.     
  328.     /**
  329.      * Show selected help screen.
  330.      * @param helpTopic Requested help topic. For possible values
  331.      * see {@link HelpUI}.
  332.      */
  333.     public void showHelp(int helpTopic) {
  334.         if (helpUI_ == null) {
  335.             helpUI_ = new HelpUI(this);
  336.         }
  337.         helpUI_.init(helpTopic, getCurrDisplayable());
  338.         progressUI_ = null;
  339.         replaceCurrent(helpUI_);
  340.     }
  341.     
  342.     /**
  343.      * Close <i>Help</i> form and switch to other displayable object.
  344.      * @param d Next displayable object. May be <b>null</b>.
  345.      */
  346.     public void endShowHelp(Displayable d) {
  347.         replaceCurrent(d);
  348.     }
  349.     
  350.     // Player's queue methods //////////////////////////////////////////////////
  351.     
  352.     /**
  353.      * Player's queue listener method. Fired if queue is not empty. Close
  354.      * <i>Wait</i> form and switch to <i>Player</i> form.
  355.      */
  356.     public void queueIsNotEmpty() {
  357.         new Thread(new Runnable() {
  358.             public void run() {
  359.                 getPlayerUI().updateUI();
  360.                 endWaitScreenRequested(getPlayerUI());
  361.             }
  362.         }).start();
  363.     }
  364.     
  365.     /**
  366.      * Player's queue listener method. Fired if queue is empty. Start
  367.      * <i>Wait</i> form.
  368.      */
  369.     public void queueIsEmpty() {
  370.         beginWaitScreenRequested(locale.getString(Constants.STR_Prefetching), true);
  371.     }
  372.     
  373.     /**
  374.      * Player's maker thread listener. Fired if thread was interrupted
  375.      * by exception.
  376.      */
  377.     public void makerIsInterrupted()  {
  378.         //log("makerIsInterrupted >> begin");
  379.         Displayable d = getPlayerUI();
  380.         stopPlayStation(getCurrDisplayable() != d ? d : null);
  381.         //log("makerIsInterrupted >> end");
  382.     }
  383.     
  384.     /**
  385.      * Player's maker thread listener. Fired if thread was stopped
  386.      * by user.
  387.      */
  388.     public void makerIsStopped() {
  389.         //log("makerIsStopped >> begin");
  390.         Displayable d = getPlayerUI();
  391.         stopPlayStation(getCurrDisplayable() != d ? d : null);
  392.         //log("makerIsStopped >> end");
  393.     }
  394.     
  395.     /**
  396.      * Player's runner thread listener. Fired if thread was interrupted
  397.      * by exception.
  398.      */
  399.     public void runnerIsInterrupted() {
  400.         //log("runnerIsInterrupted >> begin");
  401.         Displayable d = getPlayerUI();
  402.         stopPlayStation(getCurrDisplayable() != d ? d : null);
  403.         //log("runnerIsInterrupted >> end");
  404.     }
  405.     
  406.     /**
  407.      * Player's runner thread listener. Fired if thread was stopped
  408.      * by user.
  409.      */
  410.     public void runnerIsStopped() {
  411.         //log("runnerIsStopped >> begin");
  412.         playedStationIdx_ = -1;
  413.         
  414.         if (!inStopPlayStation_) {
  415.             if (getCurrDisplayable() != getPlayerUI()) {
  416.                 playerUIRequested();
  417.             } else {
  418.                 updatePlayerUI();
  419.             }
  420.         } else if (getCurrDisplayable() == getPlayerUI()) {
  421.             updatePlayerUI();
  422.             //log("runnerIsStopped >> player UI is updated!");
  423.         }
  424.         
  425.         //log("runnerIsStopped >> end");
  426.     }
  427.     
  428.     // Station list methods ////////////////////////////////////////////////////
  429.     
  430.     /**
  431.      * Selected station in playlist accessor.
  432.      * @return Selected (current) station info from playlist.
  433.      */
  434.     public StationInfo getSelectedStation() {
  435.         return model_.getStationInfo(model_.getSelectedStationIdx());
  436.     }
  437.     
  438.     /**
  439.      * Index of selected station in playlist.
  440.      * @return Index of selected (current) station in playlist.
  441.      */
  442.     public int getSelectedStationIdx() {
  443.         return model_.getSelectedStationIdx();
  444.     }
  445.     
  446.     /**
  447.      * Index of currently played station in playlist.
  448.      * @return Index of currently played station in playlist.
  449.      */
  450.     public int getPlayedStationIdx() {
  451.         return playedStationIdx_;
  452.     }
  453.     
  454.     /**
  455.      * Update station info in playlist.
  456.      * @param idx Index of station.
  457.      * @param si New station information.
  458.      */
  459.     public void setStation(int idx, StationInfo si) {
  460.         if (si != null) {
  461.             model_.setStationInfo(model_.getSelectedStationIdx(), si);
  462.         }
  463.     }
  464.     
  465.     /**
  466.      * Insert new station information into playlist.
  467.      * @param si New station information.
  468.      */
  469.     public void addStation(StationInfo si) {
  470.         if (si != null) {
  471.             if (!model_.hasURL(si.Url)) {
  472.                 model_.addStationInfo(si);
  473.             } else {
  474.                 StationInfo si2 = model_.getStationInfo(si.Url);
  475.                 showWarning(locale.getString(Constants.STR_Station_in_playlist)
  476.                     + si2.Title
  477.                     , currDisplayable_);
  478.             }
  479.         }
  480.     }
  481.     
  482.     /**
  483.      * Delete current (selected) station information from playlist and select
  484.      * next or previous station.
  485.      */
  486.     public void deleteStation() {
  487.         int selectedIdx = model_.getSelectedStationIdx();
  488.         int newSelectedIdx = selectedIdx;
  489.         
  490.         if (selectedIdx > 0) {
  491.             newSelectedIdx--;
  492.         } else if (model_.getStationsCount() > 1) {
  493.             newSelectedIdx++;
  494.         } else {
  495.             newSelectedIdx = -1;
  496.         }
  497.         
  498.         model_.selectStation(newSelectedIdx); // ??
  499.         model_.deleteStationInfo(selectedIdx);
  500.         
  501.         updatePlayerUI();
  502.     }
  503.     
  504.     /**
  505.      * Select previous station info in playlist.
  506.      */
  507.     public void selectPrevStation() {
  508.         if (model_.getStationsCount() > 0) {
  509.             int oldSelIdx = model_.getSelectedStationIdx();
  510.             if (oldSelIdx > 0) {
  511.                 model_.selectStation(oldSelIdx - 1);
  512.             } else {
  513.                 model_.selectStation(model_.getStationsCount() - 1);
  514.             }
  515.         }
  516.         updatePlayerUI();
  517.     }
  518.     
  519.     /**
  520.      * Select next station info in playlist.
  521.      */
  522.     public void selectNextStation() {
  523.         if (model_.getStationsCount() > 0) {
  524.             int oldSelIdx = model_.getSelectedStationIdx();
  525.             if (oldSelIdx < model_.getStationsCount() - 1) {
  526.                 model_.selectStation(oldSelIdx + 1);
  527.             } else {
  528.                 model_.selectStation(0);
  529.             }
  530.         }
  531.         updatePlayerUI();
  532.     }
  533.     
  534.     // Handle links methods ////////////////////////////////////////////////////
  535.     
  536.     /**
  537.      * Check if application has handler for link (URL).
  538.      * @param url Link (URL) for check.
  539.      * @return Result of checking (<b>true</b> or <b>false</b>).
  540.      */
  541.     public boolean canHandleLink(String url) {
  542.         for (int idx = 0; idx < urlHandlers_.length; idx++) {
  543.             if (urlHandlers_[idx].canHandle(url)) {
  544.                 return true;
  545.             }
  546.         }
  547.         return false;
  548.     }
  549.     
  550.     /**
  551.      * Handle link (URL).
  552.      * @param title Link title.
  553.      * @param url Link URL.
  554.      */
  555.     public void handleLink(String title, String url) {
  556.         for (int idx = 0; idx < urlHandlers_.length; idx++) {
  557.             if (urlHandlers_[idx].canHandle(url)) {
  558.                 urlHandlers_[idx].handle(title, url);
  559.                 //urlHandlers_[idx].close();
  560.                 break;
  561.             }
  562.         }
  563.     }
  564.     
  565.     // Volume methods //////////////////////////////////////////////////////////
  566.     
  567.     /**
  568.      * Player volume accessor.
  569.      * @return Current player volume level.
  570.      */
  571.     public int getVolume() {
  572.         return model_.getVolume();
  573.     }
  574.     
  575.     /**
  576.      * Increment player volume level.
  577.      */
  578.     public void incVolume() {
  579.         int vol = model_.getVolume();
  580.         if (vol < Model.MAX_VOL) {
  581.             model_.setVolume(vol + 10);
  582.             player_.setVolume(vol + 10);
  583.         }
  584.         updatePlayerUI();
  585.     }
  586.     
  587.     /**
  588.      * Decrement player volume level.
  589.      */
  590.     public void decVolume() {
  591.         int vol = model_.getVolume();
  592.         if (vol > Model.MIN_VOL) {
  593.             model_.setVolume(vol - 10);
  594.             player_.setVolume(vol - 10);
  595.         }
  596.         updatePlayerUI();
  597.     }
  598.     
  599.     // Progress methods ////////////////////////////////////////////////////////
  600.     
  601.     /**
  602.      * Update progress of a current long operation.
  603.      * @throws java.lang.InterruptedException if user press <i>Stop</i>.
  604.      */
  605.     public void updateProgress() throws InterruptedException {
  606.         if ((progressUI_ != null)/* && (getCurrDisplayable() == progressUI_)*/ ) {
  607.             if (!progressUI_.isStopped()) {
  608.                 progressUI_.updateProgress();
  609.                 return;
  610.             }
  611.             //log("updateProgress >> " + locale.getString(Constants.STR_Operation_interrupted);
  612.             throw new InterruptedException(locale.getString(Constants.STR_Operation_interrupted));
  613.         }
  614.     }
  615.     
  616.     /**
  617.      * Set progress bar stoppable flag. If it's <b>true</b>, then user can
  618.      * interrupt long operation by pressing <i>Stop</i>. Operation is
  619.      * uninterrupted in other case.
  620.      * @param stoppable New <i>stoppable</i> flag value.
  621.      * @return Previous <i>stoppable</i> flag value.
  622.      */
  623.     public boolean progressStoppable(boolean stoppable) {
  624.         boolean prevStoppable = false;
  625.         if ((progressUI_ != null) && (getCurrDisplayable() == progressUI_)) {
  626.             prevStoppable = progressUI_.isStoppable();
  627.             progressUI_.setStoppable(stoppable);
  628.         }
  629.         return prevStoppable;
  630.     }
  631.     
  632.     /**
  633.      * Set progress bar message.
  634.      * @param message New progress bar message.
  635.      */
  636.     public void progressMessage(String message) {
  637.         if (progressUI_ != null) {
  638.             progressUI_.setMessage(message);
  639.         }
  640.     }
  641.     
  642.     // Switch UI methods ///////////////////////////////////////////////////////
  643.     
  644.     /**
  645.      * Start thread with long operation and show <i>Wait</i> form.
  646.      * @param thread Thread to start.
  647.      * @param title Initial title of a progress bar.
  648.      * @param stoppable Initial <i>stoppable</i> flag value of a progress bar.
  649.      */
  650.     public void runWithProgress(Thread thread, String title, boolean stoppable) {
  651.         beginWaitScreenRequested(title, stoppable);
  652.         
  653.         thread.start();
  654.     }
  655.     
  656.     /**
  657.      * Start thread with long operation and show <i>Splach</i> form.
  658.      * @param thread Thread to start.
  659.      * @param title Title of a <i>Splach</i> form.
  660.      */
  661.     public void runWithSplash(Thread thread, String title) {
  662.         SplashUI splash = new SplashUI();
  663.         splash.init(title);
  664.         progressUI_ = splash;
  665.         replaceCurrent(splash);
  666.         
  667.         thread.start();
  668.     }
  669.     
  670.     /**
  671.      * Switch to <i>Wait</i> from.
  672.      * @param title Initial title of a progress bar.
  673.      * @param stoppable Initial <i>stoppable</i> flag value of a progress bar.
  674.      */
  675.     public void beginWaitScreenRequested(String title, boolean stoppable) {
  676.         //log("beginWaitScreen >> begin; curr=" + getCurrDisplayable() + "; pBar=" + progressBarUI_);
  677. ProgressBarUI progressBar = getProgressBarUI();
  678.         if (getCurrDisplayable() != progressBar) {
  679.             progressBar.init(title, stoppable);
  680.             progressUI_ = progressBar;
  681.             replaceCurrent(progressBar);
  682.         }
  683.         //log("beginWaitScreen >> end");
  684.     }
  685.     
  686.     /**
  687.      * Hide <i>Wait</i> form and switch to other displayable object. Do nothing
  688.      * if next displayable object equals current displayable.
  689.      * @param d Next displayable object.
  690.      */
  691.     public void endWaitScreenRequested(Displayable d) {
  692.         //log("endWaitScreen >> begin; curr=" + getCurrDisplayable() + "; pBar=" + progressBarUI_);
  693.         if (getCurrDisplayable() != d && d != getProgressBarUI()) {
  694.             progressUI_ = null;
  695.             replaceCurrent(d);
  696.         }
  697.         //log("endWaitScreen >> end");
  698.     }
  699.     
  700.     // ShowXXX methods /////////////////////////////////////////////////////////
  701.     
  702.     /**
  703.      * Show error helper.
  704.      * @param msg Error message.
  705.      * @param d Next displayable object.
  706.      */
  707.     public void showError(String msg, Displayable d) {
  708.         showAlert(locale.getString(Constants.STR_Error), msg, AlertType.ERROR, d);
  709.     }
  710.     
  711.     /**
  712.      * Show warning helper.
  713.      * @param msg Warning message.
  714.      * @param d Next displayable object.
  715.      */
  716.     public void showWarning(String msg, Displayable d) {
  717.         showAlert(locale.getString(Constants.STR_Warning), msg, AlertType.WARNING, d);
  718.     }
  719.     
  720.     /**
  721.      * Show information helper.
  722.      * @param msg Information message.
  723.      * @param d Next displayable object.
  724.      */
  725.     public void showInfo(String msg, Displayable d) {
  726.         showAlert(locale.getString(Constants.STR_Info), msg, AlertType.INFO, d);
  727.     }
  728.     
  729.     private void showAlert(String title, String msg, AlertType type, Displayable d) {
  730.         Alert a = new Alert(title, msg, null, type);
  731.         a.setTimeout(Alert.FOREVER);
  732.         
  733.         if (d == null) {
  734.             d = currDisplayable_;
  735.         }
  736.         
  737.         replaceCurrent(a, d);
  738.     }
  739.     
  740.     /**
  741.      * Show form helper. Shows form with custom title and text body. Make delay
  742.      * after switch to it.
  743.      * @param title Form title.
  744.      * @param text Form text body.
  745.      * @param sec Seconds count for delay.
  746.      */
  747.     public void showForm(String title, String text, int sec) {
  748.         Form f = new Form(title);
  749.         StringItem si = new StringItem(null, text);
  750.         f.append(si);
  751.         final Command exit = new Command(locale.getString(Constants.STR_Exit), Command.EXIT, 1);
  752.         f.addCommand(exit);
  753.         f.setCommandListener(new CommandListener() {
  754.             public void commandAction(Command command, Displayable displayable) {
  755.                 if (command == exit) {
  756.                     exitMIDlet();
  757.                 }
  758.             }
  759.         });
  760.         progressUI_ = null;
  761.         replaceCurrent(f);
  762.         
  763.         try {
  764.             Thread.sleep(sec * 1000);
  765.         } catch (InterruptedException ex) {
  766.             ex.printStackTrace();
  767.         }
  768.         
  769.     }
  770.     
  771.     // logger methods //////////////////////////////////////////////////////////
  772.     
  773.     /**
  774.      * Append message to log.
  775.      * @param msg Message to append to log.
  776.      */
  777.     public void log(String msg) {
  778.         if (logger_ != null) {
  779.             logger_.log(msg);
  780.         }
  781.     }
  782.     
  783.     /**
  784.      * Append exception information to log.
  785.      * @param t Exception object.
  786.      */
  787.     public void log(Throwable t) {
  788.         if (logger_ != null) {
  789.             logger_.log(t);
  790.         }
  791.     }
  792.     
  793.    /**
  794.     * Show log messages.
  795.     */
  796.     public void showLog() {
  797.         if (logger_ != null) {
  798.             showInfo(logger_.toString(), currDisplayable_);
  799.         }
  800.     }
  801.     
  802.     // Other methods ///////////////////////////////////////////////////////////
  803.     
  804.     /**
  805.      * Set player events listener helper.
  806.      * @param listener New player listener.
  807.      */
  808.     public void setPlayerListener(PlayerListener listener) {
  809.         if (player_ != null) {
  810.             player_.setPlayerListener(listener);
  811.         }
  812.     }
  813.     
  814.     /**
  815.      * Set model events listener helper.
  816.      * @param listener New model listener.
  817.      */
  818.     public void setModelListener(ModelListener listener) {
  819.         model_.setModelListener(listener);
  820.     }
  821.     
  822.     /**
  823.      * MIDlet's display accessor.
  824.      * @return MIDlet's display.
  825.      */
  826.     public Display getDisplay() {
  827.         return display_;
  828.     }
  829.     
  830.     /**
  831.      * Current displayable object accessor.
  832.      * @return Current displayable object.
  833.      */
  834.     public Displayable getCurrDisplayable() {
  835.         return currDisplayable_;
  836.     }
  837.     
  838.     /**
  839.      * Get information about color support of the device.
  840.      * @return <b>true</b> if the display supports color, <b>false</b> othrwise.
  841.      */
  842.     public boolean isColor() {
  843.         return display_.isColor();
  844.     }
  845.     
  846.     /**
  847.      * Update <i>Player</i> form helper.
  848.      */
  849.     public void updatePlayerUI() {
  850.         getPlayerUI().updateUI();
  851.     }
  852.     
  853.     /**
  854.      * Get information about on-line radio station links repository support.
  855.      * @return <b>true</b> if repository URL not empty in the MIDlet properties,
  856.      * <b>false</b> in othrwise. Property key name is <i>RepositoryURL-1</i>.
  857.      */
  858.     public boolean isRepositorySupported() {
  859.         String url = midlet_.getAppProperty(Constants.APP_REPOSITORY_URL_KEY_PREFIX + "1");
  860.         return ((url != null) && !"".equals(url));
  861.     }
  862.     
  863.     // Navigable methods ///////////////////////////////////////////////////////
  864.     
  865.     /**
  866.      * Replace current displayable object with new.
  867.      * @param d Next displayable object.
  868.      * @return Previous displayable object.
  869.      */
  870.     public Displayable replaceCurrent(Displayable d) {
  871.         display_.setCurrent(d);
  872.         //log("go >> switch to " + d);
  873.         if (! (d instanceof Alert)) {
  874.             // Alerts come back automatically
  875.             currDisplayable_ = d;
  876.         }
  877.         return d;
  878.     }
  879.     
  880.     /**
  881.      * Show alert and switch to other displayable object after it.
  882.      * @param a Alert object to show.
  883.      * @param d Next displayable object.
  884.      * @return Previous displayable object.
  885.      */
  886.     public Displayable replaceCurrent(Alert a, Displayable d){
  887.         display_.setCurrent(a, d);
  888.         //log("go >> switch to " + d);
  889.         if (! (d instanceof Alert)) {
  890.             // Alerts come back automatically
  891.             currDisplayable_ = d;
  892.         }
  893.         return d;
  894.     }
  895.     
  896.     // Privates ////////////////////////////////////////////////////////////////
  897.     
  898.     private EditStationUI getEditStationUI() {
  899.         if (editStationUI_ == null) {
  900.             editStationUI_ = new EditStationUI(this);
  901.         }
  902.         return editStationUI_;
  903.     }
  904.     
  905.     private PlayerUI getPlayerUI() {
  906.         if (playerUI_ == null) {
  907.             playerUI_ = new PlayerUI(this);
  908.         }
  909.         return playerUI_;
  910.     }
  911.     
  912.     private ProgressBarUI getProgressBarUI() {
  913.         if (progressBarUI_ == null) {
  914.             progressBarUI_ = new ProgressBarUI(this);
  915.         }
  916.         return progressBarUI_;
  917.     }
  918.     
  919.     private Displayable getWapBrowserUI() {
  920.         if (wapBrowserUI_ == null) {
  921.             wapBrowserUI_ = new SimpleWapBrowserUI(locale.getString(Constants.STR_Repository), this);
  922.         }
  923.         return wapBrowserUI_;
  924.     }
  925.     
  926.     /*
  927.      *
  928.      */
  929.     class EventIds {
  930.         public static final int BASE_IDX    = 0;
  931.         
  932.         public static final int EVENT_ID_UNDEFINED      = BASE_IDX + 0;
  933.         public static final int EVENT_ID_START_UI       = BASE_IDX + 1;
  934.         public static final int EVENT_ID_STOP_UI        = BASE_IDX + 2;
  935.         public static final int EVENT_ID_PLAY_STATION   = BASE_IDX + 3;
  936.         public static final int EVENT_ID_SAVE_LOG       = BASE_IDX + 4;
  937.         public static final int EVENT_ID_GOTO_REPO      = BASE_IDX + 5;
  938.     }
  939.     
  940.     /*
  941.      *
  942.      */
  943.     class EventDispatcherThread
  944.         extends Thread {
  945.         
  946.         private int taskId_;
  947.         private Displayable fallbackUI_;
  948.         
  949.         EventDispatcherThread(int taskId, Displayable fallbackUI) {
  950.             taskId_ = taskId;
  951.             fallbackUI_ = fallbackUI;
  952.             return;
  953.         }
  954.         
  955.         public void run() {
  956.             try {
  957.                 switch (taskId_) {
  958.                     
  959.                     case EventIds.EVENT_ID_PLAY_STATION: {
  960.                         try {
  961.                             //log("edt:play >> start");
  962.                             synchronized (player_) {
  963.                                 stopPlayStation(null);
  964.                                 updateProgress(); // exception
  965.                                 player_.init(getSelectedStation()); // exception
  966.                                 player_.startPlay(); // exception
  967.                                 playedStationIdx_ = model_.getSelectedStationIdx();
  968.                                 updateProgress();
  969.                             }
  970.                             //log("edt:play >> finish");
  971.                         } catch (InterruptedException ex) {
  972.                             //log("edt:play >> " + ex.toString());
  973.                             //progressMessage("interrupt - 4");
  974.                             replaceCurrent(fallbackUI_);
  975.                         } catch (AppException ex) {
  976.                             //log("edt:play >> " + ex.toString());
  977.                             // do nothing, because this exception may be fired
  978.                             // only if user press "play"
  979.                         } catch (Throwable t) {
  980.                             //log("edt:play >> " + t.toString());
  981.                             showError(locale.getString(Constants.STR_Error_start_player_for)
  982.                                 + """ + getSelectedStation().Title
  983.                                 + "": " + t.getMessage()
  984.                                 , fallbackUI_);
  985.                         }
  986.                         break;
  987.                     }
  988.                     
  989.                     case EventIds.EVENT_ID_START_UI: {
  990. //                        updateProgress();
  991.                         model_.load();
  992. //                        updateProgress();
  993.                         try {Thread.sleep(2000);} catch (InterruptedException ex) { }
  994.                         
  995.                         //FIXME initial load of playlist
  996.                         getPlayerUI().playlistChanges(model_.getStationTitles(), model_.getStationsCount());
  997.                         getPlayerUI().updateUI();
  998.                         endWaitScreenRequested(getPlayerUI());
  999.                         break;
  1000.                     }
  1001.                     
  1002.                     case EventIds.EVENT_ID_GOTO_REPO: {
  1003.                         wapBrowserUI_.handle(null, repositoryURL_);
  1004.                     }
  1005.                     
  1006.                     // Other cases ...
  1007.                 }
  1008.             } catch (Exception ex) {
  1009.                 showError("Exception: " + ex.getMessage(), fallbackUI_);
  1010.             }
  1011.         } // end of run() method
  1012.         
  1013.     } // end of EventDispatcher class
  1014.     
  1015.     public void setLocale(Locale newlocale) {
  1016. if (locale != null) {
  1017.     if (!locale.equals(newlocale)) {
  1018. locale = newlocale;
  1019. progressUI_ = null;
  1020. progressBarUI_ = null;
  1021. playerUI_ = null;
  1022. editStationUI_ = null;
  1023. wapBrowserUI_ = null;
  1024. helpUI_ = null;
  1025. localeChoiceUI_ = null;
  1026.     }
  1027. }
  1028.     }
  1029.     
  1030.     public Locale getLocale() {
  1031. if (locale == null) {
  1032.     locale = Locale.getForName(model_.getLocaleName());
  1033. }
  1034. return locale;
  1035.     }
  1036.     public void endLocaleChoiceRequest(Locale newLocale) {
  1037. if ((newLocale != null) && (!locale.equals(newLocale))) {
  1038.     setLocale(newLocale);
  1039.     // create new PlayerUI object
  1040.     getPlayerUI();
  1041.     model_.setLocaleName(locale.getName());
  1042.     // when locale name is changed the model will notify PlayerUI to update playlist
  1043. }
  1044. replaceCurrent(getPlayerUI());
  1045.     }
  1046.     public void localeChoiceRequested() {
  1047. progressUI_ = null;
  1048. getLocaleChoiceUI().setSelectedLocale(locale);
  1049. replaceCurrent(getLocaleChoiceUI());
  1050.     }
  1051.     private LocaleChoiceUI getLocaleChoiceUI() {
  1052. if (localeChoiceUI_ == null) {
  1053.     localeChoiceUI_ = new LocaleChoiceUI(this);
  1054.     localeChoiceUI_.init(Locale.getAvailable());
  1055. }
  1056. return localeChoiceUI_;
  1057.     }
  1058.     
  1059. }