Android Open Source - MorseFlash Morse Transcriber






From Project

Back to project page MorseFlash.

License

The source code is released under:

Apache License

If you think the Android project MorseFlash listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2014 Jason J./*  w  w w.  j av a 2 s.  c  om*/
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.ovrhere.android.morseflash.morsecode.transcriber;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import com.ovrhere.android.morseflash.morsecode.dictionaries.MorseDictionary;
import com.ovrhere.android.morseflash.morsecode.dictionaries.MorseDictionary.MorseCharacter;

/**
 * <p>Performs the basic transcription of a string message into morse code
 * by means of a {@link MorseDictionary}. This follows the general rules of
 * <a href="http://en.wikipedia.org/wiki/Morse_code">Morse code</a>, namely:
 * <ul>
 * <li>dots are 1 Time Unit (TU)</li>
 * <li>dashes are {@value #REL_INTERVAL_DASH}TU</li>
 * <li>intervals between intra-characters (dashes/dots) are 1TU</li>
 * <li>intervals between characters (a b c) are {@value #REL_INTERVAL_CHARACTER}TU</li>
 * <li>intervals between words are {@value #REL_INTERVAL_WORD}TU</li> 
 * </ul>
 * An additional (non-standard) interval of {@value #REL_INTERVAL_LOOP_MESSAGE}TU
 * is given between looped-message iterations. Default unit time is 
 * {@value #DEFAULT_UNIT_TIME}ms.
 * </p>
 * 
 * <p>There is an additional delay before/after a message set by 
 * 
 * Default pad time is {@value #DEFAULT_UNIT_TIME}ms.</p>
 * 
 * <p>Please note this class is not safe for within rotation contexts.
 * Consider using {@link MorseTranscriberHeadlessFragment} within activities.
 * </p> 
 * 
 * @author Jason J.
 * @version 0.4.1-20140713
 */
public class MorseTranscriber implements IMorseTranscriber {
  /** The tag used for logging. */
  @SuppressWarnings("unused")
  final static private String LOGTAG = MorseTranscriber.class.getSimpleName();
  /** The default unit time in milliseconds. */
  final private static int DEFAULT_UNIT_TIME = 100; //ms
  /** The default pad time in milliseconds. */
  final private static int DEFAULT_PAD_TIME = 500; //ms
  
  /* * The relative interval between each pattern unit. */
  //final private static int REL_INTERVAL_PATTERN_UNIT = 1; //units
  /** The relative interval for each dash. */
  final private static int REL_INTERVAL_DASH = 3; //units
  /** The relative interval between each morse letter. */
  final private static int REL_INTERVAL_CHARACTER = 3; //units
  /** The relative interval between each word. */
  final private static int REL_INTERVAL_WORD = 7; //units
  /** The relative interval between each message (if looped). */
  final private static int REL_INTERVAL_LOOP_MESSAGE = 15; //units
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// End constants 
  ////////////////////////////////////////////////////////////////////////////////////////////////
  /** The number of milliseconds to wait before sending a message. */
  private int messagePadTime = DEFAULT_PAD_TIME; 
  
  /** The number of milliseconds considered to be one unit. 
   * Default value is {@value #DEFAULT_UNIT_TIME}. */ 
  private int unitTime = DEFAULT_UNIT_TIME; //ms
  /** The timing for between dots and dashes is unit time. */
  private int morseSymbolInterval = unitTime;
  /** The timing for dots is unit time. */
  private int morseDotInterval = unitTime;  
  /** The timing for dashes ({@link #unitTime}* {@link #REL_INTERVAL_DASH}). */
  private int morseDashInterval = unitTime * REL_INTERVAL_DASH;
  /** The timing between characters ({@link #unitTime}* {@link #REL_INTERVAL_CHARACTER}). */
  private int morseCharInterval = unitTime * REL_INTERVAL_CHARACTER;
  /** The timing between words ({@link #unitTime}* {@link #REL_INTERVAL_WORD}). */
  private int morseWordInterval = unitTime * REL_INTERVAL_WORD;
  /** The timing between messages ({@link #unitTime}* {@link #REL_INTERVAL_LOOP_MESSAGE}). */
  private int morseLoopMessageInterval = unitTime * REL_INTERVAL_LOOP_MESSAGE;
  
  /** The dictionary to lookup characters in. */
  private MorseDictionary dictionary = null;
  
  /** The signal listener that defines the actions to do for morse code. */
  private OnSignalListener m_SignalListener = null;
  /** The morse listener for events. Can be null. */
  private OnTranscriptionListener m_MorseListener = null;
  
  /** The time to use to schedule morse signals. */
  private Timer signalTimer = new Timer();  
  
  /** Whether the message should continue processing. Default false. */
  volatile private boolean continueMessageProcessing = false;
  /** Whether the message is currently sending. */
  volatile private boolean messageIsSending = false;
  
  /** Whether or not to play the message again when completed. Default false. */
  volatile private boolean loopMessage = false; 

  /** A list of the words in the message after said message has been exploded. */
  private String[] messageList = new String[]{};
  
  /**
   * @param dictionary The dictionary used to translate between strings and morse.
   * @param signalListener The listener for when morse start/end signals are sent.
   */
  public MorseTranscriber(MorseDictionary dictionary, OnSignalListener signalListener) {
    this.dictionary = dictionary;
    this.m_SignalListener = signalListener;
  }
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Accessors/mutators begin here
  //
  // NOTE: DO NOT add public functions here without adding them to 
  // IMorseTranscriber first
  ////////////////////////////////////////////////////////////////////////////////////////////////
  @Override
  public void setOnMorseListener(OnTranscriptionListener morseListener) {
    this.m_MorseListener = morseListener;
  }
  
  @Override
  public void setOnSignalListener(OnSignalListener signalListener) {
    this.m_SignalListener  = signalListener;
  }
  
  @Override
  public void setMessage(String msg){
    synchronized (messageList){
      this.messageList = msg.split("\\s+");
    }
  }
  /**
   * If set, loops every {@value #REL_INTERVAL_LOOP_MESSAGE} units.
   * @param loopMessage Whether or not to loop the message and replay it infinitely. 
   */
  @Override
  public void setLoop(boolean loopMessage) {
    this.loopMessage = loopMessage;
  }
  
  @Override
  public boolean isRunning() {
    return messageIsSending || continueMessageProcessing;
  }
  
  @Override 
  public boolean isMessageLooped() {
    return loopMessage;
  }
  @Override
  public void setPadTime(int padTime) {
    if (padTime < 0){
      throw new IllegalArgumentException("Pad time cannot be < 0");
    }
    this.messagePadTime = padTime;    
  }
  @Override
  public void setUnitTime(int unitTime) {
    if (unitTime < 1){
      throw new IllegalArgumentException("Unit time cannot be <=0");
    }
    this.unitTime = unitTime;
    morseDotInterval = unitTime;
    morseSymbolInterval = unitTime;
    morseDashInterval = unitTime * REL_INTERVAL_DASH;
    morseCharInterval = unitTime * REL_INTERVAL_CHARACTER;    
    morseWordInterval = unitTime * REL_INTERVAL_WORD;
    morseLoopMessageInterval = unitTime * REL_INTERVAL_LOOP_MESSAGE;
  }
  @Override 
  public int getUnitTime() {
    return unitTime;
  }
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// End mutators + accessors
  ////////////////////////////////////////////////////////////////////////////////////////////////
  @Override
  public boolean start (OnTranscriptionListener onMorseListener){
    setOnMorseListener(onMorseListener);
    return start();
  }
  
  @Override
  public boolean start(){
    if (messageList.length == 0 ){
      //prevent blank messages
      return false;
    }
    //start thread. safety to prevent multiple starts.    
    try {
      //throws exception if already started.
      new Thread(morseParser).start();
    } catch (Exception e){
      return false;
    }
    return true;
  }
  
  @Override
  public boolean cancel(){
    boolean isCancelSuccess = false;
    if (continueMessageProcessing){
      continueMessageProcessing = false;      
      isCancelSuccess = true;
    }  
    if (messageIsSending){
      messageIsSending = false;
      synchronized (signalTimer) {        
        signalTimer.cancel();
        signalTimer.purge();
        signalTimer = new Timer();
      }
      isCancelSuccess = true;
    }
    return isCancelSuccess;
  }
    
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Helper functions
  ////////////////////////////////////////////////////////////////////////////////////////////////
  /**
   * Converts a signal word into a morse message as best as possible.
   * @param word The word to explode and translate to morse.
   * @return The word converted to Morse code.
   */
  private List<MorseCharacter> wordToMorse(String word){
    List<MorseCharacter> charList = 
        new ArrayList<MorseCharacter>();
    char[] letters = word.toCharArray();
    final int SIZE = letters.length;
    for (int index = 0; index < SIZE; index++) {
      MorseCharacter mchar = 
          dictionary.encodeChar(letters[index]);
      if (mchar != null){
        charList.add(mchar);
      }
    }    
    return charList;
  }
  /** Parses a morse word and schedules the times to state/end dots+dashes.
   * @param start The starting time for the word.
   * @param morseWord The word to decode into characters and dots+dashes.
   */
  private void parseMorseWord(Date start, List<MorseCharacter> morseWord){
    final int SIZE = morseWord.size();
    for (int index = 0; index < SIZE; index++) {
      parseMorseCharacter(start, morseWord.get(index));
      //if there are more letters in the word.
      if(index + 1 < SIZE){
        //set's the next character's start time as 1 unit from the last.
        offsetTime(start, morseCharInterval * (index+1));
      }
    }
  }
  
  /**
   * Parses morse character and schedules the times to start/end dots+dashes. 
   * @param start The start time for the character
   * @param morseCharacter The character to decode into dots+dashes.
   */
  private void parseMorseCharacter(Date start, MorseCharacter morseCharacter) {
    final List<Integer> pattern = morseCharacter.getPattern();
    final int SIZE = pattern.size();
    for(int index = 0; index < SIZE; index++){
    //for (Integer symbol : pattern) {
      if (!continueMessageProcessing){ //if we are not to continue
        break;
      }
      Integer symbol = pattern.get(index);
      
      MorseSignalTask startSignal = new MorseSignalTask(true, m_SignalListener);
      MorseSignalTask endSignal = new MorseSignalTask(false, m_SignalListener);
      Date end = null;
      
      if (symbol == MorseDictionary.DOT){
        end = new Date(start.getTime() + morseDotInterval);
      } else if (symbol == MorseDictionary.DASH){
        end = new Date(start.getTime() + morseDashInterval);
      } else {
        continue;
      }        
      signalTimer.schedule(startSignal, start);
      signalTimer.schedule(endSignal, end);
      
      start.setTime(end.getTime());
      if(index + 1 < SIZE){
        offsetTime(start, morseSymbolInterval * (index+1));
      }
    }
  }
  
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Utility functions
  ////////////////////////////////////////////////////////////////////////////////////////////////
  
  /**
   * Convenience function.
   * Adds the time to {@link Date#getTime()} and resets the date's time.
   * @param date The time to offset (Reference)
   * @param time The time in milliseconds to add.
   */
  static private void offsetTime(Date date, long time) {
    date.setTime(date.getTime() + time);
  }
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Internal classes
  ////////////////////////////////////////////////////////////////////////////////////////////////
  
  
  /**
   * Denotes either the beginning or end of a morse signal unit (Dot/Dash).
   * @version 0.1.0-20140512
   */
  static private class MorseSignalTask extends TimerTask {    
    protected boolean isOnSignal = false;
    protected OnSignalListener onSignalListener = null;
    public MorseSignalTask(boolean on, OnSignalListener onSignalListener) {
      this.isOnSignal = on;
      this.onSignalListener = onSignalListener;
    }
    @Override
    public void run() {
      if (isOnSignal){
        onSignalListener.onSignalStart();
      } else {
        onSignalListener.onSignalEnd();
      }
    }
  }
    
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Internal runnable
  ////////////////////////////////////////////////////////////////////////////////////////////////
  
  /** Defines how to parse the message into morse. 
   * @throws IllegalThreadStateException If attempted to run twice. */
  private Runnable morseParser = new Runnable() {
    @Override
    public void run() {
      //if running, bad state.
      if (continueMessageProcessing){
        throw new IllegalThreadStateException(
            "Cannot start already running task");
      }
      continueMessageProcessing = true;
      final List<ArrayList<MorseCharacter>> message = 
          new ArrayList<ArrayList<MorseCharacter>>();    
      
      synchronized (messageList) {
        final int SIZE  = messageList.length;        
        for (int index = 0; index < SIZE && continueMessageProcessing; index++) {
          message.add(
                (ArrayList<MorseCharacter>) 
                wordToMorse(messageList[index])
              );
        }
      }
      if (m_MorseListener != null){
        m_MorseListener.onMorseParsed();
      }
      
      
      Date delay = new Date();
      offsetTime(delay, messagePadTime);
      signalTimer.schedule(new TimerTask() {            
        @Override
        public void run() { sendMorseMessage(message);}
      }, delay);          
    }
    /**
     * Sends the morse message.
     * @param message The message to send via parseMorseWord.
     */
    private void sendMorseMessage(final List<ArrayList<MorseCharacter>> message) {
      synchronized (signalTimer) {
        messageIsSending = true;
        if (!continueMessageProcessing){
          //we have finished
          messageIsSending = false;
          
          if (m_MorseListener != null){
            m_MorseListener.onMorseCompleted();
          }
          return;
        }
        
        final int SIZE2 = message.size();
        Date start = new Date();        
        for (int   index = 0; 
              index < SIZE2 && continueMessageProcessing; 
              index++) {
          parseMorseWord(start, message.get(index));
          //if not the last word of the message.
          if(index + 1 < SIZE2){
            //offset by a word interval
            offsetTime(start, morseWordInterval * (index+1));
          }
        }        
        
        if (loopMessage){
          //keep looping if set true.
          offsetTime(start, morseLoopMessageInterval);          
        } else {
          //if not looping, no more processing.
          continueMessageProcessing = false;  
          offsetTime(start, messagePadTime);
        }
        signalTimer.schedule(new TimerTask() {            
          @Override
          public void run() { sendMorseMessage(message);}
        }, start);
      }
    }
  };
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Internal Listener interfaces
  ////////////////////////////////////////////////////////////////////////////////////////////////
  
  //moved to IMorseTranscriber  
}




Java Source Code List

com.ovrhere.android.morseflash.morsecode.dictionaries.InternationalMorseCode.java
com.ovrhere.android.morseflash.morsecode.dictionaries.MorseDictionary.java
com.ovrhere.android.morseflash.morsecode.transcriber.IMorseTranscriber.java
com.ovrhere.android.morseflash.morsecode.transcriber.MorseTranscriberHeadlessFragment.java
com.ovrhere.android.morseflash.morsecode.transcriber.MorseTranscriber.java
com.ovrhere.android.morseflash.prefs.PreferenceUtils.java
com.ovrhere.android.morseflash.ui.MainActivity.java
com.ovrhere.android.morseflash.ui.fragments.MainFragment.java
com.ovrhere.android.morseflash.ui.fragments.ScreenFlashFragment.java
com.ovrhere.android.morseflash.utils.CameraFlashUtil.java